lis3lv02d.c revision 05faadcf59507e8eea57ffbeea9cbb14c9a2ab3d
1455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek/*
2455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek *  lis3lv02d.c - ST LIS3LV02DL accelerometer driver
3455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek *
4455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek *  Copyright (C) 2007-2008 Yan Burman
5455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek *  Copyright (C) 2008 Eric Piel
6ef2cfc790bf5f0ff189b01eabc0f4feb5e8524dfPavel Machek *  Copyright (C) 2008-2009 Pavel Machek
7455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek *
8455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek *  This program is free software; you can redistribute it and/or modify
9455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek *  it under the terms of the GNU General Public License as published by
10455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek *  the Free Software Foundation; either version 2 of the License, or
11455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek *  (at your option) any later version.
12455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek *
13455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek *  This program is distributed in the hope that it will be useful,
14455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek *  but WITHOUT ANY WARRANTY; without even the implied warranty of
15455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek *  GNU General Public License for more details.
17455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek *
18455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek *  You should have received a copy of the GNU General Public License
19455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek *  along with this program; if not, write to the Free Software
20455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek */
22455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek
2363366d37ad5dbb4f208b517c88ea4bd41738dbf7Joe Perches#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
2463366d37ad5dbb4f208b517c88ea4bd41738dbf7Joe Perches
25455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek#include <linux/kernel.h>
26455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek#include <linux/init.h>
27455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek#include <linux/dmi.h>
28455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek#include <linux/module.h>
29455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek#include <linux/types.h>
30455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek#include <linux/platform_device.h>
31455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek#include <linux/interrupt.h>
32dc6ea97bac6b8228c7a69740df35eed2be3407beEric Piel#include <linux/input-polldev.h>
33455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek#include <linux/delay.h>
34455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek#include <linux/wait.h>
35455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek#include <linux/poll.h>
36f9deb41f91c41d9d91a24c84a555ec7fe82620daSamu Onkalo#include <linux/slab.h>
37455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek#include <linux/freezer.h>
38455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek#include <linux/uaccess.h>
39ef2cfc790bf5f0ff189b01eabc0f4feb5e8524dfPavel Machek#include <linux/miscdevice.h>
402a346996626ecbb4269c239e9ff7372a182907e9Samu Onkalo#include <linux/pm_runtime.h>
41ff606677f6a47c63329cf8e6c7cf978c29f2d736Jean Delvare#include <linux/atomic.h>
42455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek#include "lis3lv02d.h"
43455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek
44455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek#define DRIVER_NAME     "lis3lv02d"
45455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek
46455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek/* joystick device poll interval in milliseconds */
47455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek#define MDPS_POLL_INTERVAL 50
484a70a413ccfd1c14ef29a290a4d2dada04ccbefbSamu Onkalo#define MDPS_POLL_MIN	   0
494a70a413ccfd1c14ef29a290a4d2dada04ccbefbSamu Onkalo#define MDPS_POLL_MAX	   2000
502a346996626ecbb4269c239e9ff7372a182907e9Samu Onkalo
512a346996626ecbb4269c239e9ff7372a182907e9Samu Onkalo#define LIS3_SYSFS_POWERDOWN_DELAY 5000 /* In milliseconds */
522a346996626ecbb4269c239e9ff7372a182907e9Samu Onkalo
53029756d0b8856f52d83dee81c01dd3af786cadffSamu Onkalo#define SELFTEST_OK	       0
54029756d0b8856f52d83dee81c01dd3af786cadffSamu Onkalo#define SELFTEST_FAIL	       -1
55029756d0b8856f52d83dee81c01dd3af786cadffSamu Onkalo#define SELFTEST_IRQ	       -2
56029756d0b8856f52d83dee81c01dd3af786cadffSamu Onkalo
57029756d0b8856f52d83dee81c01dd3af786cadffSamu Onkalo#define IRQ_LINE0	       0
58029756d0b8856f52d83dee81c01dd3af786cadffSamu Onkalo#define IRQ_LINE1	       1
59029756d0b8856f52d83dee81c01dd3af786cadffSamu Onkalo
60455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek/*
61455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek * The sensor can also generate interrupts (DRDY) but it's pretty pointless
62bc62c1471773fc32adcfc05100abd16fa2b6e126Éric Piel * because they are generated even if the data do not change. So it's better
63455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek * to keep the interrupt for the free-fall event. The values are updated at
64455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek * 40Hz (at the lowest frequency), but as it can be pretty time consuming on
65455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek * some low processor, we poll the sensor only at 20Hz... enough for the
66455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek * joystick.
67455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek */
68455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek
69641615abfac0b7c5e6f242a6db77f7690925b443Samu Onkalo#define LIS3_PWRON_DELAY_WAI_12B	(5000)
70641615abfac0b7c5e6f242a6db77f7690925b443Samu Onkalo#define LIS3_PWRON_DELAY_WAI_8B		(3000)
71641615abfac0b7c5e6f242a6db77f7690925b443Samu Onkalo
7232496c76b777752ba84b125bebfb0cc498f5602cSamu Onkalo/*
7332496c76b777752ba84b125bebfb0cc498f5602cSamu Onkalo * LIS3LV02D spec says 1024 LSBs corresponds 1 G -> 1LSB is 1000/1024 mG
7432496c76b777752ba84b125bebfb0cc498f5602cSamu Onkalo * LIS302D spec says: 18 mG / digit
7532496c76b777752ba84b125bebfb0cc498f5602cSamu Onkalo * LIS3_ACCURACY is used to increase accuracy of the intermediate
7632496c76b777752ba84b125bebfb0cc498f5602cSamu Onkalo * calculation results.
7732496c76b777752ba84b125bebfb0cc498f5602cSamu Onkalo */
7832496c76b777752ba84b125bebfb0cc498f5602cSamu Onkalo#define LIS3_ACCURACY			1024
7932496c76b777752ba84b125bebfb0cc498f5602cSamu Onkalo/* Sensitivity values for -2G +2G scale */
8032496c76b777752ba84b125bebfb0cc498f5602cSamu Onkalo#define LIS3_SENSITIVITY_12B		((LIS3_ACCURACY * 1000) / 1024)
8132496c76b777752ba84b125bebfb0cc498f5602cSamu Onkalo#define LIS3_SENSITIVITY_8B		(18 * LIS3_ACCURACY)
8232496c76b777752ba84b125bebfb0cc498f5602cSamu Onkalo
83477bc918c2323a51f577cd892ca49376f6feb5d5Samu Onkalo#define LIS3_DEFAULT_FUZZ_12B		3
84477bc918c2323a51f577cd892ca49376f6feb5d5Samu Onkalo#define LIS3_DEFAULT_FLAT_12B		3
85477bc918c2323a51f577cd892ca49376f6feb5d5Samu Onkalo#define LIS3_DEFAULT_FUZZ_8B		1
86477bc918c2323a51f577cd892ca49376f6feb5d5Samu Onkalo#define LIS3_DEFAULT_FLAT_8B		1
8732496c76b777752ba84b125bebfb0cc498f5602cSamu Onkalo
88a38da2ed74f628c1f3e907c772be21a66eccab9cDaniel Mackstruct lis3lv02d lis3_dev = {
89be84cfc588b19f14764d78556dc7b630ee8c914cPavel Machek	.misc_wait   = __WAIT_QUEUE_HEAD_INITIALIZER(lis3_dev.misc_wait),
90ef2cfc790bf5f0ff189b01eabc0f4feb5e8524dfPavel Machek};
91be84cfc588b19f14764d78556dc7b630ee8c914cPavel MachekEXPORT_SYMBOL_GPL(lis3_dev);
92455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek
932ee321440e3a594dcdd9981e68e5e302447047a2Takashi Iwai/* just like param_set_int() but does sanity-check so that it won't point
942ee321440e3a594dcdd9981e68e5e302447047a2Takashi Iwai * over the axis array size
952ee321440e3a594dcdd9981e68e5e302447047a2Takashi Iwai */
962ee321440e3a594dcdd9981e68e5e302447047a2Takashi Iwaistatic int param_set_axis(const char *val, const struct kernel_param *kp)
972ee321440e3a594dcdd9981e68e5e302447047a2Takashi Iwai{
982ee321440e3a594dcdd9981e68e5e302447047a2Takashi Iwai	int ret = param_set_int(val, kp);
992ee321440e3a594dcdd9981e68e5e302447047a2Takashi Iwai	if (!ret) {
1002ee321440e3a594dcdd9981e68e5e302447047a2Takashi Iwai		int val = *(int *)kp->arg;
1012ee321440e3a594dcdd9981e68e5e302447047a2Takashi Iwai		if (val < 0)
1022ee321440e3a594dcdd9981e68e5e302447047a2Takashi Iwai			val = -val;
1032ee321440e3a594dcdd9981e68e5e302447047a2Takashi Iwai		if (!val || val > 3)
1042ee321440e3a594dcdd9981e68e5e302447047a2Takashi Iwai			return -EINVAL;
1052ee321440e3a594dcdd9981e68e5e302447047a2Takashi Iwai	}
1062ee321440e3a594dcdd9981e68e5e302447047a2Takashi Iwai	return ret;
1072ee321440e3a594dcdd9981e68e5e302447047a2Takashi Iwai}
1082ee321440e3a594dcdd9981e68e5e302447047a2Takashi Iwai
1092ee321440e3a594dcdd9981e68e5e302447047a2Takashi Iwaistatic struct kernel_param_ops param_ops_axis = {
1102ee321440e3a594dcdd9981e68e5e302447047a2Takashi Iwai	.set = param_set_axis,
1112ee321440e3a594dcdd9981e68e5e302447047a2Takashi Iwai	.get = param_get_int,
1122ee321440e3a594dcdd9981e68e5e302447047a2Takashi Iwai};
1132ee321440e3a594dcdd9981e68e5e302447047a2Takashi Iwai
1142ee321440e3a594dcdd9981e68e5e302447047a2Takashi Iwaimodule_param_array_named(axes, lis3_dev.ac.as_array, axis, NULL, 0644);
1152ee321440e3a594dcdd9981e68e5e302447047a2Takashi IwaiMODULE_PARM_DESC(axes, "Axis-mapping for x,y,z directions");
1162ee321440e3a594dcdd9981e68e5e302447047a2Takashi Iwai
117a38da2ed74f628c1f3e907c772be21a66eccab9cDaniel Mackstatic s16 lis3lv02d_read_8(struct lis3lv02d *lis3, int reg)
118a38da2ed74f628c1f3e907c772be21a66eccab9cDaniel Mack{
119a38da2ed74f628c1f3e907c772be21a66eccab9cDaniel Mack	s8 lo;
120a38da2ed74f628c1f3e907c772be21a66eccab9cDaniel Mack	if (lis3->read(lis3, reg, &lo) < 0)
121a38da2ed74f628c1f3e907c772be21a66eccab9cDaniel Mack		return 0;
122a38da2ed74f628c1f3e907c772be21a66eccab9cDaniel Mack
123a38da2ed74f628c1f3e907c772be21a66eccab9cDaniel Mack	return lo;
124a38da2ed74f628c1f3e907c772be21a66eccab9cDaniel Mack}
125a38da2ed74f628c1f3e907c772be21a66eccab9cDaniel Mack
126bc62c1471773fc32adcfc05100abd16fa2b6e126Éric Pielstatic s16 lis3lv02d_read_12(struct lis3lv02d *lis3, int reg)
127be84cfc588b19f14764d78556dc7b630ee8c914cPavel Machek{
128be84cfc588b19f14764d78556dc7b630ee8c914cPavel Machek	u8 lo, hi;
129be84cfc588b19f14764d78556dc7b630ee8c914cPavel Machek
130a38da2ed74f628c1f3e907c772be21a66eccab9cDaniel Mack	lis3->read(lis3, reg - 1, &lo);
131a38da2ed74f628c1f3e907c772be21a66eccab9cDaniel Mack	lis3->read(lis3, reg, &hi);
132be84cfc588b19f14764d78556dc7b630ee8c914cPavel Machek	/* In "12 bit right justified" mode, bit 6, bit 7, bit 8 = bit 5 */
133be84cfc588b19f14764d78556dc7b630ee8c914cPavel Machek	return (s16)((hi << 8) | lo);
134be84cfc588b19f14764d78556dc7b630ee8c914cPavel Machek}
135be84cfc588b19f14764d78556dc7b630ee8c914cPavel Machek
136455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek/**
137455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek * lis3lv02d_get_axis - For the given axis, give the value converted
138455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek * @axis:      1,2,3 - can also be negative
139455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek * @hw_values: raw values returned by the hardware
140455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek *
141455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek * Returns the converted value.
142455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek */
143455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machekstatic inline int lis3lv02d_get_axis(s8 axis, int hw_values[3])
144455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek{
145455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek	if (axis > 0)
146455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek		return hw_values[axis - 1];
147455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek	else
148455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek		return -hw_values[-axis - 1];
149455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek}
150455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek
151455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek/**
152455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek * lis3lv02d_get_xyz - Get X, Y and Z axis values from the accelerometer
153a38da2ed74f628c1f3e907c772be21a66eccab9cDaniel Mack * @lis3: pointer to the device struct
154a38da2ed74f628c1f3e907c772be21a66eccab9cDaniel Mack * @x:    where to store the X axis value
155a38da2ed74f628c1f3e907c772be21a66eccab9cDaniel Mack * @y:    where to store the Y axis value
156a38da2ed74f628c1f3e907c772be21a66eccab9cDaniel Mack * @z:    where to store the Z axis value
157455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek *
158455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek * Note that 40Hz input device can eat up about 10% CPU at 800MHZ
159455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek */
160a38da2ed74f628c1f3e907c772be21a66eccab9cDaniel Mackstatic void lis3lv02d_get_xyz(struct lis3lv02d *lis3, int *x, int *y, int *z)
161455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek{
162455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek	int position[3];
16332496c76b777752ba84b125bebfb0cc498f5602cSamu Onkalo	int i;
164455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek
165f10a5407b58603fb3b084d7fbdbd50f47d010c82Samu Onkalo	if (lis3->blkread) {
166f10a5407b58603fb3b084d7fbdbd50f47d010c82Samu Onkalo		if (lis3_dev.whoami == WAI_12B) {
167f10a5407b58603fb3b084d7fbdbd50f47d010c82Samu Onkalo			u16 data[3];
168f10a5407b58603fb3b084d7fbdbd50f47d010c82Samu Onkalo			lis3->blkread(lis3, OUTX_L, 6, (u8 *)data);
169f10a5407b58603fb3b084d7fbdbd50f47d010c82Samu Onkalo			for (i = 0; i < 3; i++)
170f10a5407b58603fb3b084d7fbdbd50f47d010c82Samu Onkalo				position[i] = (s16)le16_to_cpu(data[i]);
171f10a5407b58603fb3b084d7fbdbd50f47d010c82Samu Onkalo		} else {
172f10a5407b58603fb3b084d7fbdbd50f47d010c82Samu Onkalo			u8 data[5];
173f10a5407b58603fb3b084d7fbdbd50f47d010c82Samu Onkalo			/* Data: x, dummy, y, dummy, z */
174f10a5407b58603fb3b084d7fbdbd50f47d010c82Samu Onkalo			lis3->blkread(lis3, OUTX, 5, data);
175f10a5407b58603fb3b084d7fbdbd50f47d010c82Samu Onkalo			for (i = 0; i < 3; i++)
176f10a5407b58603fb3b084d7fbdbd50f47d010c82Samu Onkalo				position[i] = (s8)data[i * 2];
177f10a5407b58603fb3b084d7fbdbd50f47d010c82Samu Onkalo		}
178f10a5407b58603fb3b084d7fbdbd50f47d010c82Samu Onkalo	} else {
179f10a5407b58603fb3b084d7fbdbd50f47d010c82Samu Onkalo		position[0] = lis3->read_data(lis3, OUTX);
180f10a5407b58603fb3b084d7fbdbd50f47d010c82Samu Onkalo		position[1] = lis3->read_data(lis3, OUTY);
181f10a5407b58603fb3b084d7fbdbd50f47d010c82Samu Onkalo		position[2] = lis3->read_data(lis3, OUTZ);
182f10a5407b58603fb3b084d7fbdbd50f47d010c82Samu Onkalo	}
183455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek
18432496c76b777752ba84b125bebfb0cc498f5602cSamu Onkalo	for (i = 0; i < 3; i++)
18532496c76b777752ba84b125bebfb0cc498f5602cSamu Onkalo		position[i] = (position[i] * lis3->scale) / LIS3_ACCURACY;
18632496c76b777752ba84b125bebfb0cc498f5602cSamu Onkalo
187a002ee896dfd08ce9fba44e9ae513c9094699a27Eric Piel	*x = lis3lv02d_get_axis(lis3->ac.x, position);
188a002ee896dfd08ce9fba44e9ae513c9094699a27Eric Piel	*y = lis3lv02d_get_axis(lis3->ac.y, position);
189a002ee896dfd08ce9fba44e9ae513c9094699a27Eric Piel	*z = lis3lv02d_get_axis(lis3->ac.z, position);
190455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek}
191455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek
192641615abfac0b7c5e6f242a6db77f7690925b443Samu Onkalo/* conversion btw sampling rate and the register values */
193641615abfac0b7c5e6f242a6db77f7690925b443Samu Onkalostatic int lis3_12_rates[4] = {40, 160, 640, 2560};
194641615abfac0b7c5e6f242a6db77f7690925b443Samu Onkalostatic int lis3_8_rates[2] = {100, 400};
19578537c3b6ffcb69bf4fd43a74ba57928fcefce95Takashi Iwaistatic int lis3_3dc_rates[16] = {0, 1, 10, 25, 50, 100, 200, 400, 1600, 5000};
196641615abfac0b7c5e6f242a6db77f7690925b443Samu Onkalo
197a253aaef60a37bddfa84846353edeb62a6acf5b3Samu Onkalo/* ODR is Output Data Rate */
198641615abfac0b7c5e6f242a6db77f7690925b443Samu Onkalostatic int lis3lv02d_get_odr(void)
199641615abfac0b7c5e6f242a6db77f7690925b443Samu Onkalo{
200641615abfac0b7c5e6f242a6db77f7690925b443Samu Onkalo	u8 ctrl;
201a253aaef60a37bddfa84846353edeb62a6acf5b3Samu Onkalo	int shift;
202641615abfac0b7c5e6f242a6db77f7690925b443Samu Onkalo
203641615abfac0b7c5e6f242a6db77f7690925b443Samu Onkalo	lis3_dev.read(&lis3_dev, CTRL_REG1, &ctrl);
204a253aaef60a37bddfa84846353edeb62a6acf5b3Samu Onkalo	ctrl &= lis3_dev.odr_mask;
205a253aaef60a37bddfa84846353edeb62a6acf5b3Samu Onkalo	shift = ffs(lis3_dev.odr_mask) - 1;
206a253aaef60a37bddfa84846353edeb62a6acf5b3Samu Onkalo	return lis3_dev.odrs[(ctrl >> shift)];
207a253aaef60a37bddfa84846353edeb62a6acf5b3Samu Onkalo}
208641615abfac0b7c5e6f242a6db77f7690925b443Samu Onkalo
209a253aaef60a37bddfa84846353edeb62a6acf5b3Samu Onkalostatic int lis3lv02d_set_odr(int rate)
210a253aaef60a37bddfa84846353edeb62a6acf5b3Samu Onkalo{
211a253aaef60a37bddfa84846353edeb62a6acf5b3Samu Onkalo	u8 ctrl;
212a253aaef60a37bddfa84846353edeb62a6acf5b3Samu Onkalo	int i, len, shift;
213a253aaef60a37bddfa84846353edeb62a6acf5b3Samu Onkalo
21478537c3b6ffcb69bf4fd43a74ba57928fcefce95Takashi Iwai	if (!rate)
21578537c3b6ffcb69bf4fd43a74ba57928fcefce95Takashi Iwai		return -EINVAL;
21678537c3b6ffcb69bf4fd43a74ba57928fcefce95Takashi Iwai
217a253aaef60a37bddfa84846353edeb62a6acf5b3Samu Onkalo	lis3_dev.read(&lis3_dev, CTRL_REG1, &ctrl);
218a253aaef60a37bddfa84846353edeb62a6acf5b3Samu Onkalo	ctrl &= ~lis3_dev.odr_mask;
219a253aaef60a37bddfa84846353edeb62a6acf5b3Samu Onkalo	len = 1 << hweight_long(lis3_dev.odr_mask); /* # of possible values */
220a253aaef60a37bddfa84846353edeb62a6acf5b3Samu Onkalo	shift = ffs(lis3_dev.odr_mask) - 1;
221a253aaef60a37bddfa84846353edeb62a6acf5b3Samu Onkalo
222a253aaef60a37bddfa84846353edeb62a6acf5b3Samu Onkalo	for (i = 0; i < len; i++)
223a253aaef60a37bddfa84846353edeb62a6acf5b3Samu Onkalo		if (lis3_dev.odrs[i] == rate) {
224a253aaef60a37bddfa84846353edeb62a6acf5b3Samu Onkalo			lis3_dev.write(&lis3_dev, CTRL_REG1,
225a253aaef60a37bddfa84846353edeb62a6acf5b3Samu Onkalo					ctrl | (i << shift));
226a253aaef60a37bddfa84846353edeb62a6acf5b3Samu Onkalo			return 0;
227a253aaef60a37bddfa84846353edeb62a6acf5b3Samu Onkalo		}
228a253aaef60a37bddfa84846353edeb62a6acf5b3Samu Onkalo	return -EINVAL;
229641615abfac0b7c5e6f242a6db77f7690925b443Samu Onkalo}
230641615abfac0b7c5e6f242a6db77f7690925b443Samu Onkalo
2312db4a76d5f3554e9e5632c8f91828313318579c8Samu Onkalostatic int lis3lv02d_selftest(struct lis3lv02d *lis3, s16 results[3])
2322db4a76d5f3554e9e5632c8f91828313318579c8Samu Onkalo{
23378537c3b6ffcb69bf4fd43a74ba57928fcefce95Takashi Iwai	u8 ctlreg, reg;
2342db4a76d5f3554e9e5632c8f91828313318579c8Samu Onkalo	s16 x, y, z;
2352db4a76d5f3554e9e5632c8f91828313318579c8Samu Onkalo	u8 selftest;
2362db4a76d5f3554e9e5632c8f91828313318579c8Samu Onkalo	int ret;
237029756d0b8856f52d83dee81c01dd3af786cadffSamu Onkalo	u8 ctrl_reg_data;
238029756d0b8856f52d83dee81c01dd3af786cadffSamu Onkalo	unsigned char irq_cfg;
2392db4a76d5f3554e9e5632c8f91828313318579c8Samu Onkalo
2402db4a76d5f3554e9e5632c8f91828313318579c8Samu Onkalo	mutex_lock(&lis3->mutex);
241029756d0b8856f52d83dee81c01dd3af786cadffSamu Onkalo
242029756d0b8856f52d83dee81c01dd3af786cadffSamu Onkalo	irq_cfg = lis3->irq_cfg;
243029756d0b8856f52d83dee81c01dd3af786cadffSamu Onkalo	if (lis3_dev.whoami == WAI_8B) {
244029756d0b8856f52d83dee81c01dd3af786cadffSamu Onkalo		lis3->data_ready_count[IRQ_LINE0] = 0;
245029756d0b8856f52d83dee81c01dd3af786cadffSamu Onkalo		lis3->data_ready_count[IRQ_LINE1] = 0;
246029756d0b8856f52d83dee81c01dd3af786cadffSamu Onkalo
247029756d0b8856f52d83dee81c01dd3af786cadffSamu Onkalo		/* Change interrupt cfg to data ready for selftest */
248029756d0b8856f52d83dee81c01dd3af786cadffSamu Onkalo		atomic_inc(&lis3_dev.wake_thread);
249029756d0b8856f52d83dee81c01dd3af786cadffSamu Onkalo		lis3->irq_cfg = LIS3_IRQ1_DATA_READY | LIS3_IRQ2_DATA_READY;
250029756d0b8856f52d83dee81c01dd3af786cadffSamu Onkalo		lis3->read(lis3, CTRL_REG3, &ctrl_reg_data);
251029756d0b8856f52d83dee81c01dd3af786cadffSamu Onkalo		lis3->write(lis3, CTRL_REG3, (ctrl_reg_data &
252029756d0b8856f52d83dee81c01dd3af786cadffSamu Onkalo				~(LIS3_IRQ1_MASK | LIS3_IRQ2_MASK)) |
253029756d0b8856f52d83dee81c01dd3af786cadffSamu Onkalo				(LIS3_IRQ1_DATA_READY | LIS3_IRQ2_DATA_READY));
254029756d0b8856f52d83dee81c01dd3af786cadffSamu Onkalo	}
255029756d0b8856f52d83dee81c01dd3af786cadffSamu Onkalo
25678537c3b6ffcb69bf4fd43a74ba57928fcefce95Takashi Iwai	if (lis3_dev.whoami == WAI_3DC) {
25778537c3b6ffcb69bf4fd43a74ba57928fcefce95Takashi Iwai		ctlreg = CTRL_REG4;
25878537c3b6ffcb69bf4fd43a74ba57928fcefce95Takashi Iwai		selftest = CTRL4_ST0;
25978537c3b6ffcb69bf4fd43a74ba57928fcefce95Takashi Iwai	} else {
26078537c3b6ffcb69bf4fd43a74ba57928fcefce95Takashi Iwai		ctlreg = CTRL_REG1;
26178537c3b6ffcb69bf4fd43a74ba57928fcefce95Takashi Iwai		if (lis3_dev.whoami == WAI_12B)
26278537c3b6ffcb69bf4fd43a74ba57928fcefce95Takashi Iwai			selftest = CTRL1_ST;
26378537c3b6ffcb69bf4fd43a74ba57928fcefce95Takashi Iwai		else
26478537c3b6ffcb69bf4fd43a74ba57928fcefce95Takashi Iwai			selftest = CTRL1_STP;
26578537c3b6ffcb69bf4fd43a74ba57928fcefce95Takashi Iwai	}
2662db4a76d5f3554e9e5632c8f91828313318579c8Samu Onkalo
26778537c3b6ffcb69bf4fd43a74ba57928fcefce95Takashi Iwai	lis3->read(lis3, ctlreg, &reg);
26878537c3b6ffcb69bf4fd43a74ba57928fcefce95Takashi Iwai	lis3->write(lis3, ctlreg, (reg | selftest));
2692db4a76d5f3554e9e5632c8f91828313318579c8Samu Onkalo	msleep(lis3->pwron_delay / lis3lv02d_get_odr());
2702db4a76d5f3554e9e5632c8f91828313318579c8Samu Onkalo
2712db4a76d5f3554e9e5632c8f91828313318579c8Samu Onkalo	/* Read directly to avoid axis remap */
2722db4a76d5f3554e9e5632c8f91828313318579c8Samu Onkalo	x = lis3->read_data(lis3, OUTX);
2732db4a76d5f3554e9e5632c8f91828313318579c8Samu Onkalo	y = lis3->read_data(lis3, OUTY);
2742db4a76d5f3554e9e5632c8f91828313318579c8Samu Onkalo	z = lis3->read_data(lis3, OUTZ);
2752db4a76d5f3554e9e5632c8f91828313318579c8Samu Onkalo
2762db4a76d5f3554e9e5632c8f91828313318579c8Samu Onkalo	/* back to normal settings */
27778537c3b6ffcb69bf4fd43a74ba57928fcefce95Takashi Iwai	lis3->write(lis3, ctlreg, reg);
2782db4a76d5f3554e9e5632c8f91828313318579c8Samu Onkalo	msleep(lis3->pwron_delay / lis3lv02d_get_odr());
2792db4a76d5f3554e9e5632c8f91828313318579c8Samu Onkalo
2802db4a76d5f3554e9e5632c8f91828313318579c8Samu Onkalo	results[0] = x - lis3->read_data(lis3, OUTX);
2812db4a76d5f3554e9e5632c8f91828313318579c8Samu Onkalo	results[1] = y - lis3->read_data(lis3, OUTY);
2822db4a76d5f3554e9e5632c8f91828313318579c8Samu Onkalo	results[2] = z - lis3->read_data(lis3, OUTZ);
2832db4a76d5f3554e9e5632c8f91828313318579c8Samu Onkalo
2842db4a76d5f3554e9e5632c8f91828313318579c8Samu Onkalo	ret = 0;
285029756d0b8856f52d83dee81c01dd3af786cadffSamu Onkalo
286029756d0b8856f52d83dee81c01dd3af786cadffSamu Onkalo	if (lis3_dev.whoami == WAI_8B) {
287029756d0b8856f52d83dee81c01dd3af786cadffSamu Onkalo		/* Restore original interrupt configuration */
288029756d0b8856f52d83dee81c01dd3af786cadffSamu Onkalo		atomic_dec(&lis3_dev.wake_thread);
289029756d0b8856f52d83dee81c01dd3af786cadffSamu Onkalo		lis3->write(lis3, CTRL_REG3, ctrl_reg_data);
290029756d0b8856f52d83dee81c01dd3af786cadffSamu Onkalo		lis3->irq_cfg = irq_cfg;
291029756d0b8856f52d83dee81c01dd3af786cadffSamu Onkalo
292029756d0b8856f52d83dee81c01dd3af786cadffSamu Onkalo		if ((irq_cfg & LIS3_IRQ1_MASK) &&
293029756d0b8856f52d83dee81c01dd3af786cadffSamu Onkalo			lis3->data_ready_count[IRQ_LINE0] < 2) {
294029756d0b8856f52d83dee81c01dd3af786cadffSamu Onkalo			ret = SELFTEST_IRQ;
295029756d0b8856f52d83dee81c01dd3af786cadffSamu Onkalo			goto fail;
296029756d0b8856f52d83dee81c01dd3af786cadffSamu Onkalo		}
297029756d0b8856f52d83dee81c01dd3af786cadffSamu Onkalo
298029756d0b8856f52d83dee81c01dd3af786cadffSamu Onkalo		if ((irq_cfg & LIS3_IRQ2_MASK) &&
299029756d0b8856f52d83dee81c01dd3af786cadffSamu Onkalo			lis3->data_ready_count[IRQ_LINE1] < 2) {
300029756d0b8856f52d83dee81c01dd3af786cadffSamu Onkalo			ret = SELFTEST_IRQ;
301029756d0b8856f52d83dee81c01dd3af786cadffSamu Onkalo			goto fail;
302029756d0b8856f52d83dee81c01dd3af786cadffSamu Onkalo		}
303029756d0b8856f52d83dee81c01dd3af786cadffSamu Onkalo	}
304029756d0b8856f52d83dee81c01dd3af786cadffSamu Onkalo
3052db4a76d5f3554e9e5632c8f91828313318579c8Samu Onkalo	if (lis3->pdata) {
3062db4a76d5f3554e9e5632c8f91828313318579c8Samu Onkalo		int i;
3072db4a76d5f3554e9e5632c8f91828313318579c8Samu Onkalo		for (i = 0; i < 3; i++) {
3082db4a76d5f3554e9e5632c8f91828313318579c8Samu Onkalo			/* Check against selftest acceptance limits */
3092db4a76d5f3554e9e5632c8f91828313318579c8Samu Onkalo			if ((results[i] < lis3->pdata->st_min_limits[i]) ||
3102db4a76d5f3554e9e5632c8f91828313318579c8Samu Onkalo			    (results[i] > lis3->pdata->st_max_limits[i])) {
311029756d0b8856f52d83dee81c01dd3af786cadffSamu Onkalo				ret = SELFTEST_FAIL;
3122db4a76d5f3554e9e5632c8f91828313318579c8Samu Onkalo				goto fail;
3132db4a76d5f3554e9e5632c8f91828313318579c8Samu Onkalo			}
3142db4a76d5f3554e9e5632c8f91828313318579c8Samu Onkalo		}
3152db4a76d5f3554e9e5632c8f91828313318579c8Samu Onkalo	}
3162db4a76d5f3554e9e5632c8f91828313318579c8Samu Onkalo
3172db4a76d5f3554e9e5632c8f91828313318579c8Samu Onkalo	/* test passed */
3182db4a76d5f3554e9e5632c8f91828313318579c8Samu Onkalofail:
3192db4a76d5f3554e9e5632c8f91828313318579c8Samu Onkalo	mutex_unlock(&lis3->mutex);
3202db4a76d5f3554e9e5632c8f91828313318579c8Samu Onkalo	return ret;
3212db4a76d5f3554e9e5632c8f91828313318579c8Samu Onkalo}
3222db4a76d5f3554e9e5632c8f91828313318579c8Samu Onkalo
323f9deb41f91c41d9d91a24c84a555ec7fe82620daSamu Onkalo/*
324f9deb41f91c41d9d91a24c84a555ec7fe82620daSamu Onkalo * Order of registers in the list affects to order of the restore process.
325f9deb41f91c41d9d91a24c84a555ec7fe82620daSamu Onkalo * Perhaps it is a good idea to set interrupt enable register as a last one
326f9deb41f91c41d9d91a24c84a555ec7fe82620daSamu Onkalo * after all other configurations
327f9deb41f91c41d9d91a24c84a555ec7fe82620daSamu Onkalo */
328f9deb41f91c41d9d91a24c84a555ec7fe82620daSamu Onkalostatic u8 lis3_wai8_regs[] = { FF_WU_CFG_1, FF_WU_THS_1, FF_WU_DURATION_1,
329f9deb41f91c41d9d91a24c84a555ec7fe82620daSamu Onkalo			       FF_WU_CFG_2, FF_WU_THS_2, FF_WU_DURATION_2,
330f9deb41f91c41d9d91a24c84a555ec7fe82620daSamu Onkalo			       CLICK_CFG, CLICK_SRC, CLICK_THSY_X, CLICK_THSZ,
331f9deb41f91c41d9d91a24c84a555ec7fe82620daSamu Onkalo			       CLICK_TIMELIMIT, CLICK_LATENCY, CLICK_WINDOW,
332f9deb41f91c41d9d91a24c84a555ec7fe82620daSamu Onkalo			       CTRL_REG1, CTRL_REG2, CTRL_REG3};
333f9deb41f91c41d9d91a24c84a555ec7fe82620daSamu Onkalo
334f9deb41f91c41d9d91a24c84a555ec7fe82620daSamu Onkalostatic u8 lis3_wai12_regs[] = {FF_WU_CFG, FF_WU_THS_L, FF_WU_THS_H,
335f9deb41f91c41d9d91a24c84a555ec7fe82620daSamu Onkalo			       FF_WU_DURATION, DD_CFG, DD_THSI_L, DD_THSI_H,
336f9deb41f91c41d9d91a24c84a555ec7fe82620daSamu Onkalo			       DD_THSE_L, DD_THSE_H,
337f9deb41f91c41d9d91a24c84a555ec7fe82620daSamu Onkalo			       CTRL_REG1, CTRL_REG3, CTRL_REG2};
338f9deb41f91c41d9d91a24c84a555ec7fe82620daSamu Onkalo
339f9deb41f91c41d9d91a24c84a555ec7fe82620daSamu Onkalostatic inline void lis3_context_save(struct lis3lv02d *lis3)
340f9deb41f91c41d9d91a24c84a555ec7fe82620daSamu Onkalo{
341f9deb41f91c41d9d91a24c84a555ec7fe82620daSamu Onkalo	int i;
342f9deb41f91c41d9d91a24c84a555ec7fe82620daSamu Onkalo	for (i = 0; i < lis3->regs_size; i++)
343f9deb41f91c41d9d91a24c84a555ec7fe82620daSamu Onkalo		lis3->read(lis3, lis3->regs[i], &lis3->reg_cache[i]);
344f9deb41f91c41d9d91a24c84a555ec7fe82620daSamu Onkalo	lis3->regs_stored = true;
345f9deb41f91c41d9d91a24c84a555ec7fe82620daSamu Onkalo}
346f9deb41f91c41d9d91a24c84a555ec7fe82620daSamu Onkalo
347f9deb41f91c41d9d91a24c84a555ec7fe82620daSamu Onkalostatic inline void lis3_context_restore(struct lis3lv02d *lis3)
348f9deb41f91c41d9d91a24c84a555ec7fe82620daSamu Onkalo{
349f9deb41f91c41d9d91a24c84a555ec7fe82620daSamu Onkalo	int i;
350f9deb41f91c41d9d91a24c84a555ec7fe82620daSamu Onkalo	if (lis3->regs_stored)
351f9deb41f91c41d9d91a24c84a555ec7fe82620daSamu Onkalo		for (i = 0; i < lis3->regs_size; i++)
352f9deb41f91c41d9d91a24c84a555ec7fe82620daSamu Onkalo			lis3->write(lis3, lis3->regs[i], lis3->reg_cache[i]);
353f9deb41f91c41d9d91a24c84a555ec7fe82620daSamu Onkalo}
354f9deb41f91c41d9d91a24c84a555ec7fe82620daSamu Onkalo
355a38da2ed74f628c1f3e907c772be21a66eccab9cDaniel Mackvoid lis3lv02d_poweroff(struct lis3lv02d *lis3)
356455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek{
357f9deb41f91c41d9d91a24c84a555ec7fe82620daSamu Onkalo	if (lis3->reg_ctrl)
358f9deb41f91c41d9d91a24c84a555ec7fe82620daSamu Onkalo		lis3_context_save(lis3);
359a002ee896dfd08ce9fba44e9ae513c9094699a27Eric Piel	/* disable X,Y,Z axis and power down */
360a002ee896dfd08ce9fba44e9ae513c9094699a27Eric Piel	lis3->write(lis3, CTRL_REG1, 0x00);
361f9deb41f91c41d9d91a24c84a555ec7fe82620daSamu Onkalo	if (lis3->reg_ctrl)
362f9deb41f91c41d9d91a24c84a555ec7fe82620daSamu Onkalo		lis3->reg_ctrl(lis3, LIS3_REG_OFF);
363455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek}
364cfce41a6d643c001d416ead960caf04fae2d609aEric PielEXPORT_SYMBOL_GPL(lis3lv02d_poweroff);
365455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek
366a38da2ed74f628c1f3e907c772be21a66eccab9cDaniel Mackvoid lis3lv02d_poweron(struct lis3lv02d *lis3)
367455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek{
368a002ee896dfd08ce9fba44e9ae513c9094699a27Eric Piel	u8 reg;
369455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek
370a002ee896dfd08ce9fba44e9ae513c9094699a27Eric Piel	lis3->init(lis3);
371455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek
372a002ee896dfd08ce9fba44e9ae513c9094699a27Eric Piel	/*
373a002ee896dfd08ce9fba44e9ae513c9094699a27Eric Piel	 * Common configuration
3744b5d95b3809bcd77599122494aa3f575cd6ab1b9Éric Piel	 * BDU: (12 bits sensors only) LSB and MSB values are not updated until
3754b5d95b3809bcd77599122494aa3f575cd6ab1b9Éric Piel	 *      both have been read. So the value read will always be correct.
3762a7fade7e03a7c773f91e2e5ff26ad6fafda5a9fSamu Onkalo	 * Set BOOT bit to refresh factory tuning values.
377a002ee896dfd08ce9fba44e9ae513c9094699a27Eric Piel	 */
37805faadcf59507e8eea57ffbeea9cbb14c9a2ab3dTakashi Iwai	if (lis3->pdata) {
37905faadcf59507e8eea57ffbeea9cbb14c9a2ab3dTakashi Iwai		lis3->read(lis3, CTRL_REG2, &reg);
38005faadcf59507e8eea57ffbeea9cbb14c9a2ab3dTakashi Iwai		if (lis3->whoami ==  WAI_12B)
38105faadcf59507e8eea57ffbeea9cbb14c9a2ab3dTakashi Iwai			reg |= CTRL2_BDU | CTRL2_BOOT;
38205faadcf59507e8eea57ffbeea9cbb14c9a2ab3dTakashi Iwai		else
38305faadcf59507e8eea57ffbeea9cbb14c9a2ab3dTakashi Iwai			reg |= CTRL2_BOOT_8B;
38405faadcf59507e8eea57ffbeea9cbb14c9a2ab3dTakashi Iwai		lis3->write(lis3, CTRL_REG2, reg);
38505faadcf59507e8eea57ffbeea9cbb14c9a2ab3dTakashi Iwai	}
3862a7fade7e03a7c773f91e2e5ff26ad6fafda5a9fSamu Onkalo
3872a7fade7e03a7c773f91e2e5ff26ad6fafda5a9fSamu Onkalo	/* LIS3 power on delay is quite long */
3882a7fade7e03a7c773f91e2e5ff26ad6fafda5a9fSamu Onkalo	msleep(lis3->pwron_delay / lis3lv02d_get_odr());
3892a7fade7e03a7c773f91e2e5ff26ad6fafda5a9fSamu Onkalo
390f9deb41f91c41d9d91a24c84a555ec7fe82620daSamu Onkalo	if (lis3->reg_ctrl)
391f9deb41f91c41d9d91a24c84a555ec7fe82620daSamu Onkalo		lis3_context_restore(lis3);
392455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek}
393a002ee896dfd08ce9fba44e9ae513c9094699a27Eric PielEXPORT_SYMBOL_GPL(lis3lv02d_poweron);
394a002ee896dfd08ce9fba44e9ae513c9094699a27Eric Piel
395455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek
3966d94d4081048756df78444a07201156f4930fe48Samu Onkalostatic void lis3lv02d_joystick_poll(struct input_polled_dev *pidev)
3976d94d4081048756df78444a07201156f4930fe48Samu Onkalo{
3986d94d4081048756df78444a07201156f4930fe48Samu Onkalo	int x, y, z;
3996d94d4081048756df78444a07201156f4930fe48Samu Onkalo
4006d94d4081048756df78444a07201156f4930fe48Samu Onkalo	mutex_lock(&lis3_dev.mutex);
4016d94d4081048756df78444a07201156f4930fe48Samu Onkalo	lis3lv02d_get_xyz(&lis3_dev, &x, &y, &z);
4026d94d4081048756df78444a07201156f4930fe48Samu Onkalo	input_report_abs(pidev->input, ABS_X, x);
4036d94d4081048756df78444a07201156f4930fe48Samu Onkalo	input_report_abs(pidev->input, ABS_Y, y);
4046d94d4081048756df78444a07201156f4930fe48Samu Onkalo	input_report_abs(pidev->input, ABS_Z, z);
4056d94d4081048756df78444a07201156f4930fe48Samu Onkalo	input_sync(pidev->input);
4066d94d4081048756df78444a07201156f4930fe48Samu Onkalo	mutex_unlock(&lis3_dev.mutex);
4076d94d4081048756df78444a07201156f4930fe48Samu Onkalo}
4086d94d4081048756df78444a07201156f4930fe48Samu Onkalo
4092a346996626ecbb4269c239e9ff7372a182907e9Samu Onkalostatic void lis3lv02d_joystick_open(struct input_polled_dev *pidev)
4102a346996626ecbb4269c239e9ff7372a182907e9Samu Onkalo{
4112a346996626ecbb4269c239e9ff7372a182907e9Samu Onkalo	if (lis3_dev.pm_dev)
4122a346996626ecbb4269c239e9ff7372a182907e9Samu Onkalo		pm_runtime_get_sync(lis3_dev.pm_dev);
413e726111f953f8f5b922b953caf06ba6790c5fbaaSamu Onkalo
414e726111f953f8f5b922b953caf06ba6790c5fbaaSamu Onkalo	if (lis3_dev.pdata && lis3_dev.whoami == WAI_8B && lis3_dev.idev)
415e726111f953f8f5b922b953caf06ba6790c5fbaaSamu Onkalo		atomic_set(&lis3_dev.wake_thread, 1);
416821f664644c2da9e1a51e36751abedf49d4332e0Samu Onkalo	/*
417821f664644c2da9e1a51e36751abedf49d4332e0Samu Onkalo	 * Update coordinates for the case where poll interval is 0 and
418821f664644c2da9e1a51e36751abedf49d4332e0Samu Onkalo	 * the chip in running purely under interrupt control
419821f664644c2da9e1a51e36751abedf49d4332e0Samu Onkalo	 */
420821f664644c2da9e1a51e36751abedf49d4332e0Samu Onkalo	lis3lv02d_joystick_poll(pidev);
4212a346996626ecbb4269c239e9ff7372a182907e9Samu Onkalo}
4222a346996626ecbb4269c239e9ff7372a182907e9Samu Onkalo
4232a346996626ecbb4269c239e9ff7372a182907e9Samu Onkalostatic void lis3lv02d_joystick_close(struct input_polled_dev *pidev)
4242a346996626ecbb4269c239e9ff7372a182907e9Samu Onkalo{
425e726111f953f8f5b922b953caf06ba6790c5fbaaSamu Onkalo	atomic_set(&lis3_dev.wake_thread, 0);
4262a346996626ecbb4269c239e9ff7372a182907e9Samu Onkalo	if (lis3_dev.pm_dev)
4272a346996626ecbb4269c239e9ff7372a182907e9Samu Onkalo		pm_runtime_put(lis3_dev.pm_dev);
4282a346996626ecbb4269c239e9ff7372a182907e9Samu Onkalo}
4292a346996626ecbb4269c239e9ff7372a182907e9Samu Onkalo
430ef2cfc790bf5f0ff189b01eabc0f4feb5e8524dfPavel Machekstatic irqreturn_t lis302dl_interrupt(int irq, void *dummy)
431ef2cfc790bf5f0ff189b01eabc0f4feb5e8524dfPavel Machek{
43292ba4fe4b53b4fa5ac71ec4d80572348fca85796Samu Onkalo	if (!test_bit(0, &lis3_dev.misc_opened))
43392ba4fe4b53b4fa5ac71ec4d80572348fca85796Samu Onkalo		goto out;
43492ba4fe4b53b4fa5ac71ec4d80572348fca85796Samu Onkalo
435ef2cfc790bf5f0ff189b01eabc0f4feb5e8524dfPavel Machek	/*
436ef2cfc790bf5f0ff189b01eabc0f4feb5e8524dfPavel Machek	 * Be careful: on some HP laptops the bios force DD when on battery and
437ef2cfc790bf5f0ff189b01eabc0f4feb5e8524dfPavel Machek	 * the lid is closed. This leads to interrupts as soon as a little move
438ef2cfc790bf5f0ff189b01eabc0f4feb5e8524dfPavel Machek	 * is done.
439ef2cfc790bf5f0ff189b01eabc0f4feb5e8524dfPavel Machek	 */
440be84cfc588b19f14764d78556dc7b630ee8c914cPavel Machek	atomic_inc(&lis3_dev.count);
441ef2cfc790bf5f0ff189b01eabc0f4feb5e8524dfPavel Machek
442be84cfc588b19f14764d78556dc7b630ee8c914cPavel Machek	wake_up_interruptible(&lis3_dev.misc_wait);
443be84cfc588b19f14764d78556dc7b630ee8c914cPavel Machek	kill_fasync(&lis3_dev.async_queue, SIGIO, POLL_IN);
44492ba4fe4b53b4fa5ac71ec4d80572348fca85796Samu Onkaloout:
445e726111f953f8f5b922b953caf06ba6790c5fbaaSamu Onkalo	if (atomic_read(&lis3_dev.wake_thread))
44692ba4fe4b53b4fa5ac71ec4d80572348fca85796Samu Onkalo		return IRQ_WAKE_THREAD;
447ef2cfc790bf5f0ff189b01eabc0f4feb5e8524dfPavel Machek	return IRQ_HANDLED;
448ef2cfc790bf5f0ff189b01eabc0f4feb5e8524dfPavel Machek}
449ef2cfc790bf5f0ff189b01eabc0f4feb5e8524dfPavel Machek
4506d94d4081048756df78444a07201156f4930fe48Samu Onkalostatic void lis302dl_interrupt_handle_click(struct lis3lv02d *lis3)
4516d94d4081048756df78444a07201156f4930fe48Samu Onkalo{
4526d94d4081048756df78444a07201156f4930fe48Samu Onkalo	struct input_dev *dev = lis3->idev->input;
4536d94d4081048756df78444a07201156f4930fe48Samu Onkalo	u8 click_src;
4546d94d4081048756df78444a07201156f4930fe48Samu Onkalo
4556d94d4081048756df78444a07201156f4930fe48Samu Onkalo	mutex_lock(&lis3->mutex);
4566d94d4081048756df78444a07201156f4930fe48Samu Onkalo	lis3->read(lis3, CLICK_SRC, &click_src);
4576d94d4081048756df78444a07201156f4930fe48Samu Onkalo
4586d94d4081048756df78444a07201156f4930fe48Samu Onkalo	if (click_src & CLICK_SINGLE_X) {
4596d94d4081048756df78444a07201156f4930fe48Samu Onkalo		input_report_key(dev, lis3->mapped_btns[0], 1);
4606d94d4081048756df78444a07201156f4930fe48Samu Onkalo		input_report_key(dev, lis3->mapped_btns[0], 0);
4616d94d4081048756df78444a07201156f4930fe48Samu Onkalo	}
4626d94d4081048756df78444a07201156f4930fe48Samu Onkalo
4636d94d4081048756df78444a07201156f4930fe48Samu Onkalo	if (click_src & CLICK_SINGLE_Y) {
4646d94d4081048756df78444a07201156f4930fe48Samu Onkalo		input_report_key(dev, lis3->mapped_btns[1], 1);
4656d94d4081048756df78444a07201156f4930fe48Samu Onkalo		input_report_key(dev, lis3->mapped_btns[1], 0);
4666d94d4081048756df78444a07201156f4930fe48Samu Onkalo	}
4676d94d4081048756df78444a07201156f4930fe48Samu Onkalo
4686d94d4081048756df78444a07201156f4930fe48Samu Onkalo	if (click_src & CLICK_SINGLE_Z) {
4696d94d4081048756df78444a07201156f4930fe48Samu Onkalo		input_report_key(dev, lis3->mapped_btns[2], 1);
4706d94d4081048756df78444a07201156f4930fe48Samu Onkalo		input_report_key(dev, lis3->mapped_btns[2], 0);
4716d94d4081048756df78444a07201156f4930fe48Samu Onkalo	}
4726d94d4081048756df78444a07201156f4930fe48Samu Onkalo	input_sync(dev);
4736d94d4081048756df78444a07201156f4930fe48Samu Onkalo	mutex_unlock(&lis3->mutex);
4746d94d4081048756df78444a07201156f4930fe48Samu Onkalo}
4756d94d4081048756df78444a07201156f4930fe48Samu Onkalo
476029756d0b8856f52d83dee81c01dd3af786cadffSamu Onkalostatic inline void lis302dl_data_ready(struct lis3lv02d *lis3, int index)
477ef2cfc790bf5f0ff189b01eabc0f4feb5e8524dfPavel Machek{
478029756d0b8856f52d83dee81c01dd3af786cadffSamu Onkalo	int dummy;
479029756d0b8856f52d83dee81c01dd3af786cadffSamu Onkalo
480029756d0b8856f52d83dee81c01dd3af786cadffSamu Onkalo	/* Dummy read to ack interrupt */
481029756d0b8856f52d83dee81c01dd3af786cadffSamu Onkalo	lis3lv02d_get_xyz(lis3, &dummy, &dummy, &dummy);
482029756d0b8856f52d83dee81c01dd3af786cadffSamu Onkalo	lis3->data_ready_count[index]++;
483029756d0b8856f52d83dee81c01dd3af786cadffSamu Onkalo}
4846d94d4081048756df78444a07201156f4930fe48Samu Onkalo
485029756d0b8856f52d83dee81c01dd3af786cadffSamu Onkalostatic irqreturn_t lis302dl_interrupt_thread1_8b(int irq, void *data)
486029756d0b8856f52d83dee81c01dd3af786cadffSamu Onkalo{
4876d94d4081048756df78444a07201156f4930fe48Samu Onkalo	struct lis3lv02d *lis3 = data;
488029756d0b8856f52d83dee81c01dd3af786cadffSamu Onkalo	u8 irq_cfg = lis3->irq_cfg & LIS3_IRQ1_MASK;
4896d94d4081048756df78444a07201156f4930fe48Samu Onkalo
490029756d0b8856f52d83dee81c01dd3af786cadffSamu Onkalo	if (irq_cfg == LIS3_IRQ1_CLICK)
4916d94d4081048756df78444a07201156f4930fe48Samu Onkalo		lis302dl_interrupt_handle_click(lis3);
492029756d0b8856f52d83dee81c01dd3af786cadffSamu Onkalo	else if (unlikely(irq_cfg == LIS3_IRQ1_DATA_READY))
493029756d0b8856f52d83dee81c01dd3af786cadffSamu Onkalo		lis302dl_data_ready(lis3, IRQ_LINE0);
4946d94d4081048756df78444a07201156f4930fe48Samu Onkalo	else
495e726111f953f8f5b922b953caf06ba6790c5fbaaSamu Onkalo		lis3lv02d_joystick_poll(lis3->idev);
4966d94d4081048756df78444a07201156f4930fe48Samu Onkalo
49792ba4fe4b53b4fa5ac71ec4d80572348fca85796Samu Onkalo	return IRQ_HANDLED;
49892ba4fe4b53b4fa5ac71ec4d80572348fca85796Samu Onkalo}
499ef2cfc790bf5f0ff189b01eabc0f4feb5e8524dfPavel Machek
50092ba4fe4b53b4fa5ac71ec4d80572348fca85796Samu Onkalostatic irqreturn_t lis302dl_interrupt_thread2_8b(int irq, void *data)
50192ba4fe4b53b4fa5ac71ec4d80572348fca85796Samu Onkalo{
5026d94d4081048756df78444a07201156f4930fe48Samu Onkalo	struct lis3lv02d *lis3 = data;
503029756d0b8856f52d83dee81c01dd3af786cadffSamu Onkalo	u8 irq_cfg = lis3->irq_cfg & LIS3_IRQ2_MASK;
5046d94d4081048756df78444a07201156f4930fe48Samu Onkalo
505029756d0b8856f52d83dee81c01dd3af786cadffSamu Onkalo	if (irq_cfg == LIS3_IRQ2_CLICK)
5066d94d4081048756df78444a07201156f4930fe48Samu Onkalo		lis302dl_interrupt_handle_click(lis3);
507029756d0b8856f52d83dee81c01dd3af786cadffSamu Onkalo	else if (unlikely(irq_cfg == LIS3_IRQ2_DATA_READY))
508029756d0b8856f52d83dee81c01dd3af786cadffSamu Onkalo		lis302dl_data_ready(lis3, IRQ_LINE1);
5096d94d4081048756df78444a07201156f4930fe48Samu Onkalo	else
510e726111f953f8f5b922b953caf06ba6790c5fbaaSamu Onkalo		lis3lv02d_joystick_poll(lis3->idev);
5116d94d4081048756df78444a07201156f4930fe48Samu Onkalo
51292ba4fe4b53b4fa5ac71ec4d80572348fca85796Samu Onkalo	return IRQ_HANDLED;
51392ba4fe4b53b4fa5ac71ec4d80572348fca85796Samu Onkalo}
51492ba4fe4b53b4fa5ac71ec4d80572348fca85796Samu Onkalo
51592ba4fe4b53b4fa5ac71ec4d80572348fca85796Samu Onkalostatic int lis3lv02d_misc_open(struct inode *inode, struct file *file)
51692ba4fe4b53b4fa5ac71ec4d80572348fca85796Samu Onkalo{
517be84cfc588b19f14764d78556dc7b630ee8c914cPavel Machek	if (test_and_set_bit(0, &lis3_dev.misc_opened))
518ef2cfc790bf5f0ff189b01eabc0f4feb5e8524dfPavel Machek		return -EBUSY; /* already open */
519ef2cfc790bf5f0ff189b01eabc0f4feb5e8524dfPavel Machek
5202a346996626ecbb4269c239e9ff7372a182907e9Samu Onkalo	if (lis3_dev.pm_dev)
5212a346996626ecbb4269c239e9ff7372a182907e9Samu Onkalo		pm_runtime_get_sync(lis3_dev.pm_dev);
5222a346996626ecbb4269c239e9ff7372a182907e9Samu Onkalo
523be84cfc588b19f14764d78556dc7b630ee8c914cPavel Machek	atomic_set(&lis3_dev.count, 0);
524ef2cfc790bf5f0ff189b01eabc0f4feb5e8524dfPavel Machek	return 0;
525ef2cfc790bf5f0ff189b01eabc0f4feb5e8524dfPavel Machek}
526ef2cfc790bf5f0ff189b01eabc0f4feb5e8524dfPavel Machek
527ef2cfc790bf5f0ff189b01eabc0f4feb5e8524dfPavel Machekstatic int lis3lv02d_misc_release(struct inode *inode, struct file *file)
528ef2cfc790bf5f0ff189b01eabc0f4feb5e8524dfPavel Machek{
529be84cfc588b19f14764d78556dc7b630ee8c914cPavel Machek	fasync_helper(-1, file, 0, &lis3_dev.async_queue);
530be84cfc588b19f14764d78556dc7b630ee8c914cPavel Machek	clear_bit(0, &lis3_dev.misc_opened); /* release the device */
5312a346996626ecbb4269c239e9ff7372a182907e9Samu Onkalo	if (lis3_dev.pm_dev)
5322a346996626ecbb4269c239e9ff7372a182907e9Samu Onkalo		pm_runtime_put(lis3_dev.pm_dev);
533ef2cfc790bf5f0ff189b01eabc0f4feb5e8524dfPavel Machek	return 0;
534ef2cfc790bf5f0ff189b01eabc0f4feb5e8524dfPavel Machek}
535ef2cfc790bf5f0ff189b01eabc0f4feb5e8524dfPavel Machek
536ef2cfc790bf5f0ff189b01eabc0f4feb5e8524dfPavel Machekstatic ssize_t lis3lv02d_misc_read(struct file *file, char __user *buf,
537ef2cfc790bf5f0ff189b01eabc0f4feb5e8524dfPavel Machek				size_t count, loff_t *pos)
538ef2cfc790bf5f0ff189b01eabc0f4feb5e8524dfPavel Machek{
539ef2cfc790bf5f0ff189b01eabc0f4feb5e8524dfPavel Machek	DECLARE_WAITQUEUE(wait, current);
540ef2cfc790bf5f0ff189b01eabc0f4feb5e8524dfPavel Machek	u32 data;
541ef2cfc790bf5f0ff189b01eabc0f4feb5e8524dfPavel Machek	unsigned char byte_data;
542ef2cfc790bf5f0ff189b01eabc0f4feb5e8524dfPavel Machek	ssize_t retval = 1;
543ef2cfc790bf5f0ff189b01eabc0f4feb5e8524dfPavel Machek
544ef2cfc790bf5f0ff189b01eabc0f4feb5e8524dfPavel Machek	if (count < 1)
545ef2cfc790bf5f0ff189b01eabc0f4feb5e8524dfPavel Machek		return -EINVAL;
546ef2cfc790bf5f0ff189b01eabc0f4feb5e8524dfPavel Machek
547be84cfc588b19f14764d78556dc7b630ee8c914cPavel Machek	add_wait_queue(&lis3_dev.misc_wait, &wait);
548ef2cfc790bf5f0ff189b01eabc0f4feb5e8524dfPavel Machek	while (true) {
549ef2cfc790bf5f0ff189b01eabc0f4feb5e8524dfPavel Machek		set_current_state(TASK_INTERRUPTIBLE);
550be84cfc588b19f14764d78556dc7b630ee8c914cPavel Machek		data = atomic_xchg(&lis3_dev.count, 0);
551ef2cfc790bf5f0ff189b01eabc0f4feb5e8524dfPavel Machek		if (data)
552ef2cfc790bf5f0ff189b01eabc0f4feb5e8524dfPavel Machek			break;
553ef2cfc790bf5f0ff189b01eabc0f4feb5e8524dfPavel Machek
554ef2cfc790bf5f0ff189b01eabc0f4feb5e8524dfPavel Machek		if (file->f_flags & O_NONBLOCK) {
555ef2cfc790bf5f0ff189b01eabc0f4feb5e8524dfPavel Machek			retval = -EAGAIN;
556ef2cfc790bf5f0ff189b01eabc0f4feb5e8524dfPavel Machek			goto out;
557ef2cfc790bf5f0ff189b01eabc0f4feb5e8524dfPavel Machek		}
558ef2cfc790bf5f0ff189b01eabc0f4feb5e8524dfPavel Machek
559ef2cfc790bf5f0ff189b01eabc0f4feb5e8524dfPavel Machek		if (signal_pending(current)) {
560ef2cfc790bf5f0ff189b01eabc0f4feb5e8524dfPavel Machek			retval = -ERESTARTSYS;
561ef2cfc790bf5f0ff189b01eabc0f4feb5e8524dfPavel Machek			goto out;
562ef2cfc790bf5f0ff189b01eabc0f4feb5e8524dfPavel Machek		}
563ef2cfc790bf5f0ff189b01eabc0f4feb5e8524dfPavel Machek
564ef2cfc790bf5f0ff189b01eabc0f4feb5e8524dfPavel Machek		schedule();
565ef2cfc790bf5f0ff189b01eabc0f4feb5e8524dfPavel Machek	}
566ef2cfc790bf5f0ff189b01eabc0f4feb5e8524dfPavel Machek
567ef2cfc790bf5f0ff189b01eabc0f4feb5e8524dfPavel Machek	if (data < 255)
568ef2cfc790bf5f0ff189b01eabc0f4feb5e8524dfPavel Machek		byte_data = data;
569ef2cfc790bf5f0ff189b01eabc0f4feb5e8524dfPavel Machek	else
570ef2cfc790bf5f0ff189b01eabc0f4feb5e8524dfPavel Machek		byte_data = 255;
571ef2cfc790bf5f0ff189b01eabc0f4feb5e8524dfPavel Machek
572ef2cfc790bf5f0ff189b01eabc0f4feb5e8524dfPavel Machek	/* make sure we are not going into copy_to_user() with
573ef2cfc790bf5f0ff189b01eabc0f4feb5e8524dfPavel Machek	 * TASK_INTERRUPTIBLE state */
574ef2cfc790bf5f0ff189b01eabc0f4feb5e8524dfPavel Machek	set_current_state(TASK_RUNNING);
575ef2cfc790bf5f0ff189b01eabc0f4feb5e8524dfPavel Machek	if (copy_to_user(buf, &byte_data, sizeof(byte_data)))
576ef2cfc790bf5f0ff189b01eabc0f4feb5e8524dfPavel Machek		retval = -EFAULT;
577ef2cfc790bf5f0ff189b01eabc0f4feb5e8524dfPavel Machek
578ef2cfc790bf5f0ff189b01eabc0f4feb5e8524dfPavel Machekout:
579ef2cfc790bf5f0ff189b01eabc0f4feb5e8524dfPavel Machek	__set_current_state(TASK_RUNNING);
580be84cfc588b19f14764d78556dc7b630ee8c914cPavel Machek	remove_wait_queue(&lis3_dev.misc_wait, &wait);
581ef2cfc790bf5f0ff189b01eabc0f4feb5e8524dfPavel Machek
582ef2cfc790bf5f0ff189b01eabc0f4feb5e8524dfPavel Machek	return retval;
583ef2cfc790bf5f0ff189b01eabc0f4feb5e8524dfPavel Machek}
584ef2cfc790bf5f0ff189b01eabc0f4feb5e8524dfPavel Machek
585ef2cfc790bf5f0ff189b01eabc0f4feb5e8524dfPavel Machekstatic unsigned int lis3lv02d_misc_poll(struct file *file, poll_table *wait)
586ef2cfc790bf5f0ff189b01eabc0f4feb5e8524dfPavel Machek{
587be84cfc588b19f14764d78556dc7b630ee8c914cPavel Machek	poll_wait(file, &lis3_dev.misc_wait, wait);
588be84cfc588b19f14764d78556dc7b630ee8c914cPavel Machek	if (atomic_read(&lis3_dev.count))
589ef2cfc790bf5f0ff189b01eabc0f4feb5e8524dfPavel Machek		return POLLIN | POLLRDNORM;
590ef2cfc790bf5f0ff189b01eabc0f4feb5e8524dfPavel Machek	return 0;
591ef2cfc790bf5f0ff189b01eabc0f4feb5e8524dfPavel Machek}
592ef2cfc790bf5f0ff189b01eabc0f4feb5e8524dfPavel Machek
593ef2cfc790bf5f0ff189b01eabc0f4feb5e8524dfPavel Machekstatic int lis3lv02d_misc_fasync(int fd, struct file *file, int on)
594ef2cfc790bf5f0ff189b01eabc0f4feb5e8524dfPavel Machek{
595be84cfc588b19f14764d78556dc7b630ee8c914cPavel Machek	return fasync_helper(fd, file, on, &lis3_dev.async_queue);
596ef2cfc790bf5f0ff189b01eabc0f4feb5e8524dfPavel Machek}
597ef2cfc790bf5f0ff189b01eabc0f4feb5e8524dfPavel Machek
598ef2cfc790bf5f0ff189b01eabc0f4feb5e8524dfPavel Machekstatic const struct file_operations lis3lv02d_misc_fops = {
599ef2cfc790bf5f0ff189b01eabc0f4feb5e8524dfPavel Machek	.owner   = THIS_MODULE,
600ef2cfc790bf5f0ff189b01eabc0f4feb5e8524dfPavel Machek	.llseek  = no_llseek,
601ef2cfc790bf5f0ff189b01eabc0f4feb5e8524dfPavel Machek	.read    = lis3lv02d_misc_read,
602ef2cfc790bf5f0ff189b01eabc0f4feb5e8524dfPavel Machek	.open    = lis3lv02d_misc_open,
603ef2cfc790bf5f0ff189b01eabc0f4feb5e8524dfPavel Machek	.release = lis3lv02d_misc_release,
604ef2cfc790bf5f0ff189b01eabc0f4feb5e8524dfPavel Machek	.poll    = lis3lv02d_misc_poll,
605ef2cfc790bf5f0ff189b01eabc0f4feb5e8524dfPavel Machek	.fasync  = lis3lv02d_misc_fasync,
606ef2cfc790bf5f0ff189b01eabc0f4feb5e8524dfPavel Machek};
607ef2cfc790bf5f0ff189b01eabc0f4feb5e8524dfPavel Machek
608ef2cfc790bf5f0ff189b01eabc0f4feb5e8524dfPavel Machekstatic struct miscdevice lis3lv02d_misc_device = {
609ef2cfc790bf5f0ff189b01eabc0f4feb5e8524dfPavel Machek	.minor   = MISC_DYNAMIC_MINOR,
610ef2cfc790bf5f0ff189b01eabc0f4feb5e8524dfPavel Machek	.name    = "freefall",
611ef2cfc790bf5f0ff189b01eabc0f4feb5e8524dfPavel Machek	.fops    = &lis3lv02d_misc_fops,
612ef2cfc790bf5f0ff189b01eabc0f4feb5e8524dfPavel Machek};
613ef2cfc790bf5f0ff189b01eabc0f4feb5e8524dfPavel Machek
614cfce41a6d643c001d416ead960caf04fae2d609aEric Pielint lis3lv02d_joystick_enable(void)
615455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek{
616dc6ea97bac6b8228c7a69740df35eed2be3407beEric Piel	struct input_dev *input_dev;
617455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek	int err;
61832496c76b777752ba84b125bebfb0cc498f5602cSamu Onkalo	int max_val, fuzz, flat;
6196d94d4081048756df78444a07201156f4930fe48Samu Onkalo	int btns[] = {BTN_X, BTN_Y, BTN_Z};
620455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek
621be84cfc588b19f14764d78556dc7b630ee8c914cPavel Machek	if (lis3_dev.idev)
622455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek		return -EINVAL;
623455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek
624dc6ea97bac6b8228c7a69740df35eed2be3407beEric Piel	lis3_dev.idev = input_allocate_polled_device();
625be84cfc588b19f14764d78556dc7b630ee8c914cPavel Machek	if (!lis3_dev.idev)
626455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek		return -ENOMEM;
627455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek
628dc6ea97bac6b8228c7a69740df35eed2be3407beEric Piel	lis3_dev.idev->poll = lis3lv02d_joystick_poll;
6292a346996626ecbb4269c239e9ff7372a182907e9Samu Onkalo	lis3_dev.idev->open = lis3lv02d_joystick_open;
6302a346996626ecbb4269c239e9ff7372a182907e9Samu Onkalo	lis3_dev.idev->close = lis3lv02d_joystick_close;
631dc6ea97bac6b8228c7a69740df35eed2be3407beEric Piel	lis3_dev.idev->poll_interval = MDPS_POLL_INTERVAL;
6324a70a413ccfd1c14ef29a290a4d2dada04ccbefbSamu Onkalo	lis3_dev.idev->poll_interval_min = MDPS_POLL_MIN;
6334a70a413ccfd1c14ef29a290a4d2dada04ccbefbSamu Onkalo	lis3_dev.idev->poll_interval_max = MDPS_POLL_MAX;
634dc6ea97bac6b8228c7a69740df35eed2be3407beEric Piel	input_dev = lis3_dev.idev->input;
635dc6ea97bac6b8228c7a69740df35eed2be3407beEric Piel
636dc6ea97bac6b8228c7a69740df35eed2be3407beEric Piel	input_dev->name       = "ST LIS3LV02DL Accelerometer";
637dc6ea97bac6b8228c7a69740df35eed2be3407beEric Piel	input_dev->phys       = DRIVER_NAME "/input0";
638dc6ea97bac6b8228c7a69740df35eed2be3407beEric Piel	input_dev->id.bustype = BUS_HOST;
639dc6ea97bac6b8228c7a69740df35eed2be3407beEric Piel	input_dev->id.vendor  = 0;
640dc6ea97bac6b8228c7a69740df35eed2be3407beEric Piel	input_dev->dev.parent = &lis3_dev.pdev->dev;
641455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek
642dc6ea97bac6b8228c7a69740df35eed2be3407beEric Piel	set_bit(EV_ABS, input_dev->evbit);
64332496c76b777752ba84b125bebfb0cc498f5602cSamu Onkalo	max_val = (lis3_dev.mdps_max_val * lis3_dev.scale) / LIS3_ACCURACY;
644477bc918c2323a51f577cd892ca49376f6feb5d5Samu Onkalo	if (lis3_dev.whoami == WAI_12B) {
645477bc918c2323a51f577cd892ca49376f6feb5d5Samu Onkalo		fuzz = LIS3_DEFAULT_FUZZ_12B;
646477bc918c2323a51f577cd892ca49376f6feb5d5Samu Onkalo		flat = LIS3_DEFAULT_FLAT_12B;
647477bc918c2323a51f577cd892ca49376f6feb5d5Samu Onkalo	} else {
648477bc918c2323a51f577cd892ca49376f6feb5d5Samu Onkalo		fuzz = LIS3_DEFAULT_FUZZ_8B;
649477bc918c2323a51f577cd892ca49376f6feb5d5Samu Onkalo		flat = LIS3_DEFAULT_FLAT_8B;
650477bc918c2323a51f577cd892ca49376f6feb5d5Samu Onkalo	}
651477bc918c2323a51f577cd892ca49376f6feb5d5Samu Onkalo	fuzz = (fuzz * lis3_dev.scale) / LIS3_ACCURACY;
652477bc918c2323a51f577cd892ca49376f6feb5d5Samu Onkalo	flat = (flat * lis3_dev.scale) / LIS3_ACCURACY;
653477bc918c2323a51f577cd892ca49376f6feb5d5Samu Onkalo
65432496c76b777752ba84b125bebfb0cc498f5602cSamu Onkalo	input_set_abs_params(input_dev, ABS_X, -max_val, max_val, fuzz, flat);
65532496c76b777752ba84b125bebfb0cc498f5602cSamu Onkalo	input_set_abs_params(input_dev, ABS_Y, -max_val, max_val, fuzz, flat);
65632496c76b777752ba84b125bebfb0cc498f5602cSamu Onkalo	input_set_abs_params(input_dev, ABS_Z, -max_val, max_val, fuzz, flat);
657455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek
6586d94d4081048756df78444a07201156f4930fe48Samu Onkalo	lis3_dev.mapped_btns[0] = lis3lv02d_get_axis(abs(lis3_dev.ac.x), btns);
6596d94d4081048756df78444a07201156f4930fe48Samu Onkalo	lis3_dev.mapped_btns[1] = lis3lv02d_get_axis(abs(lis3_dev.ac.y), btns);
6606d94d4081048756df78444a07201156f4930fe48Samu Onkalo	lis3_dev.mapped_btns[2] = lis3lv02d_get_axis(abs(lis3_dev.ac.z), btns);
6616d94d4081048756df78444a07201156f4930fe48Samu Onkalo
662dc6ea97bac6b8228c7a69740df35eed2be3407beEric Piel	err = input_register_polled_device(lis3_dev.idev);
663455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek	if (err) {
664dc6ea97bac6b8228c7a69740df35eed2be3407beEric Piel		input_free_polled_device(lis3_dev.idev);
665be84cfc588b19f14764d78556dc7b630ee8c914cPavel Machek		lis3_dev.idev = NULL;
666455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek	}
667455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek
668455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek	return err;
669455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek}
670cfce41a6d643c001d416ead960caf04fae2d609aEric PielEXPORT_SYMBOL_GPL(lis3lv02d_joystick_enable);
671455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek
672cfce41a6d643c001d416ead960caf04fae2d609aEric Pielvoid lis3lv02d_joystick_disable(void)
673455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek{
67492ba4fe4b53b4fa5ac71ec4d80572348fca85796Samu Onkalo	if (lis3_dev.irq)
67592ba4fe4b53b4fa5ac71ec4d80572348fca85796Samu Onkalo		free_irq(lis3_dev.irq, &lis3_dev);
67692ba4fe4b53b4fa5ac71ec4d80572348fca85796Samu Onkalo	if (lis3_dev.pdata && lis3_dev.pdata->irq2)
67792ba4fe4b53b4fa5ac71ec4d80572348fca85796Samu Onkalo		free_irq(lis3_dev.pdata->irq2, &lis3_dev);
67892ba4fe4b53b4fa5ac71ec4d80572348fca85796Samu Onkalo
679be84cfc588b19f14764d78556dc7b630ee8c914cPavel Machek	if (!lis3_dev.idev)
680455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek		return;
681455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek
682c28842421cb6a29fd952043381bc5391bdf6be50Eric Piel	if (lis3_dev.irq)
683c28842421cb6a29fd952043381bc5391bdf6be50Eric Piel		misc_deregister(&lis3lv02d_misc_device);
684dc6ea97bac6b8228c7a69740df35eed2be3407beEric Piel	input_unregister_polled_device(lis3_dev.idev);
68566c8569bf990064b3f11e0f211a81a46e0b627ffSamu Onkalo	input_free_polled_device(lis3_dev.idev);
686be84cfc588b19f14764d78556dc7b630ee8c914cPavel Machek	lis3_dev.idev = NULL;
687455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek}
688cfce41a6d643c001d416ead960caf04fae2d609aEric PielEXPORT_SYMBOL_GPL(lis3lv02d_joystick_disable);
689455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek
690455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek/* Sysfs stuff */
6912a346996626ecbb4269c239e9ff7372a182907e9Samu Onkalostatic void lis3lv02d_sysfs_poweron(struct lis3lv02d *lis3)
6922a346996626ecbb4269c239e9ff7372a182907e9Samu Onkalo{
6932a346996626ecbb4269c239e9ff7372a182907e9Samu Onkalo	/*
6942a346996626ecbb4269c239e9ff7372a182907e9Samu Onkalo	 * SYSFS functions are fast visitors so put-call
6952a346996626ecbb4269c239e9ff7372a182907e9Samu Onkalo	 * immediately after the get-call. However, keep
6962a346996626ecbb4269c239e9ff7372a182907e9Samu Onkalo	 * chip running for a while and schedule delayed
6972a346996626ecbb4269c239e9ff7372a182907e9Samu Onkalo	 * suspend. This way periodic sysfs calls doesn't
6982a346996626ecbb4269c239e9ff7372a182907e9Samu Onkalo	 * suffer from relatively long power up time.
6992a346996626ecbb4269c239e9ff7372a182907e9Samu Onkalo	 */
7002a346996626ecbb4269c239e9ff7372a182907e9Samu Onkalo
7012a346996626ecbb4269c239e9ff7372a182907e9Samu Onkalo	if (lis3->pm_dev) {
7022a346996626ecbb4269c239e9ff7372a182907e9Samu Onkalo		pm_runtime_get_sync(lis3->pm_dev);
7032a346996626ecbb4269c239e9ff7372a182907e9Samu Onkalo		pm_runtime_put_noidle(lis3->pm_dev);
7042a346996626ecbb4269c239e9ff7372a182907e9Samu Onkalo		pm_schedule_suspend(lis3->pm_dev, LIS3_SYSFS_POWERDOWN_DELAY);
7052a346996626ecbb4269c239e9ff7372a182907e9Samu Onkalo	}
7062a346996626ecbb4269c239e9ff7372a182907e9Samu Onkalo}
7072a346996626ecbb4269c239e9ff7372a182907e9Samu Onkalo
7082db4a76d5f3554e9e5632c8f91828313318579c8Samu Onkalostatic ssize_t lis3lv02d_selftest_show(struct device *dev,
7092db4a76d5f3554e9e5632c8f91828313318579c8Samu Onkalo				struct device_attribute *attr, char *buf)
7102db4a76d5f3554e9e5632c8f91828313318579c8Samu Onkalo{
7112db4a76d5f3554e9e5632c8f91828313318579c8Samu Onkalo	s16 values[3];
7122db4a76d5f3554e9e5632c8f91828313318579c8Samu Onkalo
713029756d0b8856f52d83dee81c01dd3af786cadffSamu Onkalo	static const char ok[] = "OK";
714029756d0b8856f52d83dee81c01dd3af786cadffSamu Onkalo	static const char fail[] = "FAIL";
715029756d0b8856f52d83dee81c01dd3af786cadffSamu Onkalo	static const char irq[] = "FAIL_IRQ";
716029756d0b8856f52d83dee81c01dd3af786cadffSamu Onkalo	const char *res;
717029756d0b8856f52d83dee81c01dd3af786cadffSamu Onkalo
7182a346996626ecbb4269c239e9ff7372a182907e9Samu Onkalo	lis3lv02d_sysfs_poweron(&lis3_dev);
719029756d0b8856f52d83dee81c01dd3af786cadffSamu Onkalo	switch (lis3lv02d_selftest(&lis3_dev, values)) {
720029756d0b8856f52d83dee81c01dd3af786cadffSamu Onkalo	case SELFTEST_FAIL:
721029756d0b8856f52d83dee81c01dd3af786cadffSamu Onkalo		res = fail;
722029756d0b8856f52d83dee81c01dd3af786cadffSamu Onkalo		break;
723029756d0b8856f52d83dee81c01dd3af786cadffSamu Onkalo	case SELFTEST_IRQ:
724029756d0b8856f52d83dee81c01dd3af786cadffSamu Onkalo		res = irq;
725029756d0b8856f52d83dee81c01dd3af786cadffSamu Onkalo		break;
726029756d0b8856f52d83dee81c01dd3af786cadffSamu Onkalo	case SELFTEST_OK:
727029756d0b8856f52d83dee81c01dd3af786cadffSamu Onkalo	default:
728029756d0b8856f52d83dee81c01dd3af786cadffSamu Onkalo		res = ok;
729029756d0b8856f52d83dee81c01dd3af786cadffSamu Onkalo		break;
730029756d0b8856f52d83dee81c01dd3af786cadffSamu Onkalo	}
731029756d0b8856f52d83dee81c01dd3af786cadffSamu Onkalo	return sprintf(buf, "%s %d %d %d\n", res,
7322db4a76d5f3554e9e5632c8f91828313318579c8Samu Onkalo		values[0], values[1], values[2]);
7332db4a76d5f3554e9e5632c8f91828313318579c8Samu Onkalo}
7342db4a76d5f3554e9e5632c8f91828313318579c8Samu Onkalo
735455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machekstatic ssize_t lis3lv02d_position_show(struct device *dev,
736455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek				struct device_attribute *attr, char *buf)
737455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek{
738455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek	int x, y, z;
739455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek
7402a346996626ecbb4269c239e9ff7372a182907e9Samu Onkalo	lis3lv02d_sysfs_poweron(&lis3_dev);
7416d94d4081048756df78444a07201156f4930fe48Samu Onkalo	mutex_lock(&lis3_dev.mutex);
742a38da2ed74f628c1f3e907c772be21a66eccab9cDaniel Mack	lis3lv02d_get_xyz(&lis3_dev, &x, &y, &z);
7436d94d4081048756df78444a07201156f4930fe48Samu Onkalo	mutex_unlock(&lis3_dev.mutex);
744455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek	return sprintf(buf, "(%d,%d,%d)\n", x, y, z);
745455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek}
746455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek
747455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machekstatic ssize_t lis3lv02d_rate_show(struct device *dev,
748455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek			struct device_attribute *attr, char *buf)
749455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek{
7502a346996626ecbb4269c239e9ff7372a182907e9Samu Onkalo	lis3lv02d_sysfs_poweron(&lis3_dev);
751641615abfac0b7c5e6f242a6db77f7690925b443Samu Onkalo	return sprintf(buf, "%d\n", lis3lv02d_get_odr());
752455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek}
753455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek
754a253aaef60a37bddfa84846353edeb62a6acf5b3Samu Onkalostatic ssize_t lis3lv02d_rate_set(struct device *dev,
755a253aaef60a37bddfa84846353edeb62a6acf5b3Samu Onkalo				struct device_attribute *attr, const char *buf,
756a253aaef60a37bddfa84846353edeb62a6acf5b3Samu Onkalo				size_t count)
757a253aaef60a37bddfa84846353edeb62a6acf5b3Samu Onkalo{
758a253aaef60a37bddfa84846353edeb62a6acf5b3Samu Onkalo	unsigned long rate;
759a253aaef60a37bddfa84846353edeb62a6acf5b3Samu Onkalo
760a253aaef60a37bddfa84846353edeb62a6acf5b3Samu Onkalo	if (strict_strtoul(buf, 0, &rate))
761a253aaef60a37bddfa84846353edeb62a6acf5b3Samu Onkalo		return -EINVAL;
762a253aaef60a37bddfa84846353edeb62a6acf5b3Samu Onkalo
7632a346996626ecbb4269c239e9ff7372a182907e9Samu Onkalo	lis3lv02d_sysfs_poweron(&lis3_dev);
764a253aaef60a37bddfa84846353edeb62a6acf5b3Samu Onkalo	if (lis3lv02d_set_odr(rate))
765a253aaef60a37bddfa84846353edeb62a6acf5b3Samu Onkalo		return -EINVAL;
766a253aaef60a37bddfa84846353edeb62a6acf5b3Samu Onkalo
767a253aaef60a37bddfa84846353edeb62a6acf5b3Samu Onkalo	return count;
768a253aaef60a37bddfa84846353edeb62a6acf5b3Samu Onkalo}
769a253aaef60a37bddfa84846353edeb62a6acf5b3Samu Onkalo
7702db4a76d5f3554e9e5632c8f91828313318579c8Samu Onkalostatic DEVICE_ATTR(selftest, S_IRUSR, lis3lv02d_selftest_show, NULL);
771455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machekstatic DEVICE_ATTR(position, S_IRUGO, lis3lv02d_position_show, NULL);
772a253aaef60a37bddfa84846353edeb62a6acf5b3Samu Onkalostatic DEVICE_ATTR(rate, S_IRUGO | S_IWUSR, lis3lv02d_rate_show,
773a253aaef60a37bddfa84846353edeb62a6acf5b3Samu Onkalo					    lis3lv02d_rate_set);
774455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek
775455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machekstatic struct attribute *lis3lv02d_attributes[] = {
7762db4a76d5f3554e9e5632c8f91828313318579c8Samu Onkalo	&dev_attr_selftest.attr,
777455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek	&dev_attr_position.attr,
778455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek	&dev_attr_rate.attr,
779455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek	NULL
780455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek};
781455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek
782455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machekstatic struct attribute_group lis3lv02d_attribute_group = {
783455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek	.attrs = lis3lv02d_attributes
784455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek};
785455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek
786cfce41a6d643c001d416ead960caf04fae2d609aEric Piel
787a38da2ed74f628c1f3e907c772be21a66eccab9cDaniel Mackstatic int lis3lv02d_add_fs(struct lis3lv02d *lis3)
788455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek{
789a002ee896dfd08ce9fba44e9ae513c9094699a27Eric Piel	lis3->pdev = platform_device_register_simple(DRIVER_NAME, -1, NULL, 0);
790a002ee896dfd08ce9fba44e9ae513c9094699a27Eric Piel	if (IS_ERR(lis3->pdev))
791a002ee896dfd08ce9fba44e9ae513c9094699a27Eric Piel		return PTR_ERR(lis3->pdev);
792455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek
793a002ee896dfd08ce9fba44e9ae513c9094699a27Eric Piel	return sysfs_create_group(&lis3->pdev->dev.kobj, &lis3lv02d_attribute_group);
794455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek}
795455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek
796a002ee896dfd08ce9fba44e9ae513c9094699a27Eric Pielint lis3lv02d_remove_fs(struct lis3lv02d *lis3)
797455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek{
798a002ee896dfd08ce9fba44e9ae513c9094699a27Eric Piel	sysfs_remove_group(&lis3->pdev->dev.kobj, &lis3lv02d_attribute_group);
799a002ee896dfd08ce9fba44e9ae513c9094699a27Eric Piel	platform_device_unregister(lis3->pdev);
8002a346996626ecbb4269c239e9ff7372a182907e9Samu Onkalo	if (lis3->pm_dev) {
8012a346996626ecbb4269c239e9ff7372a182907e9Samu Onkalo		/* Barrier after the sysfs remove */
8022a346996626ecbb4269c239e9ff7372a182907e9Samu Onkalo		pm_runtime_barrier(lis3->pm_dev);
8032a346996626ecbb4269c239e9ff7372a182907e9Samu Onkalo
8042a346996626ecbb4269c239e9ff7372a182907e9Samu Onkalo		/* SYSFS may have left chip running. Turn off if necessary */
8052a346996626ecbb4269c239e9ff7372a182907e9Samu Onkalo		if (!pm_runtime_suspended(lis3->pm_dev))
8062a346996626ecbb4269c239e9ff7372a182907e9Samu Onkalo			lis3lv02d_poweroff(&lis3_dev);
8072a346996626ecbb4269c239e9ff7372a182907e9Samu Onkalo
8082a346996626ecbb4269c239e9ff7372a182907e9Samu Onkalo		pm_runtime_disable(lis3->pm_dev);
8092a346996626ecbb4269c239e9ff7372a182907e9Samu Onkalo		pm_runtime_set_suspended(lis3->pm_dev);
8102a346996626ecbb4269c239e9ff7372a182907e9Samu Onkalo	}
811f9deb41f91c41d9d91a24c84a555ec7fe82620daSamu Onkalo	kfree(lis3->reg_cache);
812455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek	return 0;
813455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek}
814cfce41a6d643c001d416ead960caf04fae2d609aEric PielEXPORT_SYMBOL_GPL(lis3lv02d_remove_fs);
815455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek
816ecc437aeee65afeea2e1bed963ccf6c384c555eaSamu Onkalostatic void lis3lv02d_8b_configure(struct lis3lv02d *dev,
817ecc437aeee65afeea2e1bed963ccf6c384c555eaSamu Onkalo				struct lis3lv02d_platform_data *p)
818ecc437aeee65afeea2e1bed963ccf6c384c555eaSamu Onkalo{
81992ba4fe4b53b4fa5ac71ec4d80572348fca85796Samu Onkalo	int err;
820342c5f128140d54961c435d1702eadcaba97a37aSamu Onkalo	int ctrl2 = p->hipass_ctrl;
821342c5f128140d54961c435d1702eadcaba97a37aSamu Onkalo
822ecc437aeee65afeea2e1bed963ccf6c384c555eaSamu Onkalo	if (p->click_flags) {
823ecc437aeee65afeea2e1bed963ccf6c384c555eaSamu Onkalo		dev->write(dev, CLICK_CFG, p->click_flags);
824ecc437aeee65afeea2e1bed963ccf6c384c555eaSamu Onkalo		dev->write(dev, CLICK_TIMELIMIT, p->click_time_limit);
825ecc437aeee65afeea2e1bed963ccf6c384c555eaSamu Onkalo		dev->write(dev, CLICK_LATENCY, p->click_latency);
826ecc437aeee65afeea2e1bed963ccf6c384c555eaSamu Onkalo		dev->write(dev, CLICK_WINDOW, p->click_window);
827ecc437aeee65afeea2e1bed963ccf6c384c555eaSamu Onkalo		dev->write(dev, CLICK_THSZ, p->click_thresh_z & 0xf);
828ecc437aeee65afeea2e1bed963ccf6c384c555eaSamu Onkalo		dev->write(dev, CLICK_THSY_X,
829ecc437aeee65afeea2e1bed963ccf6c384c555eaSamu Onkalo			(p->click_thresh_x & 0xf) |
830ecc437aeee65afeea2e1bed963ccf6c384c555eaSamu Onkalo			(p->click_thresh_y << 4));
8316d94d4081048756df78444a07201156f4930fe48Samu Onkalo
8326d94d4081048756df78444a07201156f4930fe48Samu Onkalo		if (dev->idev) {
8336d94d4081048756df78444a07201156f4930fe48Samu Onkalo			struct input_dev *input_dev = lis3_dev.idev->input;
8346d94d4081048756df78444a07201156f4930fe48Samu Onkalo			input_set_capability(input_dev, EV_KEY, BTN_X);
8356d94d4081048756df78444a07201156f4930fe48Samu Onkalo			input_set_capability(input_dev, EV_KEY, BTN_Y);
8366d94d4081048756df78444a07201156f4930fe48Samu Onkalo			input_set_capability(input_dev, EV_KEY, BTN_Z);
8376d94d4081048756df78444a07201156f4930fe48Samu Onkalo		}
838ecc437aeee65afeea2e1bed963ccf6c384c555eaSamu Onkalo	}
839ecc437aeee65afeea2e1bed963ccf6c384c555eaSamu Onkalo
840ecc437aeee65afeea2e1bed963ccf6c384c555eaSamu Onkalo	if (p->wakeup_flags) {
841ecc437aeee65afeea2e1bed963ccf6c384c555eaSamu Onkalo		dev->write(dev, FF_WU_CFG_1, p->wakeup_flags);
842ecc437aeee65afeea2e1bed963ccf6c384c555eaSamu Onkalo		dev->write(dev, FF_WU_THS_1, p->wakeup_thresh & 0x7f);
843cc23aa1ce2631b2fe1e3fba82ee444460f5ee3b7Samu Onkalo		/* pdata value + 1 to keep this backward compatible*/
844cc23aa1ce2631b2fe1e3fba82ee444460f5ee3b7Samu Onkalo		dev->write(dev, FF_WU_DURATION_1, p->duration1 + 1);
845342c5f128140d54961c435d1702eadcaba97a37aSamu Onkalo		ctrl2 ^= HP_FF_WU1; /* Xor to keep compatible with old pdata*/
846342c5f128140d54961c435d1702eadcaba97a37aSamu Onkalo	}
847342c5f128140d54961c435d1702eadcaba97a37aSamu Onkalo
848342c5f128140d54961c435d1702eadcaba97a37aSamu Onkalo	if (p->wakeup_flags2) {
849342c5f128140d54961c435d1702eadcaba97a37aSamu Onkalo		dev->write(dev, FF_WU_CFG_2, p->wakeup_flags2);
850342c5f128140d54961c435d1702eadcaba97a37aSamu Onkalo		dev->write(dev, FF_WU_THS_2, p->wakeup_thresh2 & 0x7f);
851cc23aa1ce2631b2fe1e3fba82ee444460f5ee3b7Samu Onkalo		/* pdata value + 1 to keep this backward compatible*/
852cc23aa1ce2631b2fe1e3fba82ee444460f5ee3b7Samu Onkalo		dev->write(dev, FF_WU_DURATION_2, p->duration2 + 1);
853342c5f128140d54961c435d1702eadcaba97a37aSamu Onkalo		ctrl2 ^= HP_FF_WU2; /* Xor to keep compatible with old pdata*/
854ecc437aeee65afeea2e1bed963ccf6c384c555eaSamu Onkalo	}
855342c5f128140d54961c435d1702eadcaba97a37aSamu Onkalo	/* Configure hipass filters */
856342c5f128140d54961c435d1702eadcaba97a37aSamu Onkalo	dev->write(dev, CTRL_REG2, ctrl2);
85792ba4fe4b53b4fa5ac71ec4d80572348fca85796Samu Onkalo
85892ba4fe4b53b4fa5ac71ec4d80572348fca85796Samu Onkalo	if (p->irq2) {
85992ba4fe4b53b4fa5ac71ec4d80572348fca85796Samu Onkalo		err = request_threaded_irq(p->irq2,
86092ba4fe4b53b4fa5ac71ec4d80572348fca85796Samu Onkalo					NULL,
86192ba4fe4b53b4fa5ac71ec4d80572348fca85796Samu Onkalo					lis302dl_interrupt_thread2_8b,
862cc23aa1ce2631b2fe1e3fba82ee444460f5ee3b7Samu Onkalo					IRQF_TRIGGER_RISING | IRQF_ONESHOT |
863cc23aa1ce2631b2fe1e3fba82ee444460f5ee3b7Samu Onkalo					(p->irq_flags2 & IRQF_TRIGGER_MASK),
86492ba4fe4b53b4fa5ac71ec4d80572348fca85796Samu Onkalo					DRIVER_NAME, &lis3_dev);
86592ba4fe4b53b4fa5ac71ec4d80572348fca85796Samu Onkalo		if (err < 0)
86663366d37ad5dbb4f208b517c88ea4bd41738dbf7Joe Perches			pr_err("No second IRQ. Limited functionality\n");
86792ba4fe4b53b4fa5ac71ec4d80572348fca85796Samu Onkalo	}
868ecc437aeee65afeea2e1bed963ccf6c384c555eaSamu Onkalo}
869ecc437aeee65afeea2e1bed963ccf6c384c555eaSamu Onkalo
870ab337a632783c251a3c3852aec0ead8a0281cbddDaniel Mack/*
871ab337a632783c251a3c3852aec0ead8a0281cbddDaniel Mack * Initialise the accelerometer and the various subsystems.
872bc62c1471773fc32adcfc05100abd16fa2b6e126Éric Piel * Should be rather independent of the bus system.
873ab337a632783c251a3c3852aec0ead8a0281cbddDaniel Mack */
874a38da2ed74f628c1f3e907c772be21a66eccab9cDaniel Mackint lis3lv02d_init_device(struct lis3lv02d *dev)
875ab337a632783c251a3c3852aec0ead8a0281cbddDaniel Mack{
87692ba4fe4b53b4fa5ac71ec4d80572348fca85796Samu Onkalo	int err;
87792ba4fe4b53b4fa5ac71ec4d80572348fca85796Samu Onkalo	irq_handler_t thread_fn;
878cc23aa1ce2631b2fe1e3fba82ee444460f5ee3b7Samu Onkalo	int irq_flags = 0;
87992ba4fe4b53b4fa5ac71ec4d80572348fca85796Samu Onkalo
880a38da2ed74f628c1f3e907c772be21a66eccab9cDaniel Mack	dev->whoami = lis3lv02d_read_8(dev, WHO_AM_I);
881a38da2ed74f628c1f3e907c772be21a66eccab9cDaniel Mack
882a38da2ed74f628c1f3e907c772be21a66eccab9cDaniel Mack	switch (dev->whoami) {
883bc62c1471773fc32adcfc05100abd16fa2b6e126Éric Piel	case WAI_12B:
88463366d37ad5dbb4f208b517c88ea4bd41738dbf7Joe Perches		pr_info("12 bits sensor found\n");
885bc62c1471773fc32adcfc05100abd16fa2b6e126Éric Piel		dev->read_data = lis3lv02d_read_12;
886a38da2ed74f628c1f3e907c772be21a66eccab9cDaniel Mack		dev->mdps_max_val = 2048;
887641615abfac0b7c5e6f242a6db77f7690925b443Samu Onkalo		dev->pwron_delay = LIS3_PWRON_DELAY_WAI_12B;
888a253aaef60a37bddfa84846353edeb62a6acf5b3Samu Onkalo		dev->odrs = lis3_12_rates;
889a253aaef60a37bddfa84846353edeb62a6acf5b3Samu Onkalo		dev->odr_mask = CTRL1_DF0 | CTRL1_DF1;
89032496c76b777752ba84b125bebfb0cc498f5602cSamu Onkalo		dev->scale = LIS3_SENSITIVITY_12B;
891f9deb41f91c41d9d91a24c84a555ec7fe82620daSamu Onkalo		dev->regs = lis3_wai12_regs;
892f9deb41f91c41d9d91a24c84a555ec7fe82620daSamu Onkalo		dev->regs_size = ARRAY_SIZE(lis3_wai12_regs);
893a38da2ed74f628c1f3e907c772be21a66eccab9cDaniel Mack		break;
894bc62c1471773fc32adcfc05100abd16fa2b6e126Éric Piel	case WAI_8B:
89563366d37ad5dbb4f208b517c88ea4bd41738dbf7Joe Perches		pr_info("8 bits sensor found\n");
896a38da2ed74f628c1f3e907c772be21a66eccab9cDaniel Mack		dev->read_data = lis3lv02d_read_8;
897a38da2ed74f628c1f3e907c772be21a66eccab9cDaniel Mack		dev->mdps_max_val = 128;
898641615abfac0b7c5e6f242a6db77f7690925b443Samu Onkalo		dev->pwron_delay = LIS3_PWRON_DELAY_WAI_8B;
899a253aaef60a37bddfa84846353edeb62a6acf5b3Samu Onkalo		dev->odrs = lis3_8_rates;
900a253aaef60a37bddfa84846353edeb62a6acf5b3Samu Onkalo		dev->odr_mask = CTRL1_DR;
90132496c76b777752ba84b125bebfb0cc498f5602cSamu Onkalo		dev->scale = LIS3_SENSITIVITY_8B;
902f9deb41f91c41d9d91a24c84a555ec7fe82620daSamu Onkalo		dev->regs = lis3_wai8_regs;
903f9deb41f91c41d9d91a24c84a555ec7fe82620daSamu Onkalo		dev->regs_size = ARRAY_SIZE(lis3_wai8_regs);
904a38da2ed74f628c1f3e907c772be21a66eccab9cDaniel Mack		break;
90578537c3b6ffcb69bf4fd43a74ba57928fcefce95Takashi Iwai	case WAI_3DC:
90663366d37ad5dbb4f208b517c88ea4bd41738dbf7Joe Perches		pr_info("8 bits 3DC sensor found\n");
90778537c3b6ffcb69bf4fd43a74ba57928fcefce95Takashi Iwai		dev->read_data = lis3lv02d_read_8;
90878537c3b6ffcb69bf4fd43a74ba57928fcefce95Takashi Iwai		dev->mdps_max_val = 128;
90978537c3b6ffcb69bf4fd43a74ba57928fcefce95Takashi Iwai		dev->pwron_delay = LIS3_PWRON_DELAY_WAI_8B;
91078537c3b6ffcb69bf4fd43a74ba57928fcefce95Takashi Iwai		dev->odrs = lis3_3dc_rates;
91178537c3b6ffcb69bf4fd43a74ba57928fcefce95Takashi Iwai		dev->odr_mask = CTRL1_ODR0|CTRL1_ODR1|CTRL1_ODR2|CTRL1_ODR3;
91278537c3b6ffcb69bf4fd43a74ba57928fcefce95Takashi Iwai		dev->scale = LIS3_SENSITIVITY_8B;
91378537c3b6ffcb69bf4fd43a74ba57928fcefce95Takashi Iwai		break;
914a38da2ed74f628c1f3e907c772be21a66eccab9cDaniel Mack	default:
91563366d37ad5dbb4f208b517c88ea4bd41738dbf7Joe Perches		pr_err("unknown sensor type 0x%X\n", dev->whoami);
916a38da2ed74f628c1f3e907c772be21a66eccab9cDaniel Mack		return -EINVAL;
917a38da2ed74f628c1f3e907c772be21a66eccab9cDaniel Mack	}
918a38da2ed74f628c1f3e907c772be21a66eccab9cDaniel Mack
919f9deb41f91c41d9d91a24c84a555ec7fe82620daSamu Onkalo	dev->reg_cache = kzalloc(max(sizeof(lis3_wai8_regs),
920f9deb41f91c41d9d91a24c84a555ec7fe82620daSamu Onkalo				     sizeof(lis3_wai12_regs)), GFP_KERNEL);
921f9deb41f91c41d9d91a24c84a555ec7fe82620daSamu Onkalo
922f9deb41f91c41d9d91a24c84a555ec7fe82620daSamu Onkalo	if (dev->reg_cache == NULL) {
923f9deb41f91c41d9d91a24c84a555ec7fe82620daSamu Onkalo		printk(KERN_ERR DRIVER_NAME "out of memory\n");
924f9deb41f91c41d9d91a24c84a555ec7fe82620daSamu Onkalo		return -ENOMEM;
925f9deb41f91c41d9d91a24c84a555ec7fe82620daSamu Onkalo	}
926f9deb41f91c41d9d91a24c84a555ec7fe82620daSamu Onkalo
9272db4a76d5f3554e9e5632c8f91828313318579c8Samu Onkalo	mutex_init(&dev->mutex);
928e726111f953f8f5b922b953caf06ba6790c5fbaaSamu Onkalo	atomic_set(&dev->wake_thread, 0);
9292db4a76d5f3554e9e5632c8f91828313318579c8Samu Onkalo
930a38da2ed74f628c1f3e907c772be21a66eccab9cDaniel Mack	lis3lv02d_add_fs(dev);
931a002ee896dfd08ce9fba44e9ae513c9094699a27Eric Piel	lis3lv02d_poweron(dev);
932ab337a632783c251a3c3852aec0ead8a0281cbddDaniel Mack
9332a346996626ecbb4269c239e9ff7372a182907e9Samu Onkalo	if (dev->pm_dev) {
9342a346996626ecbb4269c239e9ff7372a182907e9Samu Onkalo		pm_runtime_set_active(dev->pm_dev);
9352a346996626ecbb4269c239e9ff7372a182907e9Samu Onkalo		pm_runtime_enable(dev->pm_dev);
9362a346996626ecbb4269c239e9ff7372a182907e9Samu Onkalo	}
9372a346996626ecbb4269c239e9ff7372a182907e9Samu Onkalo
938ab337a632783c251a3c3852aec0ead8a0281cbddDaniel Mack	if (lis3lv02d_joystick_enable())
93963366d37ad5dbb4f208b517c88ea4bd41738dbf7Joe Perches		pr_err("joystick initialization failed\n");
940ab337a632783c251a3c3852aec0ead8a0281cbddDaniel Mack
9418f3128e714ded7cf1e8c786c204a4f253b5d8ff4Daniel Mack	/* passing in platform specific data is purely optional and only
9428f3128e714ded7cf1e8c786c204a4f253b5d8ff4Daniel Mack	 * used by the SPI transport layer at the moment */
9438f3128e714ded7cf1e8c786c204a4f253b5d8ff4Daniel Mack	if (dev->pdata) {
9448f3128e714ded7cf1e8c786c204a4f253b5d8ff4Daniel Mack		struct lis3lv02d_platform_data *p = dev->pdata;
9458f3128e714ded7cf1e8c786c204a4f253b5d8ff4Daniel Mack
946ecc437aeee65afeea2e1bed963ccf6c384c555eaSamu Onkalo		if (dev->whoami == WAI_8B)
947ecc437aeee65afeea2e1bed963ccf6c384c555eaSamu Onkalo			lis3lv02d_8b_configure(dev, p);
9488873c33483e62988ed886230aab71ef4c678f710Daniel Mack
949cc23aa1ce2631b2fe1e3fba82ee444460f5ee3b7Samu Onkalo		irq_flags = p->irq_flags1 & IRQF_TRIGGER_MASK;
950cc23aa1ce2631b2fe1e3fba82ee444460f5ee3b7Samu Onkalo
951e726111f953f8f5b922b953caf06ba6790c5fbaaSamu Onkalo		dev->irq_cfg = p->irq_cfg;
9528f3128e714ded7cf1e8c786c204a4f253b5d8ff4Daniel Mack		if (p->irq_cfg)
9538f3128e714ded7cf1e8c786c204a4f253b5d8ff4Daniel Mack			dev->write(dev, CTRL_REG3, p->irq_cfg);
954cc23aa1ce2631b2fe1e3fba82ee444460f5ee3b7Samu Onkalo
955cc23aa1ce2631b2fe1e3fba82ee444460f5ee3b7Samu Onkalo		if (p->default_rate)
956cc23aa1ce2631b2fe1e3fba82ee444460f5ee3b7Samu Onkalo			lis3lv02d_set_odr(p->default_rate);
9578f3128e714ded7cf1e8c786c204a4f253b5d8ff4Daniel Mack	}
9588f3128e714ded7cf1e8c786c204a4f253b5d8ff4Daniel Mack
959a38da2ed74f628c1f3e907c772be21a66eccab9cDaniel Mack	/* bail if we did not get an IRQ from the bus layer */
960ab337a632783c251a3c3852aec0ead8a0281cbddDaniel Mack	if (!dev->irq) {
961a20f0bc10c47fcf62be027e1a50b62791052ab56Kalhan Trisal		pr_debug("No IRQ. Disabling /dev/freefall\n");
962ab337a632783c251a3c3852aec0ead8a0281cbddDaniel Mack		goto out;
963ab337a632783c251a3c3852aec0ead8a0281cbddDaniel Mack	}
964ab337a632783c251a3c3852aec0ead8a0281cbddDaniel Mack
96592ba4fe4b53b4fa5ac71ec4d80572348fca85796Samu Onkalo	/*
96692ba4fe4b53b4fa5ac71ec4d80572348fca85796Samu Onkalo	 * The sensor can generate interrupts for free-fall and direction
96792ba4fe4b53b4fa5ac71ec4d80572348fca85796Samu Onkalo	 * detection (distinguishable with FF_WU_SRC and DD_SRC) but to keep
96892ba4fe4b53b4fa5ac71ec4d80572348fca85796Samu Onkalo	 * the things simple and _fast_ we activate it only for free-fall, so
96992ba4fe4b53b4fa5ac71ec4d80572348fca85796Samu Onkalo	 * no need to read register (very slow with ACPI). For the same reason,
97092ba4fe4b53b4fa5ac71ec4d80572348fca85796Samu Onkalo	 * we forbid shared interrupts.
97192ba4fe4b53b4fa5ac71ec4d80572348fca85796Samu Onkalo	 *
97292ba4fe4b53b4fa5ac71ec4d80572348fca85796Samu Onkalo	 * IRQF_TRIGGER_RISING seems pointless on HP laptops because the
97392ba4fe4b53b4fa5ac71ec4d80572348fca85796Samu Onkalo	 * io-apic is not configurable (and generates a warning) but I keep it
97492ba4fe4b53b4fa5ac71ec4d80572348fca85796Samu Onkalo	 * in case of support for other hardware.
97592ba4fe4b53b4fa5ac71ec4d80572348fca85796Samu Onkalo	 */
976f7c77a3dc4683659b6f0d1b6cbc82b6253d095e0Takashi Iwai	if (dev->pdata && dev->whoami == WAI_8B)
97792ba4fe4b53b4fa5ac71ec4d80572348fca85796Samu Onkalo		thread_fn = lis302dl_interrupt_thread1_8b;
97892ba4fe4b53b4fa5ac71ec4d80572348fca85796Samu Onkalo	else
97992ba4fe4b53b4fa5ac71ec4d80572348fca85796Samu Onkalo		thread_fn = NULL;
98092ba4fe4b53b4fa5ac71ec4d80572348fca85796Samu Onkalo
98192ba4fe4b53b4fa5ac71ec4d80572348fca85796Samu Onkalo	err = request_threaded_irq(dev->irq, lis302dl_interrupt,
98292ba4fe4b53b4fa5ac71ec4d80572348fca85796Samu Onkalo				thread_fn,
983cc23aa1ce2631b2fe1e3fba82ee444460f5ee3b7Samu Onkalo				IRQF_TRIGGER_RISING | IRQF_ONESHOT |
984cc23aa1ce2631b2fe1e3fba82ee444460f5ee3b7Samu Onkalo				irq_flags,
98592ba4fe4b53b4fa5ac71ec4d80572348fca85796Samu Onkalo				DRIVER_NAME, &lis3_dev);
98692ba4fe4b53b4fa5ac71ec4d80572348fca85796Samu Onkalo
98792ba4fe4b53b4fa5ac71ec4d80572348fca85796Samu Onkalo	if (err < 0) {
98863366d37ad5dbb4f208b517c88ea4bd41738dbf7Joe Perches		pr_err("Cannot get IRQ\n");
98992ba4fe4b53b4fa5ac71ec4d80572348fca85796Samu Onkalo		goto out;
99092ba4fe4b53b4fa5ac71ec4d80572348fca85796Samu Onkalo	}
99192ba4fe4b53b4fa5ac71ec4d80572348fca85796Samu Onkalo
992ab337a632783c251a3c3852aec0ead8a0281cbddDaniel Mack	if (misc_register(&lis3lv02d_misc_device))
99363366d37ad5dbb4f208b517c88ea4bd41738dbf7Joe Perches		pr_err("misc_register failed\n");
994ab337a632783c251a3c3852aec0ead8a0281cbddDaniel Mackout:
995ab337a632783c251a3c3852aec0ead8a0281cbddDaniel Mack	return 0;
996ab337a632783c251a3c3852aec0ead8a0281cbddDaniel Mack}
997ab337a632783c251a3c3852aec0ead8a0281cbddDaniel MackEXPORT_SYMBOL_GPL(lis3lv02d_init_device);
998ab337a632783c251a3c3852aec0ead8a0281cbddDaniel Mack
999455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel MachekMODULE_DESCRIPTION("ST LIS3LV02Dx three-axis digital accelerometer driver");
1000ef2cfc790bf5f0ff189b01eabc0f4feb5e8524dfPavel MachekMODULE_AUTHOR("Yan Burman, Eric Piel, Pavel Machek");
1001455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel MachekMODULE_LICENSE("GPL");
1002