Re: Accelerometer driver for HP laptops

From: Pavel Machek
Date: Wed Oct 15 2008 - 06:58:19 EST


Hi!

> > I took a look at the driver, and it seems to work for me... I'd like
> > to get it to the mainline...
> >
> >>>> Attached is our work-in-progress version of the driver for a fourth take
> >>>> on the LKML (it used to be mdps). It works really fine, with lots of
> >>>> "goodies" (like automatically convert the axes to fit to a standard,
> >>>> automatic power-off of the device when not in used) excepted the
> >>>> interrupt support for free-fall detection.
> >
> > I wonder if mdps was really a better name? Driver is HP-specific
> > (because of the ACPI usage), not really chip specific... and lis3lv02d
> > looks like an alphabet soup to me...
> >
> > Ok, it is a bit chip-specific, too...
> Yes, the point is that several people got interested in adding to the
> driver SPI and I?C support last time I submitted the driver. I did my
> best to concentrate the ACPI stuff only to some 3 or 4 small functions,
> so that it can be be easily done.

Yes, I seen that. Nice.

> I've got already a changelog ready. Give me a couple of days to clean
> things up and I'll send it to akpm myself :-)

Ok, I'd like to help with cleaning... let me know if I can help some more.

* whitelist hp2133

* the driver has different name now.

* remove /** from comments that are not linuxdoc compliant.

* remove /dev/accel. We can reintroduce it when it works

Signed-off-by: Pavel Machek <pavel@xxxxxxx>
Pavel

diff --git a/drivers/hwmon/lis3lv02d.c b/drivers/hwmon/lis3lv02d.c
index 3cdfb52..1d6ca17 100644
--- a/drivers/hwmon/lis3lv02d.c
+++ b/drivers/hwmon/lis3lv02d.c
@@ -1,5 +1,6 @@
/*
- * mdps.c - ST LIS3LV02DL accelerometer driver
+ * lis3lv02d.c - ST LIS3LV02DL accelerometer driver used for HDD protection
+ * in HP notebooks
*
* Copyright (C) 2007-2008 Yan Burman
* Copyright (C) 2008 Eric Piel
@@ -106,7 +107,7 @@ static struct acpi_device_id lis3lv02d_d
};
MODULE_DEVICE_TABLE(acpi, lis3lv02d_device_ids);

-/**
+/*
* acpi_init - ACPI _INI method: initialize the device.
* @handle the handle of the device
*
@@ -161,7 +162,7 @@ static acpi_status lis3lv02d_acpi_write(
return acpi_evaluate_integer(handle, "ALWR", &args, &ret);
}

-/**
+/*
* Write a 16 bit word on a pair of registers
* @handle the handle of the device
* @reg the low register to write to
@@ -187,15 +188,8 @@ static s16 lis3lv02d_read_16(acpi_handle
return (s16)((hi << 8) | lo);
}

-static void lis3lv02d_display_reg(int reg)
-{
- u8 val;
- lis3lv02d_acpi_read(adev.device->handle, reg, &val);
- printk(DRIVER_NAME " reg %x : %.2x\n", reg, val);
-}
-
/**
- * For the given axis, give the value converted
+ * lis3lv02d_get_axis For the given axis, give the value converted
* @param axis 1,2,3 - can also be negative
* @param hw_values raw values returned by the hardware
*
@@ -277,12 +271,6 @@ #endif
static int lis3lv02d_resume(struct acpi_device *device)
{
/* make sure the device went online */
- // TODO: check if the device could be automatically turned on by acpi
- // If so, put the power_off timer back
-// down(&adev.poff_sem);
-// if (adev.usage > 0)
-// lis3lv02d_poweron(device->handle);
-// up(&adev.poff_sem);
printk(KERN_INFO DRIVER_NAME " Resuming device\n");
return 0;
}
@@ -326,192 +314,6 @@ static void lis3lv02d_poweroff_timeout(u
printk(KERN_DEBUG DRIVER_NAME ": Turning off the device\n");
}

-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.
- */
- atomic_inc(&adev.count);
-
- wake_up_interruptible(&adev.misc_wait);
- kill_fasync(&adev.async_queue, SIGIO, POLL_IN);
-
-// //lis3lv02d_display_reg(STATUS_REG);
-// lis3lv02d_display_reg(FF_WU_SRC);
-// lis3lv02d_display_reg(DD_SRC);
-// //lis3lv02d_display_reg(CTRL_REG1);
-// //lis3lv02d_display_reg(CTRL_REG2);
-// //lis3lv02d_display_reg(CTRL_REG3);
-// lis3lv02d_display_reg(FF_WU_ACK);
-// lis3lv02d_display_reg(FF_WU_CFG);
-// lis3lv02d_display_reg(DD_CFG);
-// //lis3lv02d_display_reg(FF_WU_THS_L);
-// //lis3lv02d_display_reg(FF_WU_THS_H);
-// //lis3lv02d_display_reg(FF_WU_DURATION);
-// //lis3lv02d_acpi_write(adev.device->handle, FF_WU_SRC, 0);
-// printk(KERN_DEBUG DRIVER_NAME ": irq received\n");
-
- return IRQ_HANDLED;
-}
-
-static int lis3lv02d_misc_open(struct inode *inode, struct file *file)
-{
- int ret;
-
- if (test_and_set_bit(0, &adev.misc_opened))
- return -EBUSY; /* already open */
-
- atomic_set(&adev.count, 0);
-
- /*
- * 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.
- */
- ret = request_irq(adev.irq, lis302dl_interrupt, IRQF_TRIGGER_RISING,
- DRIVER_NAME, &adev);
- if (ret) {
- clear_bit(0, &adev.misc_opened);
- printk(KERN_ERR DRIVER_NAME ": IRQ%d allocation failed\n", adev.irq);
- return -EBUSY;
- }
- lis3lv02d_increase_use(&adev);
- // TODO: set up the registers correctly
-// lis3lv02d_display_reg(STATUS_REG);
-// lis3lv02d_display_reg(CTRL_REG1);
-// lis3lv02d_display_reg(CTRL_REG2);
-// lis3lv02d_display_reg(CTRL_REG3);
- lis3lv02d_display_reg(FF_WU_CFG);
-// lis3lv02d_display_reg(FF_WU_SRC);
-// lis3lv02d_display_reg(FF_WU_THS_L);
-// lis3lv02d_display_reg(FF_WU_THS_H);
-// lis3lv02d_display_reg(FF_WU_DURATION);
-// lis3lv02d_display_reg(DD_CFG);
-// lis3lv02d_display_reg(DD_SRC);
-// lis3lv02d_display_reg(DD_THSI_L);
-// lis3lv02d_display_reg(DD_THSI_H);
-// lis3lv02d_display_reg(DD_THSE_L);
-// lis3lv02d_display_reg(DD_THSE_H);
-// lis3lv02d_acpi_write(adev.device->handle, FF_WU_SRC, 0);
- /* Threshold not too big (10) */
-// lis3lv02d_write_16(adev.device->handle, FF_WU_THS_L, 10);
-// /* 2 samples in a row before activation */
-// lis3lv02d_acpi_write(adev.device->handle, FF_WU_DURATION, 2);
-// /* detect every direction, don't wait for validation */
- lis3lv02d_acpi_write(adev.device->handle, FF_WU_CFG, 0);
-// lis3lv02d_acpi_write(adev.device->handle, FF_WU_CFG, FF_WU_CFG_XLIE | FF_WU_CFG_XHIE
-// | FF_WU_CFG_YLIE | FF_WU_CFG_YHIE
-// | FF_WU_CFG_ZLIE | FF_WU_CFG_ZHIE);
- // TODO after turning on, this generates one useless interrupt just after set up, it shouldn't be passed to userspace
-// lis3lv02d_display_reg(STATUS_REG);
-// lis3lv02d_display_reg(CTRL_REG1);
-// lis3lv02d_display_reg(CTRL_REG2);
-// lis3lv02d_display_reg(CTRL_REG3);
- lis3lv02d_display_reg(FF_WU_CFG);
-// lis3lv02d_display_reg(FF_WU_SRC);
-// lis3lv02d_display_reg(FF_WU_THS_L);
-// lis3lv02d_display_reg(FF_WU_THS_H);
-// lis3lv02d_display_reg(FF_WU_DURATION);
-// lis3lv02d_display_reg(DD_CFG);
-// lis3lv02d_display_reg(DD_SRC);
-// lis3lv02d_display_reg(DD_THSI_L);
-// lis3lv02d_display_reg(DD_THSI_H);
-// lis3lv02d_display_reg(DD_THSE_L);
-// lis3lv02d_display_reg(DD_THSE_H);
-
- return 0;
-}
-
-static int lis3lv02d_misc_release(struct inode *inode, struct file *file)
-{
- fasync_helper(-1, file, 0, &adev.async_queue);
- lis3lv02d_acpi_write(adev.device->handle, FF_WU_CFG, 0);
- lis3lv02d_decrease_use(&adev);
- free_irq(adev.irq, &adev);
- clear_bit(0, &adev.misc_opened); /* release the device */
- 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;
- ssize_t retval = count;
-
- if (count != sizeof(u32))
- return -EINVAL;
-
- add_wait_queue(&adev.misc_wait, &wait);
- while (true) {
- set_current_state(TASK_INTERRUPTIBLE);
- data = atomic_xchg(&adev.count, 0);
- if (data)
- break;
-
- if (file->f_flags & O_NONBLOCK) {
- retval = -EAGAIN;
- goto out;
- }
-
- if (signal_pending(current)) {
- retval = -ERESTARTSYS;
- goto out;
- }
-
- schedule();
- }
-
- /* make sure we are not going into copy_to_user() with
- * TASK_INTERRUPTIBLE state */
- set_current_state(TASK_RUNNING);
- if (copy_to_user(buf, &data, sizeof(data)))
- retval = -EFAULT;
-
-out:
- __set_current_state(TASK_RUNNING);
- remove_wait_queue(&adev.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))
- 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);
-}
-
-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 = "accel",
- .fops = &lis3lv02d_misc_fops,
-};
-
/**
* lis3lv02d_joystick_kthread - Kthread polling function
* @param data unused - here to conform to threadfn prototype
@@ -644,16 +446,6 @@ static int lis3lv02d_init_device(struct
/* obtain IRQ number of our device from ACPI */
lis3lv02d_enum_resources(dev->device);

- /* 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/accel\n");
- goto out;
- }
-
- if (misc_register(&lis3lv02d_misc_device))
- printk(KERN_ERR DRIVER_NAME ": misc_register failed\n");
-out:
lis3lv02d_decrease_use(dev);
return 0;
}
@@ -673,6 +465,7 @@ static struct axis_conversion lis3lv02d_
static struct axis_conversion lis3lv02d_axis_x_inverted = {-1, 2, 3};
static struct axis_conversion lis3lv02d_axis_z_inverted = {1, 2, -3};
static struct axis_conversion lis3lv02d_axis_xy_swap_inverted = {-2, -1, 3};
+static struct axis_conversion lis3lv02d_axis_xy_swap_x_inverted = {-2, 1, 3};

#define AXIS_DMI_MATCH(_ident, _name, _axis) { \
.ident = _ident, \
@@ -690,6 +483,7 @@ static struct dmi_system_id lis3lv02d_dm
AXIS_DMI_MATCH("NW9440", "HP Compaq nw9440", x_inverted),
AXIS_DMI_MATCH("NC2510", "HP Compaq 2510", y_inverted),
AXIS_DMI_MATCH("NC8510", "HP Compaq 8510", xy_swap_inverted),
+ AXIS_DMI_MATCH("HP2133", "HP 2133", xy_swap_x_inverted ),
{ NULL, }
/* Laptop models without axis info (yet):
* "NC651xx" "HP Compaq 651"
@@ -738,7 +532,6 @@ static int lis3lv02d_remove(struct acpi_
if (!device)
return -EINVAL;

- misc_deregister(&lis3lv02d_misc_device);
lis3lv02d_joystick_disable();
del_timer(&adev.poff_timer);
lis3lv02d_poweroff(device->handle);


--
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/