lis3: remove automatic shutdown of the device
[sfrench/cifs-2.6.git] / drivers / hwmon / lis3lv02d.c
index 8bb2158f0453c01f3c089ffaa88d8eea2927fd9d..df3f58613f7bf4067284c2040d8b9fad8ae43f58 100644 (file)
@@ -36,7 +36,6 @@
 #include <linux/freezer.h>
 #include <linux/uaccess.h>
 #include <linux/miscdevice.h>
-#include <acpi/acpi_drivers.h>
 #include <asm/atomic.h>
 #include "lis3lv02d.h"
 
  * joystick.
  */
 
-struct acpi_lis3lv02d adev = {
-       .misc_wait   = __WAIT_QUEUE_HEAD_INITIALIZER(adev.misc_wait),
+struct lis3lv02d lis3_dev = {
+       .misc_wait   = __WAIT_QUEUE_HEAD_INITIALIZER(lis3_dev.misc_wait),
 };
 
-EXPORT_SYMBOL_GPL(adev);
+EXPORT_SYMBOL_GPL(lis3_dev);
 
-static int lis3lv02d_add_fs(struct acpi_device *device);
+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)
+{
+       u8 lo, hi;
+
+       lis3->read(lis3, reg - 1, &lo);
+       lis3->read(lis3, reg, &hi);
+       /* In "12 bit right justified" mode, bit 6, bit 7, bit 8 = bit 5 */
+       return (s16)((hi << 8) | lo);
+}
 
 /**
  * lis3lv02d_get_axis - For the given axis, give the value converted
@@ -78,67 +94,50 @@ static inline int lis3lv02d_get_axis(s8 axis, int hw_values[3])
 
 /**
  * lis3lv02d_get_xyz - Get X, Y and Z axis values from the accelerometer
- * @handle: the handle to the device
- * @x:      where to store the X axis value
- * @y:      where to store the Y axis value
- * @z:      where to store the Z axis value
+ * @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
  *
  * Note that 40Hz input device can eat up about 10% CPU at 800MHZ
  */
-static void lis3lv02d_get_xyz(acpi_handle handle, int *x, int *y, int *z)
+static void lis3lv02d_get_xyz(struct lis3lv02d *lis3, int *x, int *y, int *z)
 {
        int position[3];
 
-       position[0] = adev.read_data(handle, OUTX);
-       position[1] = adev.read_data(handle, OUTY);
-       position[2] = adev.read_data(handle, OUTZ);
+       position[0] = lis3->read_data(lis3, OUTX);
+       position[1] = lis3->read_data(lis3, OUTY);
+       position[2] = lis3->read_data(lis3, OUTZ);
 
-       *x = lis3lv02d_get_axis(adev.ac.x, position);
-       *y = lis3lv02d_get_axis(adev.ac.y, position);
-       *z = lis3lv02d_get_axis(adev.ac.z, position);
+       *x = lis3lv02d_get_axis(lis3->ac.x, position);
+       *y = lis3lv02d_get_axis(lis3->ac.y, position);
+       *z = lis3lv02d_get_axis(lis3->ac.z, position);
 }
 
-void lis3lv02d_poweroff(acpi_handle handle)
+void lis3lv02d_poweroff(struct lis3lv02d *lis3)
 {
-       adev.is_on = 0;
+       /* disable X,Y,Z axis and power down */
+       lis3->write(lis3, CTRL_REG1, 0x00);
 }
 EXPORT_SYMBOL_GPL(lis3lv02d_poweroff);
 
-void lis3lv02d_poweron(acpi_handle handle)
+void lis3lv02d_poweron(struct lis3lv02d *lis3)
 {
-       adev.is_on = 1;
-       adev.init(handle);
-}
-EXPORT_SYMBOL_GPL(lis3lv02d_poweron);
+       u8 reg;
 
-/*
- * 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.
- */
-static void lis3lv02d_increase_use(struct acpi_lis3lv02d *dev)
-{
-       mutex_lock(&dev->lock);
-       dev->usage++;
-       if (dev->usage == 1) {
-               if (!dev->is_on)
-                       lis3lv02d_poweron(dev->device->handle);
-       }
-       mutex_unlock(&dev->lock);
-}
+       lis3->init(lis3);
 
-/*
- * 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.
- */
-static void lis3lv02d_decrease_use(struct acpi_lis3lv02d *dev)
-{
-       mutex_lock(&dev->lock);
-       dev->usage--;
-       if (dev->usage == 0)
-               lis3lv02d_poweroff(dev->device->handle);
-       mutex_unlock(&dev->lock);
+       /*
+        * Common configuration
+        * BDU: LSB and MSB values are not updated until both have been read.
+        *      So the value read will always be correct.
+        */
+       lis3->read(lis3, CTRL_REG2, &reg);
+       reg |= CTRL2_BDU;
+       lis3->write(lis3, CTRL_REG2, reg);
 }
+EXPORT_SYMBOL_GPL(lis3lv02d_poweron);
+
 
 static irqreturn_t lis302dl_interrupt(int irq, void *dummy)
 {
@@ -147,10 +146,10 @@ static irqreturn_t lis302dl_interrupt(int irq, void *dummy)
         * the lid is closed. This leads to interrupts as soon as a little move
         * is done.
         */
-       atomic_inc(&adev.count);
+       atomic_inc(&lis3_dev.count);
 
-       wake_up_interruptible(&adev.misc_wait);
-       kill_fasync(&adev.async_queue, SIGIO, POLL_IN);
+       wake_up_interruptible(&lis3_dev.misc_wait);
+       kill_fasync(&lis3_dev.async_queue, SIGIO, POLL_IN);
        return IRQ_HANDLED;
 }
 
@@ -158,10 +157,10 @@ static int lis3lv02d_misc_open(struct inode *inode, struct file *file)
 {
        int ret;
 
-       if (test_and_set_bit(0, &adev.misc_opened))
+       if (test_and_set_bit(0, &lis3_dev.misc_opened))
                return -EBUSY; /* already open */
 
-       atomic_set(&adev.count, 0);
+       atomic_set(&lis3_dev.count, 0);
 
        /*
         * The sensor can generate interrupts for free-fall and direction
@@ -174,25 +173,22 @@ static int lis3lv02d_misc_open(struct inode *inode, struct file *file)
         * io-apic is not configurable (and generates a warning) but I keep it
         * in case of support for other hardware.
         */
-       ret = request_irq(adev.irq, lis302dl_interrupt, IRQF_TRIGGER_RISING,
-                         DRIVER_NAME, &adev);
+       ret = request_irq(lis3_dev.irq, lis302dl_interrupt, IRQF_TRIGGER_RISING,
+                         DRIVER_NAME, &lis3_dev);
 
        if (ret) {
-               clear_bit(0, &adev.misc_opened);
-               printk(KERN_ERR DRIVER_NAME ": IRQ%d allocation failed\n", adev.irq);
+               clear_bit(0, &lis3_dev.misc_opened);
+               printk(KERN_ERR DRIVER_NAME ": IRQ%d allocation failed\n", lis3_dev.irq);
                return -EBUSY;
        }
-       lis3lv02d_increase_use(&adev);
-       printk("lis3: registered interrupt %d\n", adev.irq);
        return 0;
 }
 
 static int lis3lv02d_misc_release(struct inode *inode, struct file *file)
 {
-       fasync_helper(-1, file, 0, &adev.async_queue);
-       lis3lv02d_decrease_use(&adev);
-       free_irq(adev.irq, &adev);
-       clear_bit(0, &adev.misc_opened); /* release the device */
+       fasync_helper(-1, file, 0, &lis3_dev.async_queue);
+       free_irq(lis3_dev.irq, &lis3_dev);
+       clear_bit(0, &lis3_dev.misc_opened); /* release the device */
        return 0;
 }
 
@@ -207,10 +203,10 @@ static ssize_t lis3lv02d_misc_read(struct file *file, char __user *buf,
        if (count < 1)
                return -EINVAL;
 
-       add_wait_queue(&adev.misc_wait, &wait);
+       add_wait_queue(&lis3_dev.misc_wait, &wait);
        while (true) {
                set_current_state(TASK_INTERRUPTIBLE);
-               data = atomic_xchg(&adev.count, 0);
+               data = atomic_xchg(&lis3_dev.count, 0);
                if (data)
                        break;
 
@@ -240,22 +236,22 @@ static ssize_t lis3lv02d_misc_read(struct file *file, char __user *buf,
 
 out:
        __set_current_state(TASK_RUNNING);
-       remove_wait_queue(&adev.misc_wait, &wait);
+       remove_wait_queue(&lis3_dev.misc_wait, &wait);
 
        return retval;
 }
 
 static unsigned int lis3lv02d_misc_poll(struct file *file, poll_table *wait)
 {
-       poll_wait(file, &adev.misc_wait, wait);
-       if (atomic_read(&adev.count))
+       poll_wait(file, &lis3_dev.misc_wait, wait);
+       if (atomic_read(&lis3_dev.count))
                return POLLIN | POLLRDNORM;
        return 0;
 }
 
 static int lis3lv02d_misc_fasync(int fd, struct file *file, int on)
 {
-       return fasync_helper(fd, file, on, &adev.async_queue);
+       return fasync_helper(fd, file, on, &lis3_dev.async_queue);
 }
 
 static const struct file_operations lis3lv02d_misc_fops = {
@@ -283,12 +279,12 @@ static int lis3lv02d_joystick_kthread(void *data)
        int x, y, z;
 
        while (!kthread_should_stop()) {
-               lis3lv02d_get_xyz(adev.device->handle, &x, &y, &z);
-               input_report_abs(adev.idev, ABS_X, x - adev.xcalib);
-               input_report_abs(adev.idev, ABS_Y, y - adev.ycalib);
-               input_report_abs(adev.idev, ABS_Z, z - adev.zcalib);
+               lis3lv02d_get_xyz(&lis3_dev, &x, &y, &z);
+               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);
 
-               input_sync(adev.idev);
+               input_sync(lis3_dev.idev);
 
                try_to_freeze();
                msleep_interruptible(MDPS_POLL_INTERVAL);
@@ -299,11 +295,9 @@ static int lis3lv02d_joystick_kthread(void *data)
 
 static int lis3lv02d_joystick_open(struct input_dev *input)
 {
-       lis3lv02d_increase_use(&adev);
-       adev.kthread = kthread_run(lis3lv02d_joystick_kthread, NULL, "klis3lv02d");
-       if (IS_ERR(adev.kthread)) {
-               lis3lv02d_decrease_use(&adev);
-               return PTR_ERR(adev.kthread);
+       lis3_dev.kthread = kthread_run(lis3lv02d_joystick_kthread, NULL, "klis3lv02d");
+       if (IS_ERR(lis3_dev.kthread)) {
+               return PTR_ERR(lis3_dev.kthread);
        }
 
        return 0;
@@ -311,45 +305,45 @@ static int lis3lv02d_joystick_open(struct input_dev *input)
 
 static void lis3lv02d_joystick_close(struct input_dev *input)
 {
-       kthread_stop(adev.kthread);
-       lis3lv02d_decrease_use(&adev);
+       kthread_stop(lis3_dev.kthread);
 }
 
 static inline void lis3lv02d_calibrate_joystick(void)
 {
-       lis3lv02d_get_xyz(adev.device->handle, &adev.xcalib, &adev.ycalib, &adev.zcalib);
+       lis3lv02d_get_xyz(&lis3_dev,
+               &lis3_dev.xcalib, &lis3_dev.ycalib, &lis3_dev.zcalib);
 }
 
 int lis3lv02d_joystick_enable(void)
 {
        int err;
 
-       if (adev.idev)
+       if (lis3_dev.idev)
                return -EINVAL;
 
-       adev.idev = input_allocate_device();
-       if (!adev.idev)
+       lis3_dev.idev = input_allocate_device();
+       if (!lis3_dev.idev)
                return -ENOMEM;
 
        lis3lv02d_calibrate_joystick();
 
-       adev.idev->name       = "ST LIS3LV02DL Accelerometer";
-       adev.idev->phys       = DRIVER_NAME "/input0";
-       adev.idev->id.bustype = BUS_HOST;
-       adev.idev->id.vendor  = 0;
-       adev.idev->dev.parent = &adev.pdev->dev;
-       adev.idev->open       = lis3lv02d_joystick_open;
-       adev.idev->close      = lis3lv02d_joystick_close;
+       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;
 
-       set_bit(EV_ABS, adev.idev->evbit);
-       input_set_abs_params(adev.idev, ABS_X, -adev.mdps_max_val, adev.mdps_max_val, 3, 3);
-       input_set_abs_params(adev.idev, ABS_Y, -adev.mdps_max_val, adev.mdps_max_val, 3, 3);
-       input_set_abs_params(adev.idev, ABS_Z, -adev.mdps_max_val, adev.mdps_max_val, 3, 3);
+       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);
 
-       err = input_register_device(adev.idev);
+       err = input_register_device(lis3_dev.idev);
        if (err) {
-               input_free_device(adev.idev);
-               adev.idev = NULL;
+               input_free_device(lis3_dev.idev);
+               lis3_dev.idev = NULL;
        }
 
        return err;
@@ -358,71 +352,37 @@ EXPORT_SYMBOL_GPL(lis3lv02d_joystick_enable);
 
 void lis3lv02d_joystick_disable(void)
 {
-       if (!adev.idev)
+       if (!lis3_dev.idev)
                return;
 
-       misc_deregister(&lis3lv02d_misc_device);
-       input_unregister_device(adev.idev);
-       adev.idev = NULL;
+       if (lis3_dev.irq)
+               misc_deregister(&lis3lv02d_misc_device);
+       input_unregister_device(lis3_dev.idev);
+       lis3_dev.idev = NULL;
 }
 EXPORT_SYMBOL_GPL(lis3lv02d_joystick_disable);
 
-/*
- * Initialise the accelerometer and the various subsystems.
- * Should be rather independant of the bus system.
- */
-int lis3lv02d_init_device(struct acpi_lis3lv02d *dev)
-{
-       mutex_init(&dev->lock);
-       lis3lv02d_add_fs(dev->device);
-       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);
-
-       /* if we did not get an IRQ from ACPI - we have nothing more to do */
-       if (!dev->irq) {
-               printk(KERN_ERR DRIVER_NAME
-                       ": No IRQ in ACPI. Disabling /dev/freefall\n");
-               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);
-
 /* Sysfs stuff */
 static ssize_t lis3lv02d_position_show(struct device *dev,
                                struct device_attribute *attr, char *buf)
 {
        int x, y, z;
 
-       lis3lv02d_increase_use(&adev);
-       lis3lv02d_get_xyz(adev.device->handle, &x, &y, &z);
-       lis3lv02d_decrease_use(&adev);
+       lis3lv02d_get_xyz(&lis3_dev, &x, &y, &z);
        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)
 {
-       return sprintf(buf, "(%d,%d,%d)\n", adev.xcalib, adev.ycalib, adev.zcalib);
+       return sprintf(buf, "(%d,%d,%d)\n", lis3_dev.xcalib, lis3_dev.ycalib, lis3_dev.zcalib);
 }
 
 static ssize_t lis3lv02d_calibrate_store(struct device *dev,
                                struct device_attribute *attr,
                                const char *buf, size_t count)
 {
-       lis3lv02d_increase_use(&adev);
        lis3lv02d_calibrate_joystick();
-       lis3lv02d_decrease_use(&adev);
        return count;
 }
 
@@ -434,9 +394,7 @@ static ssize_t lis3lv02d_rate_show(struct device *dev,
        u8 ctrl;
        int val;
 
-       lis3lv02d_increase_use(&adev);
-       adev.read(adev.device->handle, CTRL_REG1, &ctrl);
-       lis3lv02d_decrease_use(&adev);
+       lis3_dev.read(&lis3_dev, CTRL_REG1, &ctrl);
        val = (ctrl & (CTRL1_DF0 | CTRL1_DF1)) >> 4;
        return sprintf(buf, "%d\n", lis3lv02dl_df_val[val]);
 }
@@ -458,23 +416,68 @@ static struct attribute_group lis3lv02d_attribute_group = {
 };
 
 
-static int lis3lv02d_add_fs(struct acpi_device *device)
+static int lis3lv02d_add_fs(struct lis3lv02d *lis3)
 {
-       adev.pdev = platform_device_register_simple(DRIVER_NAME, -1, NULL, 0);
-       if (IS_ERR(adev.pdev))
-               return PTR_ERR(adev.pdev);
+       lis3->pdev = platform_device_register_simple(DRIVER_NAME, -1, NULL, 0);
+       if (IS_ERR(lis3->pdev))
+               return PTR_ERR(lis3->pdev);
 
-       return sysfs_create_group(&adev.pdev->dev.kobj, &lis3lv02d_attribute_group);
+       return sysfs_create_group(&lis3->pdev->dev.kobj, &lis3lv02d_attribute_group);
 }
 
-int lis3lv02d_remove_fs(void)
+int lis3lv02d_remove_fs(struct lis3lv02d *lis3)
 {
-       sysfs_remove_group(&adev.pdev->dev.kobj, &lis3lv02d_attribute_group);
-       platform_device_unregister(adev.pdev);
+       sysfs_remove_group(&lis3->pdev->dev.kobj, &lis3lv02d_attribute_group);
+       platform_device_unregister(lis3->pdev);
        return 0;
 }
 EXPORT_SYMBOL_GPL(lis3lv02d_remove_fs);
 
+/*
+ * Initialise the accelerometer and the various subsystems.
+ * Should be rather independant of the bus system.
+ */
+int lis3lv02d_init_device(struct lis3lv02d *dev)
+{
+       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", dev->whoami);
+               return -EINVAL;
+       }
+
+       lis3lv02d_add_fs(dev);
+       lis3lv02d_poweron(dev);
+
+       if (lis3lv02d_joystick_enable())
+               printk(KERN_ERR DRIVER_NAME ": joystick initialization failed\n");
+
+       /* bail if we did not get an IRQ from the bus layer */
+       if (!dev->irq) {
+               printk(KERN_ERR DRIVER_NAME
+                       ": No IRQ. Disabling /dev/freefall\n");
+               goto out;
+       }
+
+       if (misc_register(&lis3lv02d_misc_device))
+               printk(KERN_ERR DRIVER_NAME ": misc_register failed\n");
+out:
+       return 0;
+}
+EXPORT_SYMBOL_GPL(lis3lv02d_init_device);
+
 MODULE_DESCRIPTION("ST LIS3LV02Dx three-axis digital accelerometer driver");
 MODULE_AUTHOR("Yan Burman, Eric Piel, Pavel Machek");
 MODULE_LICENSE("GPL");