Path: blob/master/drivers/misc/lis3lv02d/lis3lv02d.c
15111 views
/*1* lis3lv02d.c - ST LIS3LV02DL accelerometer driver2*3* Copyright (C) 2007-2008 Yan Burman4* Copyright (C) 2008 Eric Piel5* Copyright (C) 2008-2009 Pavel Machek6*7* This program is free software; you can redistribute it and/or modify8* it under the terms of the GNU General Public License as published by9* the Free Software Foundation; either version 2 of the License, or10* (at your option) any later version.11*12* This program is distributed in the hope that it will be useful,13* but WITHOUT ANY WARRANTY; without even the implied warranty of14* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the15* GNU General Public License for more details.16*17* You should have received a copy of the GNU General Public License18* along with this program; if not, write to the Free Software19* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA20*/2122#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt2324#include <linux/kernel.h>25#include <linux/init.h>26#include <linux/dmi.h>27#include <linux/module.h>28#include <linux/types.h>29#include <linux/platform_device.h>30#include <linux/interrupt.h>31#include <linux/input-polldev.h>32#include <linux/delay.h>33#include <linux/wait.h>34#include <linux/poll.h>35#include <linux/slab.h>36#include <linux/freezer.h>37#include <linux/uaccess.h>38#include <linux/miscdevice.h>39#include <linux/pm_runtime.h>40#include <linux/atomic.h>41#include "lis3lv02d.h"4243#define DRIVER_NAME "lis3lv02d"4445/* joystick device poll interval in milliseconds */46#define MDPS_POLL_INTERVAL 5047#define MDPS_POLL_MIN 048#define MDPS_POLL_MAX 20004950#define LIS3_SYSFS_POWERDOWN_DELAY 5000 /* In milliseconds */5152#define SELFTEST_OK 053#define SELFTEST_FAIL -154#define SELFTEST_IRQ -25556#define IRQ_LINE0 057#define IRQ_LINE1 15859/*60* The sensor can also generate interrupts (DRDY) but it's pretty pointless61* because they are generated even if the data do not change. So it's better62* to keep the interrupt for the free-fall event. The values are updated at63* 40Hz (at the lowest frequency), but as it can be pretty time consuming on64* some low processor, we poll the sensor only at 20Hz... enough for the65* joystick.66*/6768#define LIS3_PWRON_DELAY_WAI_12B (5000)69#define LIS3_PWRON_DELAY_WAI_8B (3000)7071/*72* LIS3LV02D spec says 1024 LSBs corresponds 1 G -> 1LSB is 1000/1024 mG73* LIS302D spec says: 18 mG / digit74* LIS3_ACCURACY is used to increase accuracy of the intermediate75* calculation results.76*/77#define LIS3_ACCURACY 102478/* Sensitivity values for -2G +2G scale */79#define LIS3_SENSITIVITY_12B ((LIS3_ACCURACY * 1000) / 1024)80#define LIS3_SENSITIVITY_8B (18 * LIS3_ACCURACY)8182#define LIS3_DEFAULT_FUZZ_12B 383#define LIS3_DEFAULT_FLAT_12B 384#define LIS3_DEFAULT_FUZZ_8B 185#define LIS3_DEFAULT_FLAT_8B 18687struct lis3lv02d lis3_dev = {88.misc_wait = __WAIT_QUEUE_HEAD_INITIALIZER(lis3_dev.misc_wait),89};90EXPORT_SYMBOL_GPL(lis3_dev);9192/* just like param_set_int() but does sanity-check so that it won't point93* over the axis array size94*/95static int param_set_axis(const char *val, const struct kernel_param *kp)96{97int ret = param_set_int(val, kp);98if (!ret) {99int val = *(int *)kp->arg;100if (val < 0)101val = -val;102if (!val || val > 3)103return -EINVAL;104}105return ret;106}107108static struct kernel_param_ops param_ops_axis = {109.set = param_set_axis,110.get = param_get_int,111};112113module_param_array_named(axes, lis3_dev.ac.as_array, axis, NULL, 0644);114MODULE_PARM_DESC(axes, "Axis-mapping for x,y,z directions");115116static s16 lis3lv02d_read_8(struct lis3lv02d *lis3, int reg)117{118s8 lo;119if (lis3->read(lis3, reg, &lo) < 0)120return 0;121122return lo;123}124125static s16 lis3lv02d_read_12(struct lis3lv02d *lis3, int reg)126{127u8 lo, hi;128129lis3->read(lis3, reg - 1, &lo);130lis3->read(lis3, reg, &hi);131/* In "12 bit right justified" mode, bit 6, bit 7, bit 8 = bit 5 */132return (s16)((hi << 8) | lo);133}134135/**136* lis3lv02d_get_axis - For the given axis, give the value converted137* @axis: 1,2,3 - can also be negative138* @hw_values: raw values returned by the hardware139*140* Returns the converted value.141*/142static inline int lis3lv02d_get_axis(s8 axis, int hw_values[3])143{144if (axis > 0)145return hw_values[axis - 1];146else147return -hw_values[-axis - 1];148}149150/**151* lis3lv02d_get_xyz - Get X, Y and Z axis values from the accelerometer152* @lis3: pointer to the device struct153* @x: where to store the X axis value154* @y: where to store the Y axis value155* @z: where to store the Z axis value156*157* Note that 40Hz input device can eat up about 10% CPU at 800MHZ158*/159static void lis3lv02d_get_xyz(struct lis3lv02d *lis3, int *x, int *y, int *z)160{161int position[3];162int i;163164if (lis3->blkread) {165if (lis3_dev.whoami == WAI_12B) {166u16 data[3];167lis3->blkread(lis3, OUTX_L, 6, (u8 *)data);168for (i = 0; i < 3; i++)169position[i] = (s16)le16_to_cpu(data[i]);170} else {171u8 data[5];172/* Data: x, dummy, y, dummy, z */173lis3->blkread(lis3, OUTX, 5, data);174for (i = 0; i < 3; i++)175position[i] = (s8)data[i * 2];176}177} else {178position[0] = lis3->read_data(lis3, OUTX);179position[1] = lis3->read_data(lis3, OUTY);180position[2] = lis3->read_data(lis3, OUTZ);181}182183for (i = 0; i < 3; i++)184position[i] = (position[i] * lis3->scale) / LIS3_ACCURACY;185186*x = lis3lv02d_get_axis(lis3->ac.x, position);187*y = lis3lv02d_get_axis(lis3->ac.y, position);188*z = lis3lv02d_get_axis(lis3->ac.z, position);189}190191/* conversion btw sampling rate and the register values */192static int lis3_12_rates[4] = {40, 160, 640, 2560};193static int lis3_8_rates[2] = {100, 400};194static int lis3_3dc_rates[16] = {0, 1, 10, 25, 50, 100, 200, 400, 1600, 5000};195196/* ODR is Output Data Rate */197static int lis3lv02d_get_odr(void)198{199u8 ctrl;200int shift;201202lis3_dev.read(&lis3_dev, CTRL_REG1, &ctrl);203ctrl &= lis3_dev.odr_mask;204shift = ffs(lis3_dev.odr_mask) - 1;205return lis3_dev.odrs[(ctrl >> shift)];206}207208static int lis3lv02d_set_odr(int rate)209{210u8 ctrl;211int i, len, shift;212213if (!rate)214return -EINVAL;215216lis3_dev.read(&lis3_dev, CTRL_REG1, &ctrl);217ctrl &= ~lis3_dev.odr_mask;218len = 1 << hweight_long(lis3_dev.odr_mask); /* # of possible values */219shift = ffs(lis3_dev.odr_mask) - 1;220221for (i = 0; i < len; i++)222if (lis3_dev.odrs[i] == rate) {223lis3_dev.write(&lis3_dev, CTRL_REG1,224ctrl | (i << shift));225return 0;226}227return -EINVAL;228}229230static int lis3lv02d_selftest(struct lis3lv02d *lis3, s16 results[3])231{232u8 ctlreg, reg;233s16 x, y, z;234u8 selftest;235int ret;236u8 ctrl_reg_data;237unsigned char irq_cfg;238239mutex_lock(&lis3->mutex);240241irq_cfg = lis3->irq_cfg;242if (lis3_dev.whoami == WAI_8B) {243lis3->data_ready_count[IRQ_LINE0] = 0;244lis3->data_ready_count[IRQ_LINE1] = 0;245246/* Change interrupt cfg to data ready for selftest */247atomic_inc(&lis3_dev.wake_thread);248lis3->irq_cfg = LIS3_IRQ1_DATA_READY | LIS3_IRQ2_DATA_READY;249lis3->read(lis3, CTRL_REG3, &ctrl_reg_data);250lis3->write(lis3, CTRL_REG3, (ctrl_reg_data &251~(LIS3_IRQ1_MASK | LIS3_IRQ2_MASK)) |252(LIS3_IRQ1_DATA_READY | LIS3_IRQ2_DATA_READY));253}254255if (lis3_dev.whoami == WAI_3DC) {256ctlreg = CTRL_REG4;257selftest = CTRL4_ST0;258} else {259ctlreg = CTRL_REG1;260if (lis3_dev.whoami == WAI_12B)261selftest = CTRL1_ST;262else263selftest = CTRL1_STP;264}265266lis3->read(lis3, ctlreg, ®);267lis3->write(lis3, ctlreg, (reg | selftest));268msleep(lis3->pwron_delay / lis3lv02d_get_odr());269270/* Read directly to avoid axis remap */271x = lis3->read_data(lis3, OUTX);272y = lis3->read_data(lis3, OUTY);273z = lis3->read_data(lis3, OUTZ);274275/* back to normal settings */276lis3->write(lis3, ctlreg, reg);277msleep(lis3->pwron_delay / lis3lv02d_get_odr());278279results[0] = x - lis3->read_data(lis3, OUTX);280results[1] = y - lis3->read_data(lis3, OUTY);281results[2] = z - lis3->read_data(lis3, OUTZ);282283ret = 0;284285if (lis3_dev.whoami == WAI_8B) {286/* Restore original interrupt configuration */287atomic_dec(&lis3_dev.wake_thread);288lis3->write(lis3, CTRL_REG3, ctrl_reg_data);289lis3->irq_cfg = irq_cfg;290291if ((irq_cfg & LIS3_IRQ1_MASK) &&292lis3->data_ready_count[IRQ_LINE0] < 2) {293ret = SELFTEST_IRQ;294goto fail;295}296297if ((irq_cfg & LIS3_IRQ2_MASK) &&298lis3->data_ready_count[IRQ_LINE1] < 2) {299ret = SELFTEST_IRQ;300goto fail;301}302}303304if (lis3->pdata) {305int i;306for (i = 0; i < 3; i++) {307/* Check against selftest acceptance limits */308if ((results[i] < lis3->pdata->st_min_limits[i]) ||309(results[i] > lis3->pdata->st_max_limits[i])) {310ret = SELFTEST_FAIL;311goto fail;312}313}314}315316/* test passed */317fail:318mutex_unlock(&lis3->mutex);319return ret;320}321322/*323* Order of registers in the list affects to order of the restore process.324* Perhaps it is a good idea to set interrupt enable register as a last one325* after all other configurations326*/327static u8 lis3_wai8_regs[] = { FF_WU_CFG_1, FF_WU_THS_1, FF_WU_DURATION_1,328FF_WU_CFG_2, FF_WU_THS_2, FF_WU_DURATION_2,329CLICK_CFG, CLICK_SRC, CLICK_THSY_X, CLICK_THSZ,330CLICK_TIMELIMIT, CLICK_LATENCY, CLICK_WINDOW,331CTRL_REG1, CTRL_REG2, CTRL_REG3};332333static u8 lis3_wai12_regs[] = {FF_WU_CFG, FF_WU_THS_L, FF_WU_THS_H,334FF_WU_DURATION, DD_CFG, DD_THSI_L, DD_THSI_H,335DD_THSE_L, DD_THSE_H,336CTRL_REG1, CTRL_REG3, CTRL_REG2};337338static inline void lis3_context_save(struct lis3lv02d *lis3)339{340int i;341for (i = 0; i < lis3->regs_size; i++)342lis3->read(lis3, lis3->regs[i], &lis3->reg_cache[i]);343lis3->regs_stored = true;344}345346static inline void lis3_context_restore(struct lis3lv02d *lis3)347{348int i;349if (lis3->regs_stored)350for (i = 0; i < lis3->regs_size; i++)351lis3->write(lis3, lis3->regs[i], lis3->reg_cache[i]);352}353354void lis3lv02d_poweroff(struct lis3lv02d *lis3)355{356if (lis3->reg_ctrl)357lis3_context_save(lis3);358/* disable X,Y,Z axis and power down */359lis3->write(lis3, CTRL_REG1, 0x00);360if (lis3->reg_ctrl)361lis3->reg_ctrl(lis3, LIS3_REG_OFF);362}363EXPORT_SYMBOL_GPL(lis3lv02d_poweroff);364365void lis3lv02d_poweron(struct lis3lv02d *lis3)366{367u8 reg;368369lis3->init(lis3);370371/*372* Common configuration373* BDU: (12 bits sensors only) LSB and MSB values are not updated until374* both have been read. So the value read will always be correct.375* Set BOOT bit to refresh factory tuning values.376*/377lis3->read(lis3, CTRL_REG2, ®);378if (lis3->whoami == WAI_12B)379reg |= CTRL2_BDU | CTRL2_BOOT;380else381reg |= CTRL2_BOOT_8B;382lis3->write(lis3, CTRL_REG2, reg);383384/* LIS3 power on delay is quite long */385msleep(lis3->pwron_delay / lis3lv02d_get_odr());386387if (lis3->reg_ctrl)388lis3_context_restore(lis3);389}390EXPORT_SYMBOL_GPL(lis3lv02d_poweron);391392393static void lis3lv02d_joystick_poll(struct input_polled_dev *pidev)394{395int x, y, z;396397mutex_lock(&lis3_dev.mutex);398lis3lv02d_get_xyz(&lis3_dev, &x, &y, &z);399input_report_abs(pidev->input, ABS_X, x);400input_report_abs(pidev->input, ABS_Y, y);401input_report_abs(pidev->input, ABS_Z, z);402input_sync(pidev->input);403mutex_unlock(&lis3_dev.mutex);404}405406static void lis3lv02d_joystick_open(struct input_polled_dev *pidev)407{408if (lis3_dev.pm_dev)409pm_runtime_get_sync(lis3_dev.pm_dev);410411if (lis3_dev.pdata && lis3_dev.whoami == WAI_8B && lis3_dev.idev)412atomic_set(&lis3_dev.wake_thread, 1);413/*414* Update coordinates for the case where poll interval is 0 and415* the chip in running purely under interrupt control416*/417lis3lv02d_joystick_poll(pidev);418}419420static void lis3lv02d_joystick_close(struct input_polled_dev *pidev)421{422atomic_set(&lis3_dev.wake_thread, 0);423if (lis3_dev.pm_dev)424pm_runtime_put(lis3_dev.pm_dev);425}426427static irqreturn_t lis302dl_interrupt(int irq, void *dummy)428{429if (!test_bit(0, &lis3_dev.misc_opened))430goto out;431432/*433* Be careful: on some HP laptops the bios force DD when on battery and434* the lid is closed. This leads to interrupts as soon as a little move435* is done.436*/437atomic_inc(&lis3_dev.count);438439wake_up_interruptible(&lis3_dev.misc_wait);440kill_fasync(&lis3_dev.async_queue, SIGIO, POLL_IN);441out:442if (atomic_read(&lis3_dev.wake_thread))443return IRQ_WAKE_THREAD;444return IRQ_HANDLED;445}446447static void lis302dl_interrupt_handle_click(struct lis3lv02d *lis3)448{449struct input_dev *dev = lis3->idev->input;450u8 click_src;451452mutex_lock(&lis3->mutex);453lis3->read(lis3, CLICK_SRC, &click_src);454455if (click_src & CLICK_SINGLE_X) {456input_report_key(dev, lis3->mapped_btns[0], 1);457input_report_key(dev, lis3->mapped_btns[0], 0);458}459460if (click_src & CLICK_SINGLE_Y) {461input_report_key(dev, lis3->mapped_btns[1], 1);462input_report_key(dev, lis3->mapped_btns[1], 0);463}464465if (click_src & CLICK_SINGLE_Z) {466input_report_key(dev, lis3->mapped_btns[2], 1);467input_report_key(dev, lis3->mapped_btns[2], 0);468}469input_sync(dev);470mutex_unlock(&lis3->mutex);471}472473static inline void lis302dl_data_ready(struct lis3lv02d *lis3, int index)474{475int dummy;476477/* Dummy read to ack interrupt */478lis3lv02d_get_xyz(lis3, &dummy, &dummy, &dummy);479lis3->data_ready_count[index]++;480}481482static irqreturn_t lis302dl_interrupt_thread1_8b(int irq, void *data)483{484struct lis3lv02d *lis3 = data;485u8 irq_cfg = lis3->irq_cfg & LIS3_IRQ1_MASK;486487if (irq_cfg == LIS3_IRQ1_CLICK)488lis302dl_interrupt_handle_click(lis3);489else if (unlikely(irq_cfg == LIS3_IRQ1_DATA_READY))490lis302dl_data_ready(lis3, IRQ_LINE0);491else492lis3lv02d_joystick_poll(lis3->idev);493494return IRQ_HANDLED;495}496497static irqreturn_t lis302dl_interrupt_thread2_8b(int irq, void *data)498{499struct lis3lv02d *lis3 = data;500u8 irq_cfg = lis3->irq_cfg & LIS3_IRQ2_MASK;501502if (irq_cfg == LIS3_IRQ2_CLICK)503lis302dl_interrupt_handle_click(lis3);504else if (unlikely(irq_cfg == LIS3_IRQ2_DATA_READY))505lis302dl_data_ready(lis3, IRQ_LINE1);506else507lis3lv02d_joystick_poll(lis3->idev);508509return IRQ_HANDLED;510}511512static int lis3lv02d_misc_open(struct inode *inode, struct file *file)513{514if (test_and_set_bit(0, &lis3_dev.misc_opened))515return -EBUSY; /* already open */516517if (lis3_dev.pm_dev)518pm_runtime_get_sync(lis3_dev.pm_dev);519520atomic_set(&lis3_dev.count, 0);521return 0;522}523524static int lis3lv02d_misc_release(struct inode *inode, struct file *file)525{526fasync_helper(-1, file, 0, &lis3_dev.async_queue);527clear_bit(0, &lis3_dev.misc_opened); /* release the device */528if (lis3_dev.pm_dev)529pm_runtime_put(lis3_dev.pm_dev);530return 0;531}532533static ssize_t lis3lv02d_misc_read(struct file *file, char __user *buf,534size_t count, loff_t *pos)535{536DECLARE_WAITQUEUE(wait, current);537u32 data;538unsigned char byte_data;539ssize_t retval = 1;540541if (count < 1)542return -EINVAL;543544add_wait_queue(&lis3_dev.misc_wait, &wait);545while (true) {546set_current_state(TASK_INTERRUPTIBLE);547data = atomic_xchg(&lis3_dev.count, 0);548if (data)549break;550551if (file->f_flags & O_NONBLOCK) {552retval = -EAGAIN;553goto out;554}555556if (signal_pending(current)) {557retval = -ERESTARTSYS;558goto out;559}560561schedule();562}563564if (data < 255)565byte_data = data;566else567byte_data = 255;568569/* make sure we are not going into copy_to_user() with570* TASK_INTERRUPTIBLE state */571set_current_state(TASK_RUNNING);572if (copy_to_user(buf, &byte_data, sizeof(byte_data)))573retval = -EFAULT;574575out:576__set_current_state(TASK_RUNNING);577remove_wait_queue(&lis3_dev.misc_wait, &wait);578579return retval;580}581582static unsigned int lis3lv02d_misc_poll(struct file *file, poll_table *wait)583{584poll_wait(file, &lis3_dev.misc_wait, wait);585if (atomic_read(&lis3_dev.count))586return POLLIN | POLLRDNORM;587return 0;588}589590static int lis3lv02d_misc_fasync(int fd, struct file *file, int on)591{592return fasync_helper(fd, file, on, &lis3_dev.async_queue);593}594595static const struct file_operations lis3lv02d_misc_fops = {596.owner = THIS_MODULE,597.llseek = no_llseek,598.read = lis3lv02d_misc_read,599.open = lis3lv02d_misc_open,600.release = lis3lv02d_misc_release,601.poll = lis3lv02d_misc_poll,602.fasync = lis3lv02d_misc_fasync,603};604605static struct miscdevice lis3lv02d_misc_device = {606.minor = MISC_DYNAMIC_MINOR,607.name = "freefall",608.fops = &lis3lv02d_misc_fops,609};610611int lis3lv02d_joystick_enable(void)612{613struct input_dev *input_dev;614int err;615int max_val, fuzz, flat;616int btns[] = {BTN_X, BTN_Y, BTN_Z};617618if (lis3_dev.idev)619return -EINVAL;620621lis3_dev.idev = input_allocate_polled_device();622if (!lis3_dev.idev)623return -ENOMEM;624625lis3_dev.idev->poll = lis3lv02d_joystick_poll;626lis3_dev.idev->open = lis3lv02d_joystick_open;627lis3_dev.idev->close = lis3lv02d_joystick_close;628lis3_dev.idev->poll_interval = MDPS_POLL_INTERVAL;629lis3_dev.idev->poll_interval_min = MDPS_POLL_MIN;630lis3_dev.idev->poll_interval_max = MDPS_POLL_MAX;631input_dev = lis3_dev.idev->input;632633input_dev->name = "ST LIS3LV02DL Accelerometer";634input_dev->phys = DRIVER_NAME "/input0";635input_dev->id.bustype = BUS_HOST;636input_dev->id.vendor = 0;637input_dev->dev.parent = &lis3_dev.pdev->dev;638639set_bit(EV_ABS, input_dev->evbit);640max_val = (lis3_dev.mdps_max_val * lis3_dev.scale) / LIS3_ACCURACY;641if (lis3_dev.whoami == WAI_12B) {642fuzz = LIS3_DEFAULT_FUZZ_12B;643flat = LIS3_DEFAULT_FLAT_12B;644} else {645fuzz = LIS3_DEFAULT_FUZZ_8B;646flat = LIS3_DEFAULT_FLAT_8B;647}648fuzz = (fuzz * lis3_dev.scale) / LIS3_ACCURACY;649flat = (flat * lis3_dev.scale) / LIS3_ACCURACY;650651input_set_abs_params(input_dev, ABS_X, -max_val, max_val, fuzz, flat);652input_set_abs_params(input_dev, ABS_Y, -max_val, max_val, fuzz, flat);653input_set_abs_params(input_dev, ABS_Z, -max_val, max_val, fuzz, flat);654655lis3_dev.mapped_btns[0] = lis3lv02d_get_axis(abs(lis3_dev.ac.x), btns);656lis3_dev.mapped_btns[1] = lis3lv02d_get_axis(abs(lis3_dev.ac.y), btns);657lis3_dev.mapped_btns[2] = lis3lv02d_get_axis(abs(lis3_dev.ac.z), btns);658659err = input_register_polled_device(lis3_dev.idev);660if (err) {661input_free_polled_device(lis3_dev.idev);662lis3_dev.idev = NULL;663}664665return err;666}667EXPORT_SYMBOL_GPL(lis3lv02d_joystick_enable);668669void lis3lv02d_joystick_disable(void)670{671if (lis3_dev.irq)672free_irq(lis3_dev.irq, &lis3_dev);673if (lis3_dev.pdata && lis3_dev.pdata->irq2)674free_irq(lis3_dev.pdata->irq2, &lis3_dev);675676if (!lis3_dev.idev)677return;678679if (lis3_dev.irq)680misc_deregister(&lis3lv02d_misc_device);681input_unregister_polled_device(lis3_dev.idev);682input_free_polled_device(lis3_dev.idev);683lis3_dev.idev = NULL;684}685EXPORT_SYMBOL_GPL(lis3lv02d_joystick_disable);686687/* Sysfs stuff */688static void lis3lv02d_sysfs_poweron(struct lis3lv02d *lis3)689{690/*691* SYSFS functions are fast visitors so put-call692* immediately after the get-call. However, keep693* chip running for a while and schedule delayed694* suspend. This way periodic sysfs calls doesn't695* suffer from relatively long power up time.696*/697698if (lis3->pm_dev) {699pm_runtime_get_sync(lis3->pm_dev);700pm_runtime_put_noidle(lis3->pm_dev);701pm_schedule_suspend(lis3->pm_dev, LIS3_SYSFS_POWERDOWN_DELAY);702}703}704705static ssize_t lis3lv02d_selftest_show(struct device *dev,706struct device_attribute *attr, char *buf)707{708s16 values[3];709710static const char ok[] = "OK";711static const char fail[] = "FAIL";712static const char irq[] = "FAIL_IRQ";713const char *res;714715lis3lv02d_sysfs_poweron(&lis3_dev);716switch (lis3lv02d_selftest(&lis3_dev, values)) {717case SELFTEST_FAIL:718res = fail;719break;720case SELFTEST_IRQ:721res = irq;722break;723case SELFTEST_OK:724default:725res = ok;726break;727}728return sprintf(buf, "%s %d %d %d\n", res,729values[0], values[1], values[2]);730}731732static ssize_t lis3lv02d_position_show(struct device *dev,733struct device_attribute *attr, char *buf)734{735int x, y, z;736737lis3lv02d_sysfs_poweron(&lis3_dev);738mutex_lock(&lis3_dev.mutex);739lis3lv02d_get_xyz(&lis3_dev, &x, &y, &z);740mutex_unlock(&lis3_dev.mutex);741return sprintf(buf, "(%d,%d,%d)\n", x, y, z);742}743744static ssize_t lis3lv02d_rate_show(struct device *dev,745struct device_attribute *attr, char *buf)746{747lis3lv02d_sysfs_poweron(&lis3_dev);748return sprintf(buf, "%d\n", lis3lv02d_get_odr());749}750751static ssize_t lis3lv02d_rate_set(struct device *dev,752struct device_attribute *attr, const char *buf,753size_t count)754{755unsigned long rate;756757if (strict_strtoul(buf, 0, &rate))758return -EINVAL;759760lis3lv02d_sysfs_poweron(&lis3_dev);761if (lis3lv02d_set_odr(rate))762return -EINVAL;763764return count;765}766767static DEVICE_ATTR(selftest, S_IRUSR, lis3lv02d_selftest_show, NULL);768static DEVICE_ATTR(position, S_IRUGO, lis3lv02d_position_show, NULL);769static DEVICE_ATTR(rate, S_IRUGO | S_IWUSR, lis3lv02d_rate_show,770lis3lv02d_rate_set);771772static struct attribute *lis3lv02d_attributes[] = {773&dev_attr_selftest.attr,774&dev_attr_position.attr,775&dev_attr_rate.attr,776NULL777};778779static struct attribute_group lis3lv02d_attribute_group = {780.attrs = lis3lv02d_attributes781};782783784static int lis3lv02d_add_fs(struct lis3lv02d *lis3)785{786lis3->pdev = platform_device_register_simple(DRIVER_NAME, -1, NULL, 0);787if (IS_ERR(lis3->pdev))788return PTR_ERR(lis3->pdev);789790return sysfs_create_group(&lis3->pdev->dev.kobj, &lis3lv02d_attribute_group);791}792793int lis3lv02d_remove_fs(struct lis3lv02d *lis3)794{795sysfs_remove_group(&lis3->pdev->dev.kobj, &lis3lv02d_attribute_group);796platform_device_unregister(lis3->pdev);797if (lis3->pm_dev) {798/* Barrier after the sysfs remove */799pm_runtime_barrier(lis3->pm_dev);800801/* SYSFS may have left chip running. Turn off if necessary */802if (!pm_runtime_suspended(lis3->pm_dev))803lis3lv02d_poweroff(&lis3_dev);804805pm_runtime_disable(lis3->pm_dev);806pm_runtime_set_suspended(lis3->pm_dev);807}808kfree(lis3->reg_cache);809return 0;810}811EXPORT_SYMBOL_GPL(lis3lv02d_remove_fs);812813static void lis3lv02d_8b_configure(struct lis3lv02d *dev,814struct lis3lv02d_platform_data *p)815{816int err;817int ctrl2 = p->hipass_ctrl;818819if (p->click_flags) {820dev->write(dev, CLICK_CFG, p->click_flags);821dev->write(dev, CLICK_TIMELIMIT, p->click_time_limit);822dev->write(dev, CLICK_LATENCY, p->click_latency);823dev->write(dev, CLICK_WINDOW, p->click_window);824dev->write(dev, CLICK_THSZ, p->click_thresh_z & 0xf);825dev->write(dev, CLICK_THSY_X,826(p->click_thresh_x & 0xf) |827(p->click_thresh_y << 4));828829if (dev->idev) {830struct input_dev *input_dev = lis3_dev.idev->input;831input_set_capability(input_dev, EV_KEY, BTN_X);832input_set_capability(input_dev, EV_KEY, BTN_Y);833input_set_capability(input_dev, EV_KEY, BTN_Z);834}835}836837if (p->wakeup_flags) {838dev->write(dev, FF_WU_CFG_1, p->wakeup_flags);839dev->write(dev, FF_WU_THS_1, p->wakeup_thresh & 0x7f);840/* pdata value + 1 to keep this backward compatible*/841dev->write(dev, FF_WU_DURATION_1, p->duration1 + 1);842ctrl2 ^= HP_FF_WU1; /* Xor to keep compatible with old pdata*/843}844845if (p->wakeup_flags2) {846dev->write(dev, FF_WU_CFG_2, p->wakeup_flags2);847dev->write(dev, FF_WU_THS_2, p->wakeup_thresh2 & 0x7f);848/* pdata value + 1 to keep this backward compatible*/849dev->write(dev, FF_WU_DURATION_2, p->duration2 + 1);850ctrl2 ^= HP_FF_WU2; /* Xor to keep compatible with old pdata*/851}852/* Configure hipass filters */853dev->write(dev, CTRL_REG2, ctrl2);854855if (p->irq2) {856err = request_threaded_irq(p->irq2,857NULL,858lis302dl_interrupt_thread2_8b,859IRQF_TRIGGER_RISING | IRQF_ONESHOT |860(p->irq_flags2 & IRQF_TRIGGER_MASK),861DRIVER_NAME, &lis3_dev);862if (err < 0)863pr_err("No second IRQ. Limited functionality\n");864}865}866867/*868* Initialise the accelerometer and the various subsystems.869* Should be rather independent of the bus system.870*/871int lis3lv02d_init_device(struct lis3lv02d *dev)872{873int err;874irq_handler_t thread_fn;875int irq_flags = 0;876877dev->whoami = lis3lv02d_read_8(dev, WHO_AM_I);878879switch (dev->whoami) {880case WAI_12B:881pr_info("12 bits sensor found\n");882dev->read_data = lis3lv02d_read_12;883dev->mdps_max_val = 2048;884dev->pwron_delay = LIS3_PWRON_DELAY_WAI_12B;885dev->odrs = lis3_12_rates;886dev->odr_mask = CTRL1_DF0 | CTRL1_DF1;887dev->scale = LIS3_SENSITIVITY_12B;888dev->regs = lis3_wai12_regs;889dev->regs_size = ARRAY_SIZE(lis3_wai12_regs);890break;891case WAI_8B:892pr_info("8 bits sensor found\n");893dev->read_data = lis3lv02d_read_8;894dev->mdps_max_val = 128;895dev->pwron_delay = LIS3_PWRON_DELAY_WAI_8B;896dev->odrs = lis3_8_rates;897dev->odr_mask = CTRL1_DR;898dev->scale = LIS3_SENSITIVITY_8B;899dev->regs = lis3_wai8_regs;900dev->regs_size = ARRAY_SIZE(lis3_wai8_regs);901break;902case WAI_3DC:903pr_info("8 bits 3DC sensor found\n");904dev->read_data = lis3lv02d_read_8;905dev->mdps_max_val = 128;906dev->pwron_delay = LIS3_PWRON_DELAY_WAI_8B;907dev->odrs = lis3_3dc_rates;908dev->odr_mask = CTRL1_ODR0|CTRL1_ODR1|CTRL1_ODR2|CTRL1_ODR3;909dev->scale = LIS3_SENSITIVITY_8B;910break;911default:912pr_err("unknown sensor type 0x%X\n", dev->whoami);913return -EINVAL;914}915916dev->reg_cache = kzalloc(max(sizeof(lis3_wai8_regs),917sizeof(lis3_wai12_regs)), GFP_KERNEL);918919if (dev->reg_cache == NULL) {920printk(KERN_ERR DRIVER_NAME "out of memory\n");921return -ENOMEM;922}923924mutex_init(&dev->mutex);925atomic_set(&dev->wake_thread, 0);926927lis3lv02d_add_fs(dev);928lis3lv02d_poweron(dev);929930if (dev->pm_dev) {931pm_runtime_set_active(dev->pm_dev);932pm_runtime_enable(dev->pm_dev);933}934935if (lis3lv02d_joystick_enable())936pr_err("joystick initialization failed\n");937938/* passing in platform specific data is purely optional and only939* used by the SPI transport layer at the moment */940if (dev->pdata) {941struct lis3lv02d_platform_data *p = dev->pdata;942943if (dev->whoami == WAI_8B)944lis3lv02d_8b_configure(dev, p);945946irq_flags = p->irq_flags1 & IRQF_TRIGGER_MASK;947948dev->irq_cfg = p->irq_cfg;949if (p->irq_cfg)950dev->write(dev, CTRL_REG3, p->irq_cfg);951952if (p->default_rate)953lis3lv02d_set_odr(p->default_rate);954}955956/* bail if we did not get an IRQ from the bus layer */957if (!dev->irq) {958pr_debug("No IRQ. Disabling /dev/freefall\n");959goto out;960}961962/*963* The sensor can generate interrupts for free-fall and direction964* detection (distinguishable with FF_WU_SRC and DD_SRC) but to keep965* the things simple and _fast_ we activate it only for free-fall, so966* no need to read register (very slow with ACPI). For the same reason,967* we forbid shared interrupts.968*969* IRQF_TRIGGER_RISING seems pointless on HP laptops because the970* io-apic is not configurable (and generates a warning) but I keep it971* in case of support for other hardware.972*/973if (dev->pdata && dev->whoami == WAI_8B)974thread_fn = lis302dl_interrupt_thread1_8b;975else976thread_fn = NULL;977978err = request_threaded_irq(dev->irq, lis302dl_interrupt,979thread_fn,980IRQF_TRIGGER_RISING | IRQF_ONESHOT |981irq_flags,982DRIVER_NAME, &lis3_dev);983984if (err < 0) {985pr_err("Cannot get IRQ\n");986goto out;987}988989if (misc_register(&lis3lv02d_misc_device))990pr_err("misc_register failed\n");991out:992return 0;993}994EXPORT_SYMBOL_GPL(lis3lv02d_init_device);995996MODULE_DESCRIPTION("ST LIS3LV02Dx three-axis digital accelerometer driver");997MODULE_AUTHOR("Yan Burman, Eric Piel, Pavel Machek");998MODULE_LICENSE("GPL");99910001001