lis3lv02d.c revision cfce41a6d643c001d416ead960caf04fae2d609a
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
6cfce41a6d643c001d416ead960caf04fae2d609aEric Piel *  Copyright (C) 2008 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
23455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek#include <linux/kernel.h>
24455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek#include <linux/init.h>
25455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek#include <linux/dmi.h>
26455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek#include <linux/module.h>
27455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek#include <linux/types.h>
28455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek#include <linux/platform_device.h>
29455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek#include <linux/interrupt.h>
30455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek#include <linux/input.h>
31455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek#include <linux/kthread.h>
32455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek#include <linux/semaphore.h>
33455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek#include <linux/delay.h>
34455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek#include <linux/wait.h>
35455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek#include <linux/poll.h>
36455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek#include <linux/freezer.h>
37455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek#include <linux/uaccess.h>
38455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek#include <acpi/acpi_drivers.h>
39455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek#include <asm/atomic.h>
40455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek#include "lis3lv02d.h"
41455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek
42455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek#define DRIVER_NAME     "lis3lv02d"
43455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek
44455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek/* joystick device poll interval in milliseconds */
45455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek#define MDPS_POLL_INTERVAL 50
46455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek/*
47455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek * The sensor can also generate interrupts (DRDY) but it's pretty pointless
48455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek * because their are generated even if the data do not change. So it's better
49455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek * to keep the interrupt for the free-fall event. The values are updated at
50455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek * 40Hz (at the lowest frequency), but as it can be pretty time consuming on
51455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek * some low processor, we poll the sensor only at 20Hz... enough for the
52455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek * joystick.
53455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek */
54455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek
55455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek/* Maximum value our axis may get for the input device (signed 12 bits) */
56455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek#define MDPS_MAX_VAL 2048
57455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek
58cfce41a6d643c001d416ead960caf04fae2d609aEric Pielstruct acpi_lis3lv02d adev;
59cfce41a6d643c001d416ead960caf04fae2d609aEric PielEXPORT_SYMBOL_GPL(adev);
60455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek
61455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machekstatic int lis3lv02d_add_fs(struct acpi_device *device);
62455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek
63455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machekstatic s16 lis3lv02d_read_16(acpi_handle handle, int reg)
64455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek{
65455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek	u8 lo, hi;
66455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek
67cfce41a6d643c001d416ead960caf04fae2d609aEric Piel	adev.read(handle, reg, &lo);
68cfce41a6d643c001d416ead960caf04fae2d609aEric Piel	adev.read(handle, reg + 1, &hi);
69455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek	/* In "12 bit right justified" mode, bit 6, bit 7, bit 8 = bit 5 */
70455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek	return (s16)((hi << 8) | lo);
71455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek}
72455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek
73455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek/**
74455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek * lis3lv02d_get_axis - For the given axis, give the value converted
75455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek * @axis:      1,2,3 - can also be negative
76455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek * @hw_values: raw values returned by the hardware
77455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek *
78455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek * Returns the converted value.
79455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek */
80455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machekstatic inline int lis3lv02d_get_axis(s8 axis, int hw_values[3])
81455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek{
82455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek	if (axis > 0)
83455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek		return hw_values[axis - 1];
84455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek	else
85455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek		return -hw_values[-axis - 1];
86455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek}
87455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek
88455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek/**
89455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek * lis3lv02d_get_xyz - Get X, Y and Z axis values from the accelerometer
90455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek * @handle: the handle to the device
91455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek * @x:      where to store the X axis value
92455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek * @y:      where to store the Y axis value
93455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek * @z:      where to store the Z axis value
94455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek *
95455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek * Note that 40Hz input device can eat up about 10% CPU at 800MHZ
96455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek */
97455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machekstatic void lis3lv02d_get_xyz(acpi_handle handle, int *x, int *y, int *z)
98455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek{
99455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek	int position[3];
100455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek
101455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek	position[0] = lis3lv02d_read_16(handle, OUTX_L);
102455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek	position[1] = lis3lv02d_read_16(handle, OUTY_L);
103455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek	position[2] = lis3lv02d_read_16(handle, OUTZ_L);
104455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek
105455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek	*x = lis3lv02d_get_axis(adev.ac.x, position);
106455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek	*y = lis3lv02d_get_axis(adev.ac.y, position);
107455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek	*z = lis3lv02d_get_axis(adev.ac.z, position);
108455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek}
109455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek
110cfce41a6d643c001d416ead960caf04fae2d609aEric Pielvoid lis3lv02d_poweroff(acpi_handle handle)
111455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek{
112455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek	adev.is_on = 0;
113455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek	/* disable X,Y,Z axis and power down */
114cfce41a6d643c001d416ead960caf04fae2d609aEric Piel	adev.write(handle, CTRL_REG1, 0x00);
115455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek}
116cfce41a6d643c001d416ead960caf04fae2d609aEric PielEXPORT_SYMBOL_GPL(lis3lv02d_poweroff);
117455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek
118cfce41a6d643c001d416ead960caf04fae2d609aEric Pielvoid lis3lv02d_poweron(acpi_handle handle)
119455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek{
120455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek	u8 val;
121455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek
122455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek	adev.is_on = 1;
123cfce41a6d643c001d416ead960caf04fae2d609aEric Piel	adev.init(handle);
124cfce41a6d643c001d416ead960caf04fae2d609aEric Piel	adev.write(handle, FF_WU_CFG, 0);
125455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek	/*
126455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek	 * BDU: LSB and MSB values are not updated until both have been read.
127455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek	 *      So the value read will always be correct.
128455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek	 * IEN: Interrupt for free-fall and DD, not for data-ready.
129455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek	 */
130cfce41a6d643c001d416ead960caf04fae2d609aEric Piel	adev.read(handle, CTRL_REG2, &val);
131455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek	val |= CTRL2_BDU | CTRL2_IEN;
132cfce41a6d643c001d416ead960caf04fae2d609aEric Piel	adev.write(handle, CTRL_REG2, val);
133455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek}
134cfce41a6d643c001d416ead960caf04fae2d609aEric PielEXPORT_SYMBOL_GPL(lis3lv02d_poweron);
135455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek
136455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek/*
137455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek * To be called before starting to use the device. It makes sure that the
138455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek * device will always be on until a call to lis3lv02d_decrease_use(). Not to be
139455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek * used from interrupt context.
140455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek */
141455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machekstatic void lis3lv02d_increase_use(struct acpi_lis3lv02d *dev)
142455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek{
143455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek	mutex_lock(&dev->lock);
144455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek	dev->usage++;
145455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek	if (dev->usage == 1) {
146455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek		if (!dev->is_on)
147455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek			lis3lv02d_poweron(dev->device->handle);
148455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek	}
149455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek	mutex_unlock(&dev->lock);
150455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek}
151455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek
152455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek/*
153455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek * To be called whenever a usage of the device is stopped.
154455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek * It will make sure to turn off the device when there is not usage.
155455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek */
156455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machekstatic void lis3lv02d_decrease_use(struct acpi_lis3lv02d *dev)
157455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek{
158455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek	mutex_lock(&dev->lock);
159455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek	dev->usage--;
160455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek	if (dev->usage == 0)
161455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek		lis3lv02d_poweroff(dev->device->handle);
162455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek	mutex_unlock(&dev->lock);
163455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek}
164455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek
165455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek/**
166455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek * lis3lv02d_joystick_kthread - Kthread polling function
167455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek * @data: unused - here to conform to threadfn prototype
168455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek */
169455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machekstatic int lis3lv02d_joystick_kthread(void *data)
170455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek{
171455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek	int x, y, z;
172455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek
173455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek	while (!kthread_should_stop()) {
174455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek		lis3lv02d_get_xyz(adev.device->handle, &x, &y, &z);
175455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek		input_report_abs(adev.idev, ABS_X, x - adev.xcalib);
176455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek		input_report_abs(adev.idev, ABS_Y, y - adev.ycalib);
177455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek		input_report_abs(adev.idev, ABS_Z, z - adev.zcalib);
178455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek
179455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek		input_sync(adev.idev);
180455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek
181455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek		try_to_freeze();
182455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek		msleep_interruptible(MDPS_POLL_INTERVAL);
183455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek	}
184455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek
185455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek	return 0;
186455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek}
187455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek
188455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machekstatic int lis3lv02d_joystick_open(struct input_dev *input)
189455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek{
190455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek	lis3lv02d_increase_use(&adev);
191455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek	adev.kthread = kthread_run(lis3lv02d_joystick_kthread, NULL, "klis3lv02d");
192455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek	if (IS_ERR(adev.kthread)) {
193455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek		lis3lv02d_decrease_use(&adev);
194455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek		return PTR_ERR(adev.kthread);
195455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek	}
196455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek
197455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek	return 0;
198455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek}
199455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek
200455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machekstatic void lis3lv02d_joystick_close(struct input_dev *input)
201455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek{
202455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek	kthread_stop(adev.kthread);
203455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek	lis3lv02d_decrease_use(&adev);
204455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek}
205455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek
206455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek
207455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machekstatic inline void lis3lv02d_calibrate_joystick(void)
208455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek{
209455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek	lis3lv02d_get_xyz(adev.device->handle, &adev.xcalib, &adev.ycalib, &adev.zcalib);
210455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek}
211455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek
212cfce41a6d643c001d416ead960caf04fae2d609aEric Pielint lis3lv02d_joystick_enable(void)
213455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek{
214455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek	int err;
215455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek
216455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek	if (adev.idev)
217455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek		return -EINVAL;
218455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek
219455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek	adev.idev = input_allocate_device();
220455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek	if (!adev.idev)
221455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek		return -ENOMEM;
222455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek
223455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek	lis3lv02d_calibrate_joystick();
224455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek
225455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek	adev.idev->name       = "ST LIS3LV02DL Accelerometer";
226455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek	adev.idev->phys       = DRIVER_NAME "/input0";
227455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek	adev.idev->id.bustype = BUS_HOST;
228455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek	adev.idev->id.vendor  = 0;
229455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek	adev.idev->dev.parent = &adev.pdev->dev;
230455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek	adev.idev->open       = lis3lv02d_joystick_open;
231455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek	adev.idev->close      = lis3lv02d_joystick_close;
232455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek
233455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek	set_bit(EV_ABS, adev.idev->evbit);
234455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek	input_set_abs_params(adev.idev, ABS_X, -MDPS_MAX_VAL, MDPS_MAX_VAL, 3, 3);
235455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek	input_set_abs_params(adev.idev, ABS_Y, -MDPS_MAX_VAL, MDPS_MAX_VAL, 3, 3);
236455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek	input_set_abs_params(adev.idev, ABS_Z, -MDPS_MAX_VAL, MDPS_MAX_VAL, 3, 3);
237455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek
238455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek	err = input_register_device(adev.idev);
239455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek	if (err) {
240455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek		input_free_device(adev.idev);
241455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek		adev.idev = NULL;
242455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek	}
243455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek
244455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek	return err;
245455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek}
246cfce41a6d643c001d416ead960caf04fae2d609aEric PielEXPORT_SYMBOL_GPL(lis3lv02d_joystick_enable);
247455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek
248cfce41a6d643c001d416ead960caf04fae2d609aEric Pielvoid lis3lv02d_joystick_disable(void)
249455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek{
250455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek	if (!adev.idev)
251455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek		return;
252455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek
253455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek	input_unregister_device(adev.idev);
254455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek	adev.idev = NULL;
255455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek}
256cfce41a6d643c001d416ead960caf04fae2d609aEric PielEXPORT_SYMBOL_GPL(lis3lv02d_joystick_disable);
257455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek
258455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek/*
259455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek * Initialise the accelerometer and the various subsystems.
260455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek * Should be rather independant of the bus system.
261455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek */
262cfce41a6d643c001d416ead960caf04fae2d609aEric Pielint lis3lv02d_init_device(struct acpi_lis3lv02d *dev)
263455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek{
264455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek	mutex_init(&dev->lock);
265455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek	lis3lv02d_add_fs(dev->device);
266455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek	lis3lv02d_increase_use(dev);
267455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek
268455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek	if (lis3lv02d_joystick_enable())
269455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek		printk(KERN_ERR DRIVER_NAME ": joystick initialization failed\n");
270455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek
271455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek	lis3lv02d_decrease_use(dev);
272455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek	return 0;
273455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek}
274cfce41a6d643c001d416ead960caf04fae2d609aEric PielEXPORT_SYMBOL_GPL(lis3lv02d_init_device);
275455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek
276455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek/* Sysfs stuff */
277455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machekstatic ssize_t lis3lv02d_position_show(struct device *dev,
278455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek				struct device_attribute *attr, char *buf)
279455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek{
280455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek	int x, y, z;
281455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek
282455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek	lis3lv02d_increase_use(&adev);
283455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek	lis3lv02d_get_xyz(adev.device->handle, &x, &y, &z);
284455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek	lis3lv02d_decrease_use(&adev);
285455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek	return sprintf(buf, "(%d,%d,%d)\n", x, y, z);
286455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek}
287455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek
288455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machekstatic ssize_t lis3lv02d_calibrate_show(struct device *dev,
289455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek				struct device_attribute *attr, char *buf)
290455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek{
291455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek	return sprintf(buf, "(%d,%d,%d)\n", adev.xcalib, adev.ycalib, adev.zcalib);
292455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek}
293455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek
294455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machekstatic ssize_t lis3lv02d_calibrate_store(struct device *dev,
295455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek				struct device_attribute *attr,
296455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek				const char *buf, size_t count)
297455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek{
298455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek	lis3lv02d_increase_use(&adev);
299455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek	lis3lv02d_calibrate_joystick();
300455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek	lis3lv02d_decrease_use(&adev);
301455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek	return count;
302455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek}
303455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek
304455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek/* conversion btw sampling rate and the register values */
305455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machekstatic int lis3lv02dl_df_val[4] = {40, 160, 640, 2560};
306455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machekstatic ssize_t lis3lv02d_rate_show(struct device *dev,
307455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek			struct device_attribute *attr, char *buf)
308455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek{
309455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek	u8 ctrl;
310455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek	int val;
311455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek
312455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek	lis3lv02d_increase_use(&adev);
313cfce41a6d643c001d416ead960caf04fae2d609aEric Piel	adev.read(adev.device->handle, CTRL_REG1, &ctrl);
314455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek	lis3lv02d_decrease_use(&adev);
315455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek	val = (ctrl & (CTRL1_DF0 | CTRL1_DF1)) >> 4;
316455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek	return sprintf(buf, "%d\n", lis3lv02dl_df_val[val]);
317455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek}
318455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek
319455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machekstatic DEVICE_ATTR(position, S_IRUGO, lis3lv02d_position_show, NULL);
320455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machekstatic DEVICE_ATTR(calibrate, S_IRUGO|S_IWUSR, lis3lv02d_calibrate_show,
321455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek	lis3lv02d_calibrate_store);
322455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machekstatic DEVICE_ATTR(rate, S_IRUGO, lis3lv02d_rate_show, NULL);
323455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek
324455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machekstatic struct attribute *lis3lv02d_attributes[] = {
325455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek	&dev_attr_position.attr,
326455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek	&dev_attr_calibrate.attr,
327455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek	&dev_attr_rate.attr,
328455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek	NULL
329455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek};
330455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek
331455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machekstatic struct attribute_group lis3lv02d_attribute_group = {
332455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek	.attrs = lis3lv02d_attributes
333455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek};
334455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek
335cfce41a6d643c001d416ead960caf04fae2d609aEric Piel
336455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machekstatic int lis3lv02d_add_fs(struct acpi_device *device)
337455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek{
338455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek	adev.pdev = platform_device_register_simple(DRIVER_NAME, -1, NULL, 0);
339455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek	if (IS_ERR(adev.pdev))
340455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek		return PTR_ERR(adev.pdev);
341455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek
342455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek	return sysfs_create_group(&adev.pdev->dev.kobj, &lis3lv02d_attribute_group);
343455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek}
344455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek
345cfce41a6d643c001d416ead960caf04fae2d609aEric Pielint lis3lv02d_remove_fs(void)
346455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek{
347455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek	sysfs_remove_group(&adev.pdev->dev.kobj, &lis3lv02d_attribute_group);
348455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek	platform_device_unregister(adev.pdev);
349455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek	return 0;
350455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek}
351cfce41a6d643c001d416ead960caf04fae2d609aEric PielEXPORT_SYMBOL_GPL(lis3lv02d_remove_fs);
352455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek
353455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel MachekMODULE_DESCRIPTION("ST LIS3LV02Dx three-axis digital accelerometer driver");
354455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel MachekMODULE_AUTHOR("Yan Burman and Eric Piel");
355455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel MachekMODULE_LICENSE("GPL");
356455fbdd376c3ed3a5be8c039348896fdd87e9930Pavel Machek
357