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