From d0c3a8bdfe2722f00b5ec7812500fbf1f6229b9f Mon Sep 17 00:00:00 2001 From: Andy Green <andy@openmoko.com> Date: Fri, 25 Jul 2008 23:06:16 +0100 Subject: [PATCH] fix-lis302dl-resume-and-init-reload-boot-coefficients.patch Reported-by: John Lee <john_lee@openmoko.com> We don't reset the devices either at init or resume, where init means use the BOOT bit to reload device calibration coefficients from internal EEPROM. John Lee saw brain-damaged behaviour after resume and sometimes after boot (since it may not have lost power to force a BOOT itself that makes sense). This patch - adds a diagnostic dump feature down /sys - forces BOOT action on init and resume, and waits for completion - makes sure XYZ capture is enabled on resume - adds some constants in the .h and removes some magic numbers in the code by using them Signed-off-by: Andy Green <andy@openmoko.com> --- drivers/input/misc/lis302dl.c | 78 +++++++++++++++++++++++++++++++++++++++-- include/linux/lis302dl.h | 9 +++++ 2 files changed, 84 insertions(+), 3 deletions(-) diff --git a/drivers/input/misc/lis302dl.c b/drivers/input/misc/lis302dl.c index 1d19418..553a731 100644 --- a/drivers/input/misc/lis302dl.c +++ b/drivers/input/misc/lis302dl.c @@ -221,9 +221,37 @@ static ssize_t set_scale(struct device *dev, struct device_attribute *attr, static DEVICE_ATTR(full_scale, S_IRUGO | S_IWUSR, show_scale, set_scale); +static ssize_t lis302dl_dump(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct lis302dl_info *lis = dev_get_drvdata(dev); + int n = 0; + u8 reg[0x40]; + char *end = buf; + unsigned long flags; + + local_save_flags(flags); + + for (n = 0; n < sizeof(reg); n++) + reg[n] = reg_read(lis, n); + + local_irq_restore(flags); + + for (n = 0; n < sizeof(reg); n += 16) { + hex_dump_to_buffer(reg + n, 16, 16, 1, end, 128, 0); + end += strlen(end); + *end++ = '\n'; + *end++ = '\0'; + } + + return end - buf; +} +static DEVICE_ATTR(dump, S_IRUGO, lis302dl_dump, NULL); + static struct attribute *lis302dl_sysfs_entries[] = { &dev_attr_sample_rate.attr, &dev_attr_full_scale.attr, + &dev_attr_dump.attr, NULL }; @@ -276,6 +304,24 @@ static void lis302dl_input_close(struct input_dev *inp) local_irq_restore(flags); } +/* get the device to reload its coefficients from EEPROM and wait for it + * to complete + */ + +static int __lis302dl_reset_device(struct lis302dl_info *lis) +{ + int timeout = 10; + + reg_write(lis, LIS302DL_REG_CTRL2, LIS302DL_CTRL2_BOOT | + LIS302DL_CTRL2_FDS); + + while ((reg_read(lis, LIS302DL_REG_CTRL2) & LIS302DL_CTRL2_BOOT) && + (timeout--)) + mdelay(1); + + return !!(timeout < 0); +} + static int __devinit lis302dl_probe(struct spi_device *spi) { int rc; @@ -347,12 +393,24 @@ static int __devinit lis302dl_probe(struct spi_device *spi) goto bail_inp_dev; } - reg_write(lis, LIS302DL_REG_CTRL1, 0x47); - reg_write(lis, LIS302DL_REG_CTRL3, 0xc0); + if (__lis302dl_reset_device(lis)) + dev_err(&spi->dev, "device BOOT reload failed\n"); + + /* force us powered */ + reg_write(lis, LIS302DL_REG_CTRL1, LIS302DL_CTRL1_PD | + LIS302DL_CTRL1_Xen | + LIS302DL_CTRL1_Yen | + LIS302DL_CTRL1_Zen); + mdelay(1); + + reg_write(lis, LIS302DL_REG_CTRL2, 0); + reg_write(lis, LIS302DL_REG_CTRL3, LIS302DL_CTRL3_PP_OD | + LIS302DL_CTRL3_IHL); reg_write(lis, LIS302DL_REG_FF_WU_THS_1, 0x14); reg_write(lis, LIS302DL_REG_FF_WU_DURATION_1, 0x00); reg_write(lis, LIS302DL_REG_FF_WU_CFG_1, 0x95); + /* start off in powered down mode; we power up when someone opens us */ reg_write(lis, LIS302DL_REG_CTRL1, LIS302DL_CTRL1_Xen | LIS302DL_CTRL1_Yen | LIS302DL_CTRL1_Zen); @@ -494,8 +552,22 @@ static int lis302dl_resume(struct spi_device *spi) /* get our IO to the device back in operational states */ (lis->pdata->lis302dl_suspend_io)(lis, 1); + /* resume from powerdown first! */ + reg_write(lis, LIS302DL_REG_CTRL1, LIS302DL_CTRL1_PD | + LIS302DL_CTRL1_Xen | + LIS302DL_CTRL1_Yen | + LIS302DL_CTRL1_Zen); + mdelay(1); + + if (__lis302dl_reset_device(lis)) + dev_err(&spi->dev, "device BOOT reload failed\n"); + /* restore registers after resume */ - reg_write(lis, LIS302DL_REG_CTRL1, lis->regs[LIS302DL_REG_CTRL1]); + reg_write(lis, LIS302DL_REG_CTRL1, lis->regs[LIS302DL_REG_CTRL1] | + LIS302DL_CTRL1_PD | + LIS302DL_CTRL1_Xen | + LIS302DL_CTRL1_Yen | + LIS302DL_CTRL1_Zen); reg_write(lis, LIS302DL_REG_CTRL2, lis->regs[LIS302DL_REG_CTRL2]); reg_write(lis, LIS302DL_REG_CTRL3, lis->regs[LIS302DL_REG_CTRL3]); reg_write(lis, LIS302DL_REG_FF_WU_CFG_1, diff --git a/include/linux/lis302dl.h b/include/linux/lis302dl.h index 0d6b4c4..4d8ded9 100644 --- a/include/linux/lis302dl.h +++ b/include/linux/lis302dl.h @@ -66,6 +66,15 @@ enum lis302dl_reg_ctrl1 { LIS302DL_CTRL1_DR = 0x80, }; +enum lis302dl_reg_ctrl2 { + LIS302DL_CTRL2_HPC1 = 0x01, + LIS302DL_CTRL2_HPC2 = 0x02, + LIS302DL_CTRL2_HPFF1 = 0x04, + LIS302DL_CTRL2_HPFF2 = 0x08, + LIS302DL_CTRL2_FDS = 0x10, + LIS302DL_CTRL2_BOOT = 0x40, + LIS302DL_CTRL2_SIM = 0x80, +}; enum lis302dl_reg_ctrl3 { LIS302DL_CTRL3_PP_OD = 0x40, LIS302DL_CTRL3_IHL = 0x80, -- 1.5.6.3