2008-11-13 00:27:02 +03:00
/*
* lis3lv02d . c - ST LIS3LV02DL accelerometer driver
*
* Copyright ( C ) 2007 - 2008 Yan Burman
* Copyright ( C ) 2008 Eric Piel
2009-02-19 01:48:23 +03:00
* Copyright ( C ) 2008 - 2009 Pavel Machek
2008-11-13 00:27:02 +03:00
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; either version 2 of the License , or
* ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 59 Temple Place , Suite 330 , Boston , MA 02111 - 1307 USA
*/
# include <linux/kernel.h>
# include <linux/init.h>
# include <linux/dmi.h>
# include <linux/module.h>
# include <linux/types.h>
# include <linux/platform_device.h>
# include <linux/interrupt.h>
# include <linux/input.h>
# include <linux/kthread.h>
# include <linux/semaphore.h>
# include <linux/delay.h>
# include <linux/wait.h>
# include <linux/poll.h>
# include <linux/freezer.h>
# include <linux/uaccess.h>
2009-02-19 01:48:23 +03:00
# include <linux/miscdevice.h>
2008-11-13 00:27:02 +03:00
# include <asm/atomic.h>
# include "lis3lv02d.h"
# define DRIVER_NAME "lis3lv02d"
/* joystick device poll interval in milliseconds */
# define MDPS_POLL_INTERVAL 50
/*
* The sensor can also generate interrupts ( DRDY ) but it ' s pretty pointless
* because their are generated even if the data do not change . So it ' s better
* to keep the interrupt for the free - fall event . The values are updated at
* 40 Hz ( at the lowest frequency ) , but as it can be pretty time consuming on
* some low processor , we poll the sensor only at 20 Hz . . . enough for the
* joystick .
*/
2009-04-01 02:24:32 +04:00
struct lis3lv02d lis3_dev = {
2009-04-01 02:24:26 +04:00
. misc_wait = __WAIT_QUEUE_HEAD_INITIALIZER ( lis3_dev . misc_wait ) ,
2009-02-19 01:48:23 +03:00
} ;
2009-04-01 02:24:26 +04:00
EXPORT_SYMBOL_GPL ( lis3_dev ) ;
2008-11-13 00:27:02 +03:00
2009-04-01 02:24:32 +04:00
static s16 lis3lv02d_read_8 ( struct lis3lv02d * lis3 , int reg )
{
s8 lo ;
if ( lis3 - > read ( lis3 , reg , & lo ) < 0 )
return 0 ;
return lo ;
}
static s16 lis3lv02d_read_16 ( struct lis3lv02d * lis3 , int reg )
2009-04-01 02:24:26 +04:00
{
u8 lo , hi ;
2009-04-01 02:24:32 +04:00
lis3 - > read ( lis3 , reg - 1 , & lo ) ;
lis3 - > read ( lis3 , reg , & hi ) ;
2009-04-01 02:24:26 +04:00
/* In "12 bit right justified" mode, bit 6, bit 7, bit 8 = bit 5 */
return ( s16 ) ( ( hi < < 8 ) | lo ) ;
}
2008-11-13 00:27:02 +03:00
/**
* lis3lv02d_get_axis - For the given axis , give the value converted
* @ axis : 1 , 2 , 3 - can also be negative
* @ hw_values : raw values returned by the hardware
*
* Returns the converted value .
*/
static inline int lis3lv02d_get_axis ( s8 axis , int hw_values [ 3 ] )
{
if ( axis > 0 )
return hw_values [ axis - 1 ] ;
else
return - hw_values [ - axis - 1 ] ;
}
/**
* lis3lv02d_get_xyz - Get X , Y and Z axis values from the accelerometer
2009-04-01 02:24:32 +04:00
* @ lis3 : pointer to the device struct
* @ x : where to store the X axis value
* @ y : where to store the Y axis value
* @ z : where to store the Z axis value
2008-11-13 00:27:02 +03:00
*
* Note that 40 Hz input device can eat up about 10 % CPU at 800 MHZ
*/
2009-04-01 02:24:32 +04:00
static void lis3lv02d_get_xyz ( struct lis3lv02d * lis3 , int * x , int * y , int * z )
2008-11-13 00:27:02 +03:00
{
int position [ 3 ] ;
2009-04-01 02:24:32 +04:00
position [ 0 ] = lis3_dev . read_data ( lis3 , OUTX ) ;
position [ 1 ] = lis3_dev . read_data ( lis3 , OUTY ) ;
position [ 2 ] = lis3_dev . read_data ( lis3 , OUTZ ) ;
2008-11-13 00:27:02 +03:00
2009-04-01 02:24:26 +04:00
* x = lis3lv02d_get_axis ( lis3_dev . ac . x , position ) ;
* y = lis3lv02d_get_axis ( lis3_dev . ac . y , position ) ;
* z = lis3lv02d_get_axis ( lis3_dev . ac . z , position ) ;
2008-11-13 00:27:02 +03:00
}
2009-04-01 02:24:32 +04:00
void lis3lv02d_poweroff ( struct lis3lv02d * lis3 )
2008-11-13 00:27:02 +03:00
{
2009-04-01 02:24:26 +04:00
lis3_dev . is_on = 0 ;
2008-11-13 00:27:02 +03:00
}
2009-01-10 03:41:01 +03:00
EXPORT_SYMBOL_GPL ( lis3lv02d_poweroff ) ;
2008-11-13 00:27:02 +03:00
2009-04-01 02:24:32 +04:00
void lis3lv02d_poweron ( struct lis3lv02d * lis3 )
2008-11-13 00:27:02 +03:00
{
2009-04-01 02:24:26 +04:00
lis3_dev . is_on = 1 ;
2009-04-01 02:24:32 +04:00
lis3_dev . init ( lis3 ) ;
2008-11-13 00:27:02 +03:00
}
2009-01-10 03:41:01 +03:00
EXPORT_SYMBOL_GPL ( lis3lv02d_poweron ) ;
2008-11-13 00:27:02 +03:00
/*
* To be called before starting to use the device . It makes sure that the
* device will always be on until a call to lis3lv02d_decrease_use ( ) . Not to be
* used from interrupt context .
*/
2009-04-01 02:24:32 +04:00
static void lis3lv02d_increase_use ( struct lis3lv02d * dev )
2008-11-13 00:27:02 +03:00
{
mutex_lock ( & dev - > lock ) ;
dev - > usage + + ;
if ( dev - > usage = = 1 ) {
if ( ! dev - > is_on )
2009-04-01 02:24:32 +04:00
lis3lv02d_poweron ( dev ) ;
2008-11-13 00:27:02 +03:00
}
mutex_unlock ( & dev - > lock ) ;
}
/*
* To be called whenever a usage of the device is stopped .
* It will make sure to turn off the device when there is not usage .
*/
2009-04-01 02:24:32 +04:00
static void lis3lv02d_decrease_use ( struct lis3lv02d * dev )
2008-11-13 00:27:02 +03:00
{
mutex_lock ( & dev - > lock ) ;
dev - > usage - - ;
if ( dev - > usage = = 0 )
2009-04-01 02:24:32 +04:00
lis3lv02d_poweroff ( dev ) ;
2008-11-13 00:27:02 +03:00
mutex_unlock ( & dev - > lock ) ;
}
2009-02-19 01:48:23 +03:00
static irqreturn_t lis302dl_interrupt ( int irq , void * dummy )
{
/*
* Be careful : on some HP laptops the bios force DD when on battery and
* the lid is closed . This leads to interrupts as soon as a little move
* is done .
*/
2009-04-01 02:24:26 +04:00
atomic_inc ( & lis3_dev . count ) ;
2009-02-19 01:48:23 +03:00
2009-04-01 02:24:26 +04:00
wake_up_interruptible ( & lis3_dev . misc_wait ) ;
kill_fasync ( & lis3_dev . async_queue , SIGIO , POLL_IN ) ;
2009-02-19 01:48:23 +03:00
return IRQ_HANDLED ;
}
static int lis3lv02d_misc_open ( struct inode * inode , struct file * file )
{
int ret ;
2009-04-01 02:24:26 +04:00
if ( test_and_set_bit ( 0 , & lis3_dev . misc_opened ) )
2009-02-19 01:48:23 +03:00
return - EBUSY ; /* already open */
2009-04-01 02:24:26 +04:00
atomic_set ( & lis3_dev . count , 0 ) ;
2009-02-19 01:48:23 +03:00
/*
* The sensor can generate interrupts for free - fall and direction
* detection ( distinguishable with FF_WU_SRC and DD_SRC ) but to keep
* the things simple and _fast_ we activate it only for free - fall , so
* no need to read register ( very slow with ACPI ) . For the same reason ,
* we forbid shared interrupts .
*
* IRQF_TRIGGER_RISING seems pointless on HP laptops because the
* io - apic is not configurable ( and generates a warning ) but I keep it
* in case of support for other hardware .
*/
2009-04-01 02:24:26 +04:00
ret = request_irq ( lis3_dev . irq , lis302dl_interrupt , IRQF_TRIGGER_RISING ,
DRIVER_NAME , & lis3_dev ) ;
2009-02-19 01:48:23 +03:00
if ( ret ) {
2009-04-01 02:24:26 +04:00
clear_bit ( 0 , & lis3_dev . misc_opened ) ;
printk ( KERN_ERR DRIVER_NAME " : IRQ%d allocation failed \n " , lis3_dev . irq ) ;
2009-02-19 01:48:23 +03:00
return - EBUSY ;
}
2009-04-01 02:24:26 +04:00
lis3lv02d_increase_use ( & lis3_dev ) ;
printk ( " lis3: registered interrupt %d \n " , lis3_dev . irq ) ;
2009-02-19 01:48:23 +03:00
return 0 ;
}
static int lis3lv02d_misc_release ( struct inode * inode , struct file * file )
{
2009-04-01 02:24:26 +04:00
fasync_helper ( - 1 , file , 0 , & lis3_dev . async_queue ) ;
lis3lv02d_decrease_use ( & lis3_dev ) ;
free_irq ( lis3_dev . irq , & lis3_dev ) ;
clear_bit ( 0 , & lis3_dev . misc_opened ) ; /* release the device */
2009-02-19 01:48:23 +03:00
return 0 ;
}
static ssize_t lis3lv02d_misc_read ( struct file * file , char __user * buf ,
size_t count , loff_t * pos )
{
DECLARE_WAITQUEUE ( wait , current ) ;
u32 data ;
unsigned char byte_data ;
ssize_t retval = 1 ;
if ( count < 1 )
return - EINVAL ;
2009-04-01 02:24:26 +04:00
add_wait_queue ( & lis3_dev . misc_wait , & wait ) ;
2009-02-19 01:48:23 +03:00
while ( true ) {
set_current_state ( TASK_INTERRUPTIBLE ) ;
2009-04-01 02:24:26 +04:00
data = atomic_xchg ( & lis3_dev . count , 0 ) ;
2009-02-19 01:48:23 +03:00
if ( data )
break ;
if ( file - > f_flags & O_NONBLOCK ) {
retval = - EAGAIN ;
goto out ;
}
if ( signal_pending ( current ) ) {
retval = - ERESTARTSYS ;
goto out ;
}
schedule ( ) ;
}
if ( data < 255 )
byte_data = data ;
else
byte_data = 255 ;
/* make sure we are not going into copy_to_user() with
* TASK_INTERRUPTIBLE state */
set_current_state ( TASK_RUNNING ) ;
if ( copy_to_user ( buf , & byte_data , sizeof ( byte_data ) ) )
retval = - EFAULT ;
out :
__set_current_state ( TASK_RUNNING ) ;
2009-04-01 02:24:26 +04:00
remove_wait_queue ( & lis3_dev . misc_wait , & wait ) ;
2009-02-19 01:48:23 +03:00
return retval ;
}
static unsigned int lis3lv02d_misc_poll ( struct file * file , poll_table * wait )
{
2009-04-01 02:24:26 +04:00
poll_wait ( file , & lis3_dev . misc_wait , wait ) ;
if ( atomic_read ( & lis3_dev . count ) )
2009-02-19 01:48:23 +03:00
return POLLIN | POLLRDNORM ;
return 0 ;
}
static int lis3lv02d_misc_fasync ( int fd , struct file * file , int on )
{
2009-04-01 02:24:26 +04:00
return fasync_helper ( fd , file , on , & lis3_dev . async_queue ) ;
2009-02-19 01:48:23 +03:00
}
static const struct file_operations lis3lv02d_misc_fops = {
. owner = THIS_MODULE ,
. llseek = no_llseek ,
. read = lis3lv02d_misc_read ,
. open = lis3lv02d_misc_open ,
. release = lis3lv02d_misc_release ,
. poll = lis3lv02d_misc_poll ,
. fasync = lis3lv02d_misc_fasync ,
} ;
static struct miscdevice lis3lv02d_misc_device = {
. minor = MISC_DYNAMIC_MINOR ,
. name = " freefall " ,
. fops = & lis3lv02d_misc_fops ,
} ;
2008-11-13 00:27:02 +03:00
/**
* lis3lv02d_joystick_kthread - Kthread polling function
* @ data : unused - here to conform to threadfn prototype
*/
static int lis3lv02d_joystick_kthread ( void * data )
{
int x , y , z ;
while ( ! kthread_should_stop ( ) ) {
2009-04-01 02:24:32 +04:00
lis3lv02d_get_xyz ( & lis3_dev , & x , & y , & z ) ;
2009-04-01 02:24:26 +04:00
input_report_abs ( lis3_dev . idev , ABS_X , x - lis3_dev . xcalib ) ;
input_report_abs ( lis3_dev . idev , ABS_Y , y - lis3_dev . ycalib ) ;
input_report_abs ( lis3_dev . idev , ABS_Z , z - lis3_dev . zcalib ) ;
2008-11-13 00:27:02 +03:00
2009-04-01 02:24:26 +04:00
input_sync ( lis3_dev . idev ) ;
2008-11-13 00:27:02 +03:00
try_to_freeze ( ) ;
msleep_interruptible ( MDPS_POLL_INTERVAL ) ;
}
return 0 ;
}
static int lis3lv02d_joystick_open ( struct input_dev * input )
{
2009-04-01 02:24:26 +04:00
lis3lv02d_increase_use ( & lis3_dev ) ;
lis3_dev . kthread = kthread_run ( lis3lv02d_joystick_kthread , NULL , " klis3lv02d " ) ;
if ( IS_ERR ( lis3_dev . kthread ) ) {
lis3lv02d_decrease_use ( & lis3_dev ) ;
return PTR_ERR ( lis3_dev . kthread ) ;
2008-11-13 00:27:02 +03:00
}
return 0 ;
}
static void lis3lv02d_joystick_close ( struct input_dev * input )
{
2009-04-01 02:24:26 +04:00
kthread_stop ( lis3_dev . kthread ) ;
lis3lv02d_decrease_use ( & lis3_dev ) ;
2008-11-13 00:27:02 +03:00
}
static inline void lis3lv02d_calibrate_joystick ( void )
{
2009-04-01 02:24:32 +04:00
lis3lv02d_get_xyz ( & lis3_dev ,
& lis3_dev . xcalib , & lis3_dev . ycalib , & lis3_dev . zcalib ) ;
2008-11-13 00:27:02 +03:00
}
2009-01-10 03:41:01 +03:00
int lis3lv02d_joystick_enable ( void )
2008-11-13 00:27:02 +03:00
{
int err ;
2009-04-01 02:24:26 +04:00
if ( lis3_dev . idev )
2008-11-13 00:27:02 +03:00
return - EINVAL ;
2009-04-01 02:24:26 +04:00
lis3_dev . idev = input_allocate_device ( ) ;
if ( ! lis3_dev . idev )
2008-11-13 00:27:02 +03:00
return - ENOMEM ;
lis3lv02d_calibrate_joystick ( ) ;
2009-04-01 02:24:26 +04:00
lis3_dev . idev - > name = " ST LIS3LV02DL Accelerometer " ;
lis3_dev . idev - > phys = DRIVER_NAME " /input0 " ;
lis3_dev . idev - > id . bustype = BUS_HOST ;
lis3_dev . idev - > id . vendor = 0 ;
lis3_dev . idev - > dev . parent = & lis3_dev . pdev - > dev ;
lis3_dev . idev - > open = lis3lv02d_joystick_open ;
lis3_dev . idev - > close = lis3lv02d_joystick_close ;
2008-11-13 00:27:02 +03:00
2009-04-01 02:24:26 +04:00
set_bit ( EV_ABS , lis3_dev . idev - > evbit ) ;
input_set_abs_params ( lis3_dev . idev , ABS_X , - lis3_dev . mdps_max_val , lis3_dev . mdps_max_val , 3 , 3 ) ;
input_set_abs_params ( lis3_dev . idev , ABS_Y , - lis3_dev . mdps_max_val , lis3_dev . mdps_max_val , 3 , 3 ) ;
input_set_abs_params ( lis3_dev . idev , ABS_Z , - lis3_dev . mdps_max_val , lis3_dev . mdps_max_val , 3 , 3 ) ;
2008-11-13 00:27:02 +03:00
2009-04-01 02:24:26 +04:00
err = input_register_device ( lis3_dev . idev ) ;
2008-11-13 00:27:02 +03:00
if ( err ) {
2009-04-01 02:24:26 +04:00
input_free_device ( lis3_dev . idev ) ;
lis3_dev . idev = NULL ;
2008-11-13 00:27:02 +03:00
}
return err ;
}
2009-01-10 03:41:01 +03:00
EXPORT_SYMBOL_GPL ( lis3lv02d_joystick_enable ) ;
2008-11-13 00:27:02 +03:00
2009-01-10 03:41:01 +03:00
void lis3lv02d_joystick_disable ( void )
2008-11-13 00:27:02 +03:00
{
2009-04-01 02:24:26 +04:00
if ( ! lis3_dev . idev )
2008-11-13 00:27:02 +03:00
return ;
2009-02-19 01:48:23 +03:00
misc_deregister ( & lis3lv02d_misc_device ) ;
2009-04-01 02:24:26 +04:00
input_unregister_device ( lis3_dev . idev ) ;
lis3_dev . idev = NULL ;
2008-11-13 00:27:02 +03:00
}
2009-01-10 03:41:01 +03:00
EXPORT_SYMBOL_GPL ( lis3lv02d_joystick_disable ) ;
2008-11-13 00:27:02 +03:00
/* Sysfs stuff */
static ssize_t lis3lv02d_position_show ( struct device * dev ,
struct device_attribute * attr , char * buf )
{
int x , y , z ;
2009-04-01 02:24:26 +04:00
lis3lv02d_increase_use ( & lis3_dev ) ;
2009-04-01 02:24:32 +04:00
lis3lv02d_get_xyz ( & lis3_dev , & x , & y , & z ) ;
2009-04-01 02:24:26 +04:00
lis3lv02d_decrease_use ( & lis3_dev ) ;
2008-11-13 00:27:02 +03:00
return sprintf ( buf , " (%d,%d,%d) \ n " , x, y, z) ;
}
static ssize_t lis3lv02d_calibrate_show ( struct device * dev ,
struct device_attribute * attr , char * buf )
{
2009-04-01 02:24:26 +04:00
return sprintf ( buf , " (%d,%d,%d) \ n " , lis3_dev.xcalib, lis3_dev.ycalib, lis3_dev.zcalib) ;
2008-11-13 00:27:02 +03:00
}
static ssize_t lis3lv02d_calibrate_store ( struct device * dev ,
struct device_attribute * attr ,
const char * buf , size_t count )
{
2009-04-01 02:24:26 +04:00
lis3lv02d_increase_use ( & lis3_dev ) ;
2008-11-13 00:27:02 +03:00
lis3lv02d_calibrate_joystick ( ) ;
2009-04-01 02:24:26 +04:00
lis3lv02d_decrease_use ( & lis3_dev ) ;
2008-11-13 00:27:02 +03:00
return count ;
}
/* conversion btw sampling rate and the register values */
static int lis3lv02dl_df_val [ 4 ] = { 40 , 160 , 640 , 2560 } ;
static ssize_t lis3lv02d_rate_show ( struct device * dev ,
struct device_attribute * attr , char * buf )
{
u8 ctrl ;
int val ;
2009-04-01 02:24:26 +04:00
lis3lv02d_increase_use ( & lis3_dev ) ;
2009-04-01 02:24:32 +04:00
lis3_dev . read ( & lis3_dev , CTRL_REG1 , & ctrl ) ;
2009-04-01 02:24:26 +04:00
lis3lv02d_decrease_use ( & lis3_dev ) ;
2008-11-13 00:27:02 +03:00
val = ( ctrl & ( CTRL1_DF0 | CTRL1_DF1 ) ) > > 4 ;
return sprintf ( buf , " %d \n " , lis3lv02dl_df_val [ val ] ) ;
}
static DEVICE_ATTR ( position , S_IRUGO , lis3lv02d_position_show , NULL ) ;
static DEVICE_ATTR ( calibrate , S_IRUGO | S_IWUSR , lis3lv02d_calibrate_show ,
lis3lv02d_calibrate_store ) ;
static DEVICE_ATTR ( rate , S_IRUGO , lis3lv02d_rate_show , NULL ) ;
static struct attribute * lis3lv02d_attributes [ ] = {
& dev_attr_position . attr ,
& dev_attr_calibrate . attr ,
& dev_attr_rate . attr ,
NULL
} ;
static struct attribute_group lis3lv02d_attribute_group = {
. attrs = lis3lv02d_attributes
} ;
2009-01-10 03:41:01 +03:00
2009-04-01 02:24:32 +04:00
static int lis3lv02d_add_fs ( struct lis3lv02d * lis3 )
2008-11-13 00:27:02 +03:00
{
2009-04-01 02:24:26 +04:00
lis3_dev . pdev = platform_device_register_simple ( DRIVER_NAME , - 1 , NULL , 0 ) ;
if ( IS_ERR ( lis3_dev . pdev ) )
return PTR_ERR ( lis3_dev . pdev ) ;
2008-11-13 00:27:02 +03:00
2009-04-01 02:24:26 +04:00
return sysfs_create_group ( & lis3_dev . pdev - > dev . kobj , & lis3lv02d_attribute_group ) ;
2008-11-13 00:27:02 +03:00
}
2009-01-10 03:41:01 +03:00
int lis3lv02d_remove_fs ( void )
2008-11-13 00:27:02 +03:00
{
2009-04-01 02:24:26 +04:00
sysfs_remove_group ( & lis3_dev . pdev - > dev . kobj , & lis3lv02d_attribute_group ) ;
platform_device_unregister ( lis3_dev . pdev ) ;
2008-11-13 00:27:02 +03:00
return 0 ;
}
2009-01-10 03:41:01 +03:00
EXPORT_SYMBOL_GPL ( lis3lv02d_remove_fs ) ;
2008-11-13 00:27:02 +03:00
2009-04-01 02:24:31 +04:00
/*
* Initialise the accelerometer and the various subsystems .
* Should be rather independant of the bus system .
*/
2009-04-01 02:24:32 +04:00
int lis3lv02d_init_device ( struct lis3lv02d * dev )
2009-04-01 02:24:31 +04:00
{
2009-04-01 02:24:32 +04:00
dev - > whoami = lis3lv02d_read_8 ( dev , WHO_AM_I ) ;
switch ( dev - > whoami ) {
case LIS_DOUBLE_ID :
printk ( KERN_INFO DRIVER_NAME " : 2-byte sensor found \n " ) ;
dev - > read_data = lis3lv02d_read_16 ;
dev - > mdps_max_val = 2048 ;
break ;
case LIS_SINGLE_ID :
printk ( KERN_INFO DRIVER_NAME " : 1-byte sensor found \n " ) ;
dev - > read_data = lis3lv02d_read_8 ;
dev - > mdps_max_val = 128 ;
break ;
default :
printk ( KERN_ERR DRIVER_NAME
" : unknown sensor type 0x%X \n " , lis3_dev . whoami ) ;
return - EINVAL ;
}
2009-04-01 02:24:31 +04:00
mutex_init ( & dev - > lock ) ;
2009-04-01 02:24:32 +04:00
lis3lv02d_add_fs ( dev ) ;
2009-04-01 02:24:31 +04:00
lis3lv02d_increase_use ( dev ) ;
if ( lis3lv02d_joystick_enable ( ) )
printk ( KERN_ERR DRIVER_NAME " : joystick initialization failed \n " ) ;
printk ( " lis3_init_device: irq %d \n " , dev - > irq ) ;
2009-04-01 02:24:32 +04:00
/* bail if we did not get an IRQ from the bus layer */
2009-04-01 02:24:31 +04:00
if ( ! dev - > irq ) {
printk ( KERN_ERR DRIVER_NAME
2009-04-01 02:24:32 +04:00
" : No IRQ. Disabling /dev/freefall \n " ) ;
2009-04-01 02:24:31 +04:00
goto out ;
}
printk ( " lis3: registering device \n " ) ;
if ( misc_register ( & lis3lv02d_misc_device ) )
printk ( KERN_ERR DRIVER_NAME " : misc_register failed \n " ) ;
out :
lis3lv02d_decrease_use ( dev ) ;
return 0 ;
}
EXPORT_SYMBOL_GPL ( lis3lv02d_init_device ) ;
2008-11-13 00:27:02 +03:00
MODULE_DESCRIPTION ( " ST LIS3LV02Dx three-axis digital accelerometer driver " ) ;
2009-02-19 01:48:23 +03:00
MODULE_AUTHOR ( " Yan Burman, Eric Piel, Pavel Machek " ) ;
2008-11-13 00:27:02 +03:00
MODULE_LICENSE ( " GPL " ) ;