1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597 |
- /******************** (C) COPYRIGHT 2010 STMicroelectronics ********************
- *
- * File Name : lis3dh_acc.c
- * Authors : MSH - Motion Mems BU - Application Team
- * : Matteo Dameno (matteo.dameno@st.com)
- * : Carmine Iascone (carmine.iascone@st.com)
- * : Samuel Huo (samuel.huo@st.com)
- * Version : V.1.1.0
- * Date : 07/10/2012
- * Description : LIS3DH accelerometer sensor driver
- *
- *******************************************************************************
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * THE PRESENT SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES
- * OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, FOR THE SOLE
- * PURPOSE TO SUPPORT YOUR APPLICATION DEVELOPMENT.
- * AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY DIRECT,
- * INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING FROM THE
- * CONTENT OF SUCH SOFTWARE AND/OR THE USE MADE BY CUSTOMERS OF THE CODING
- * INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS.
- *
- * THIS SOFTWARE IS SPECIFICALLY DESIGNED FOR EXCLUSIVE USE WITH ST PARTS.
- *
- ******************************************************************************
- Revision 1.0.0 05/11/09
- First Release;
- Revision 1.0.3 22/01/2010
- Linux K&R Compliant Release;
- Revision 1.0.5 16/08/2010
- modified _get_acceleration_data function;
- modified _update_odr function;
- manages 2 interrupts;
- Revision 1.0.6 15/11/2010
- supports sysfs;
- no more support for ioctl;
- Revision 1.0.7 26/11/2010
- checks for availability of interrupts pins
- correction on FUZZ and FLAT values;
- Revision 1.0.8 2010/Apr/01
- corrects a bug in interrupt pin management in 1.0.7
- Revision 1.0.9 07/25/2011
- Romove several unused functions,add 5ms delay in init,change sysfs attributes.
- Revision 1.1.0 07/10/2012
- To replace some deprecated functions for 3.4 kernel;
- To pass the checkpatch's formatting requirement;
- To add regulator request;
- ******************************************************************************/
- #include <linux/err.h>
- #include <linux/errno.h>
- #include <linux/delay.h>
- #include <linux/fs.h>
- #include <linux/i2c.h>
- #include <linux/input.h>
- #include <linux/uaccess.h>
- #include <linux/workqueue.h>
- #include <linux/irq.h>
- #include <linux/gpio.h>
- #include <linux/interrupt.h>
- #include <linux/slab.h>
- #include <linux/pm.h>
- #include <linux/input/lis3dh.h>
- #include <linux/module.h>
- #include <linux/regulator/consumer.h>
- #define DEBUG 1
- #define G_MAX 16000
- #define SENSITIVITY_2G 1 /** mg/LSB */
- #define SENSITIVITY_4G 2 /** mg/LSB */
- #define SENSITIVITY_8G 4 /** mg/LSB */
- #define SENSITIVITY_16G 12 /** mg/LSB */
- /* Accelerometer Sensor Operating Mode */
- #define LIS3DH_ACC_ENABLE 0x01
- #define LIS3DH_ACC_DISABLE 0x00
- #define HIGH_RESOLUTION 0x08
- #define AXISDATA_REG 0x28
- #define WHOAMI_LIS3DH_ACC 0x33 /* Expected content for WAI */
- /* CONTROL REGISTERS */
- #define WHO_AM_I 0x0F /* WhoAmI register */
- #define TEMP_CFG_REG 0x1F /* temper sens control reg */
- /* ctrl 1: ODR3 ODR2 ODR ODR0 LPen Zenable Yenable Zenable */
- #define CTRL_REG1 0x20 /* control reg 1 */
- #define CTRL_REG2 0x21 /* control reg 2 */
- #define CTRL_REG3 0x22 /* control reg 3 */
- #define CTRL_REG4 0x23 /* control reg 4 */
- #define CTRL_REG5 0x24 /* control reg 5 */
- #define CTRL_REG6 0x25 /* control reg 6 */
- #define FIFO_CTRL_REG 0x2E /* FiFo control reg */
- #define INT_CFG1 0x30 /* interrupt 1 config */
- #define INT_SRC1 0x31 /* interrupt 1 source */
- #define INT_THS1 0x32 /* interrupt 1 threshold */
- #define INT_DUR1 0x33 /* interrupt 1 duration */
- #define TT_CFG 0x38 /* tap config */
- #define TT_SRC 0x39 /* tap source */
- #define TT_THS 0x3A /* tap threshold */
- #define TT_LIM 0x3B /* tap time limit */
- #define TT_TLAT 0x3C /* tap time latency */
- #define TT_TW 0x3D /* tap time window */
- /* end CONTROL REGISTRES */
- #define ENABLE_HIGH_RESOLUTION 1
- #define LIS3DH_ACC_PM_OFF 0x00
- #define LIS3DH_ACC_ENABLE_ALL_AXES 0x07
- #define PMODE_MASK 0x08
- #define ODR_MASK 0XF0
- #define ODR1 0x10 /* 1Hz output data rate */
- #define ODR10 0x20 /* 10Hz output data rate */
- #define ODR25 0x30 /* 25Hz output data rate */
- #define ODR50 0x40 /* 50Hz output data rate */
- #define ODR100 0x50 /* 100Hz output data rate */
- #define ODR200 0x60 /* 200Hz output data rate */
- #define ODR400 0x70 /* 400Hz output data rate */
- #define ODR1250 0x90 /* 1250Hz output data rate */
- #define IA 0x40
- #define ZH 0x20
- #define ZL 0x10
- #define YH 0x08
- #define YL 0x04
- #define XH 0x02
- #define XL 0x01
- /* */
- /* CTRL REG BITS*/
- #define CTRL_REG3_I1_AOI1 0x40
- #define CTRL_REG6_I2_TAPEN 0x80
- #define CTRL_REG6_HLACTIVE 0x02
- /* */
- #define NO_MASK 0xFF
- #define INT1_DURATION_MASK 0x7F
- #define INT1_THRESHOLD_MASK 0x7F
- #define TAP_CFG_MASK 0x3F
- #define TAP_THS_MASK 0x7F
- #define TAP_TLIM_MASK 0x7F
- #define TAP_TLAT_MASK NO_MASK
- #define TAP_TW_MASK NO_MASK
- /* TAP_SOURCE_REG BIT */
- #define DTAP 0x20
- #define STAP 0x10
- #define SIGNTAP 0x08
- #define ZTAP 0x04
- #define YTAP 0x02
- #define XTAZ 0x01
- #define FUZZ 0
- #define FLAT 0
- #define I2C_RETRY_DELAY 5
- #define I2C_RETRIES 5
- #define I2C_AUTO_INCREMENT 0x80
- /* RESUME STATE INDICES */
- #define RES_CTRL_REG1 0
- #define RES_CTRL_REG2 1
- #define RES_CTRL_REG3 2
- #define RES_CTRL_REG4 3
- #define RES_CTRL_REG5 4
- #define RES_CTRL_REG6 5
- #define RES_INT_CFG1 6
- #define RES_INT_THS1 7
- #define RES_INT_DUR1 8
- #define RES_TT_CFG 9
- #define RES_TT_THS 10
- #define RES_TT_LIM 11
- #define RES_TT_TLAT 12
- #define RES_TT_TW 13
- #define RES_TEMP_CFG_REG 14
- #define RES_REFERENCE_REG 15
- #define RES_FIFO_CTRL_REG 16
- #define RESUME_ENTRIES 17
- /* end RESUME STATE INDICES */
- struct {
- unsigned int cutoff_ms;
- unsigned int mask;
- } lis3dh_acc_odr_table[] = {
- { 1, ODR1250 },
- { 3, ODR400 },
- { 5, ODR200 },
- { 10, ODR100 },
- { 20, ODR50 },
- { 40, ODR25 },
- { 100, ODR10 },
- { 1000, ODR1 },
- };
- struct lis3dh_acc_data {
- struct i2c_client *client;
- struct lis3dh_acc_platform_data *pdata;
- struct mutex lock;
- struct delayed_work input_work;
- struct input_dev *input_dev;
- int hw_initialized;
- /* hw_working=-1 means not tested yet */
- int hw_working;
- atomic_t enabled;
- int on_before_suspend;
- u8 sensitivity;
- u8 resume_state[RESUME_ENTRIES];
- int irq1;
- struct work_struct irq1_work;
- struct workqueue_struct *irq1_work_queue;
- int irq2;
- struct work_struct irq2_work;
- struct workqueue_struct *irq2_work_queue;
- #ifdef DEBUG
- u8 reg_addr;
- #endif
- };
- struct sensor_regulator {
- struct regulator *vreg;
- const char *name;
- u32 min_uV;
- u32 max_uV;
- };
- struct sensor_regulator lis3dh_acc_vreg[] = {
- {NULL, "vdd", 1700000, 3600000},
- {NULL, "vddio", 1700000, 3600000},
- };
- static int lis3dh_acc_config_regulator(struct lis3dh_acc_data *acc, bool on)
- {
- int rc = 0, i;
- int num_reg = sizeof(lis3dh_acc_vreg) / sizeof(struct sensor_regulator);
- if (on) {
- for (i = 0; i < num_reg; i++) {
- lis3dh_acc_vreg[i].vreg =
- regulator_get(&acc->client->dev,
- lis3dh_acc_vreg[i].name);
- if (IS_ERR(lis3dh_acc_vreg[i].vreg)) {
- rc = PTR_ERR(lis3dh_acc_vreg[i].vreg);
- pr_err("%s:regulator get failed rc=%d\n",
- __func__, rc);
- lis3dh_acc_vreg[i].vreg = NULL;
- goto error_vdd;
- }
- if (regulator_count_voltages(
- lis3dh_acc_vreg[i].vreg) > 0) {
- rc = regulator_set_voltage(
- lis3dh_acc_vreg[i].vreg,
- lis3dh_acc_vreg[i].min_uV,
- lis3dh_acc_vreg[i].max_uV);
- if (rc) {
- pr_err("%s: set voltage failed rc=%d\n",
- __func__, rc);
- regulator_put(lis3dh_acc_vreg[i].vreg);
- lis3dh_acc_vreg[i].vreg = NULL;
- goto error_vdd;
- }
- }
- rc = regulator_enable(lis3dh_acc_vreg[i].vreg);
- if (rc) {
- pr_err("%s: regulator_enable failed rc =%d\n",
- __func__, rc);
- if (regulator_count_voltages(
- lis3dh_acc_vreg[i].vreg) > 0) {
- regulator_set_voltage(
- lis3dh_acc_vreg[i].vreg, 0,
- lis3dh_acc_vreg[i].max_uV);
- }
- regulator_put(lis3dh_acc_vreg[i].vreg);
- lis3dh_acc_vreg[i].vreg = NULL;
- goto error_vdd;
- }
- }
- return rc;
- } else {
- i = num_reg;
- }
- error_vdd:
- while (--i >= 0) {
- if (!IS_ERR_OR_NULL(lis3dh_acc_vreg[i].vreg)) {
- if (regulator_count_voltages(
- lis3dh_acc_vreg[i].vreg) > 0) {
- regulator_set_voltage(lis3dh_acc_vreg[i].vreg,
- 0, lis3dh_acc_vreg[i].max_uV);
- }
- regulator_disable(lis3dh_acc_vreg[i].vreg);
- regulator_put(lis3dh_acc_vreg[i].vreg);
- lis3dh_acc_vreg[i].vreg = NULL;
- }
- }
- return rc;
- }
- static int lis3dh_acc_i2c_read(struct lis3dh_acc_data *acc,
- u8 *buf, int len)
- {
- int err;
- int tries = 0;
- struct i2c_msg msgs[] = {
- {
- .addr = acc->client->addr,
- .flags = acc->client->flags & I2C_M_TEN,
- .len = 1,
- .buf = buf,
- },
- {
- .addr = acc->client->addr,
- .flags = (acc->client->flags & I2C_M_TEN) | I2C_M_RD,
- .len = len,
- .buf = buf,
- },
- };
- do {
- err = i2c_transfer(acc->client->adapter, msgs, 2);
- if (err != 2)
- msleep_interruptible(I2C_RETRY_DELAY);
- } while ((err != 2) && (++tries < I2C_RETRIES));
- if (err != 2) {
- dev_err(&acc->client->dev, "read transfer error\n");
- err = -EIO;
- } else {
- err = 0;
- }
- return err;
- }
- static int lis3dh_acc_i2c_write(struct lis3dh_acc_data *acc, u8 *buf, int len)
- {
- int err;
- int tries = 0;
- struct i2c_msg msgs[] = {
- {
- .addr = acc->client->addr,
- .flags = acc->client->flags & I2C_M_TEN,
- .len = len + 1,
- .buf = buf,
- },
- };
- do {
- err = i2c_transfer(acc->client->adapter, msgs, 1);
- if (err != 1)
- msleep_interruptible(I2C_RETRY_DELAY);
- } while ((err != 1) && (++tries < I2C_RETRIES));
- if (err != 1) {
- dev_err(&acc->client->dev, "write transfer error\n");
- err = -EIO;
- } else {
- err = 0;
- }
- return err;
- }
- static int lis3dh_acc_hw_init(struct lis3dh_acc_data *acc)
- {
- int err = -1;
- u8 buf[7];
- printk(KERN_INFO "%s: hw init start\n", LIS3DH_ACC_DEV_NAME);
- buf[0] = WHO_AM_I;
- err = lis3dh_acc_i2c_read(acc, buf, 1);
- if (err < 0) {
- dev_warn(&acc->client->dev,
- "Error reading WHO_AM_I: is device available/working?\n");
- goto err_firstread;
- } else
- acc->hw_working = 1;
- if (buf[0] != WHOAMI_LIS3DH_ACC) {
- dev_err(&acc->client->dev,
- "device unknown. Expected: 0x%x, Replies: 0x%x\n",
- WHOAMI_LIS3DH_ACC, buf[0]);
- err = -1; /* choose the right coded error */
- goto err_unknown_device;
- }
- buf[0] = CTRL_REG1;
- buf[1] = acc->resume_state[RES_CTRL_REG1];
- err = lis3dh_acc_i2c_write(acc, buf, 1);
- if (err < 0)
- goto err_resume_state;
- buf[0] = TEMP_CFG_REG;
- buf[1] = acc->resume_state[RES_TEMP_CFG_REG];
- err = lis3dh_acc_i2c_write(acc, buf, 1);
- if (err < 0)
- goto err_resume_state;
- buf[0] = FIFO_CTRL_REG;
- buf[1] = acc->resume_state[RES_FIFO_CTRL_REG];
- err = lis3dh_acc_i2c_write(acc, buf, 1);
- if (err < 0)
- goto err_resume_state;
- buf[0] = (I2C_AUTO_INCREMENT | TT_THS);
- buf[1] = acc->resume_state[RES_TT_THS];
- buf[2] = acc->resume_state[RES_TT_LIM];
- buf[3] = acc->resume_state[RES_TT_TLAT];
- buf[4] = acc->resume_state[RES_TT_TW];
- err = lis3dh_acc_i2c_write(acc, buf, 4);
- if (err < 0)
- goto err_resume_state;
- buf[0] = TT_CFG;
- buf[1] = acc->resume_state[RES_TT_CFG];
- err = lis3dh_acc_i2c_write(acc, buf, 1);
- if (err < 0)
- goto err_resume_state;
- buf[0] = (I2C_AUTO_INCREMENT | INT_THS1);
- buf[1] = acc->resume_state[RES_INT_THS1];
- buf[2] = acc->resume_state[RES_INT_DUR1];
- err = lis3dh_acc_i2c_write(acc, buf, 2);
- if (err < 0)
- goto err_resume_state;
- buf[0] = INT_CFG1;
- buf[1] = acc->resume_state[RES_INT_CFG1];
- err = lis3dh_acc_i2c_write(acc, buf, 1);
- if (err < 0)
- goto err_resume_state;
- buf[0] = (I2C_AUTO_INCREMENT | CTRL_REG2);
- buf[1] = acc->resume_state[RES_CTRL_REG2];
- buf[2] = acc->resume_state[RES_CTRL_REG3];
- buf[3] = acc->resume_state[RES_CTRL_REG4];
- buf[4] = acc->resume_state[RES_CTRL_REG5];
- buf[5] = acc->resume_state[RES_CTRL_REG6];
- err = lis3dh_acc_i2c_write(acc, buf, 5);
- if (err < 0)
- goto err_resume_state;
- acc->hw_initialized = 1;
- printk(KERN_INFO "%s: hw init done\n", LIS3DH_ACC_DEV_NAME);
- return 0;
- err_firstread:
- acc->hw_working = 0;
- err_unknown_device:
- err_resume_state:
- acc->hw_initialized = 0;
- dev_err(&acc->client->dev, "hw init error 0x%x,0x%x: %d\n", buf[0],
- buf[1], err);
- return err;
- }
- static void lis3dh_acc_device_power_off(struct lis3dh_acc_data *acc)
- {
- int err;
- u8 buf[2] = { CTRL_REG1, LIS3DH_ACC_PM_OFF };
- err = lis3dh_acc_i2c_write(acc, buf, 1);
- if (err < 0)
- dev_err(&acc->client->dev, "soft power off failed: %d\n", err);
- if (acc->pdata->gpio_int1)
- disable_irq_nosync(acc->irq1);
- if (acc->pdata->gpio_int2)
- disable_irq_nosync(acc->irq2);
- lis3dh_acc_config_regulator(acc, false);
- if (acc->hw_initialized) {
- if (acc->pdata->gpio_int1)
- disable_irq_nosync(acc->irq1);
- if (acc->pdata->gpio_int2)
- disable_irq_nosync(acc->irq2);
- acc->hw_initialized = 0;
- }
- }
- static int lis3dh_acc_device_power_on(struct lis3dh_acc_data *acc)
- {
- int err = -1;
- err = lis3dh_acc_config_regulator(acc, true);
- if (err < 0) {
- dev_err(&acc->client->dev,
- "power_on failed: %d\n", err);
- return err;
- }
- if (acc->pdata->gpio_int1 >= 0)
- enable_irq(acc->irq1);
- if (acc->pdata->gpio_int2 >= 0)
- enable_irq(acc->irq2);
- msleep(20);
- if (!acc->hw_initialized) {
- err = lis3dh_acc_hw_init(acc);
- if (acc->hw_working == 1 && err < 0) {
- lis3dh_acc_device_power_off(acc);
- return err;
- }
- }
- if (acc->hw_initialized) {
- if (acc->pdata->gpio_int1 >= 0)
- enable_irq(acc->irq1);
- if (acc->pdata->gpio_int2 >= 0)
- enable_irq(acc->irq2);
- }
- return 0;
- }
- static irqreturn_t lis3dh_acc_isr1(int irq, void *dev)
- {
- struct lis3dh_acc_data *acc = dev;
- disable_irq_nosync(irq);
- queue_work(acc->irq1_work_queue, &acc->irq1_work);
- #ifdef DEBUG
- printk(KERN_INFO "%s: isr1 queued\n", LIS3DH_ACC_DEV_NAME);
- #endif
- return IRQ_HANDLED;
- }
- static irqreturn_t lis3dh_acc_isr2(int irq, void *dev)
- {
- struct lis3dh_acc_data *acc = dev;
- disable_irq_nosync(irq);
- queue_work(acc->irq2_work_queue, &acc->irq2_work);
- #ifdef DEBUG
- printk(KERN_INFO "%s: isr2 queued\n", LIS3DH_ACC_DEV_NAME);
- #endif
- return IRQ_HANDLED;
- }
- static void lis3dh_acc_irq1_work_func(struct work_struct *work)
- {
- struct lis3dh_acc_data *acc =
- container_of(work, struct lis3dh_acc_data, irq1_work);
- /* TODO add interrupt service procedure.
- ie:lis3dh_acc_get_int1_source(acc); */
- ;
- /* */
- printk(KERN_INFO "%s: IRQ1 triggered\n", LIS3DH_ACC_DEV_NAME);
- goto exit;
- exit:
- enable_irq(acc->irq1);
- }
- static void lis3dh_acc_irq2_work_func(struct work_struct *work)
- {
- struct lis3dh_acc_data *acc =
- container_of(work, struct lis3dh_acc_data, irq2_work);
- /* TODO add interrupt service procedure.
- ie:lis3dh_acc_get_tap_source(acc); */
- ;
- /* */
- printk(KERN_INFO "%s: IRQ2 triggered\n", LIS3DH_ACC_DEV_NAME);
- goto exit;
- exit:
- enable_irq(acc->irq2);
- }
- int lis3dh_acc_update_g_range(struct lis3dh_acc_data *acc, u8 new_g_range)
- {
- int err = -1;
- u8 sensitivity;
- u8 buf[2];
- u8 updated_val;
- u8 init_val;
- u8 new_val;
- u8 mask = LIS3DH_ACC_FS_MASK | HIGH_RESOLUTION;
- switch (new_g_range) {
- case LIS3DH_ACC_G_2G:
- sensitivity = SENSITIVITY_2G;
- break;
- case LIS3DH_ACC_G_4G:
- sensitivity = SENSITIVITY_4G;
- break;
- case LIS3DH_ACC_G_8G:
- sensitivity = SENSITIVITY_8G;
- break;
- case LIS3DH_ACC_G_16G:
- sensitivity = SENSITIVITY_16G;
- break;
- default:
- dev_err(&acc->client->dev, "invalid g range requested: %u\n",
- new_g_range);
- return -EINVAL;
- }
- if (atomic_read(&acc->enabled)) {
- /* Updates configuration register 4,
- * which contains g range setting */
- buf[0] = CTRL_REG4;
- err = lis3dh_acc_i2c_read(acc, buf, 1);
- if (err < 0)
- goto error;
- init_val = buf[0];
- acc->resume_state[RES_CTRL_REG4] = init_val;
- new_val = new_g_range | HIGH_RESOLUTION;
- updated_val = ((mask & new_val) | ((~mask) & init_val));
- buf[1] = updated_val;
- buf[0] = CTRL_REG4;
- err = lis3dh_acc_i2c_write(acc, buf, 1);
- if (err < 0)
- goto error;
- acc->resume_state[RES_CTRL_REG4] = updated_val;
- acc->sensitivity = sensitivity;
- }
- return err;
- error:
- dev_err(&acc->client->dev, "update g range failed 0x%x,0x%x: %d\n",
- buf[0], buf[1], err);
- return err;
- }
- int lis3dh_acc_update_odr(struct lis3dh_acc_data *acc, int poll_interval_ms)
- {
- int err = -1;
- int i;
- u8 config[2];
- /* Following, looks for the longest possible odr interval scrolling the
- * odr_table vector from the end (shortest interval) backward (longest
- * interval), to support the poll_interval requested by the system.
- * It must be the longest interval lower then the poll interval.*/
- for (i = ARRAY_SIZE(lis3dh_acc_odr_table) - 1; i >= 0; i--) {
- if (lis3dh_acc_odr_table[i].cutoff_ms <= poll_interval_ms)
- break;
- }
- config[1] = lis3dh_acc_odr_table[i].mask;
- config[1] |= LIS3DH_ACC_ENABLE_ALL_AXES;
- /* If device is currently enabled, we need to write new
- * configuration out to it */
- if (atomic_read(&acc->enabled)) {
- config[0] = CTRL_REG1;
- err = lis3dh_acc_i2c_write(acc, config, 1);
- if (err < 0)
- goto error;
- acc->resume_state[RES_CTRL_REG1] = config[1];
- }
- return err;
- error:
- dev_err(&acc->client->dev, "update odr failed 0x%x,0x%x: %d\n",
- config[0], config[1], err);
- return err;
- }
- static int lis3dh_acc_register_write(struct lis3dh_acc_data *acc, u8 *buf,
- u8 reg_address, u8 new_value)
- {
- int err = -1;
- /* Sets configuration register at reg_address
- * NOTE: this is a straight overwrite */
- buf[0] = reg_address;
- buf[1] = new_value;
- err = lis3dh_acc_i2c_write(acc, buf, 1);
- if (err < 0)
- return err;
- return err;
- }
- static int lis3dh_acc_get_acceleration_data(struct lis3dh_acc_data *acc,
- int *xyz)
- {
- int err = -1;
- /* Data bytes from hardware xL, xH, yL, yH, zL, zH */
- u8 acc_data[6];
- /* x,y,z hardware data */
- s16 hw_d[3] = { 0 };
- acc_data[0] = (I2C_AUTO_INCREMENT | AXISDATA_REG);
- err = lis3dh_acc_i2c_read(acc, acc_data, 6);
- if (err < 0)
- return err;
- hw_d[0] = (((s16) ((acc_data[1] << 8) | acc_data[0])) >> 4);
- hw_d[1] = (((s16) ((acc_data[3] << 8) | acc_data[2])) >> 4);
- hw_d[2] = (((s16) ((acc_data[5] << 8) | acc_data[4])) >> 4);
- hw_d[0] = hw_d[0] * acc->sensitivity;
- hw_d[1] = hw_d[1] * acc->sensitivity;
- hw_d[2] = hw_d[2] * acc->sensitivity;
- xyz[0] = ((acc->pdata->negate_x) ? (-hw_d[acc->pdata->axis_map_x])
- : (hw_d[acc->pdata->axis_map_x]));
- xyz[1] = ((acc->pdata->negate_y) ? (-hw_d[acc->pdata->axis_map_y])
- : (hw_d[acc->pdata->axis_map_y]));
- xyz[2] = ((acc->pdata->negate_z) ? (-hw_d[acc->pdata->axis_map_z])
- : (hw_d[acc->pdata->axis_map_z]));
- #ifdef DEBUG
- /*
- printk(KERN_INFO "%s read x=%d, y=%d, z=%d\n",
- LIS3DH_ACC_DEV_NAME, xyz[0], xyz[1], xyz[2]);
- */
- #endif
- return err;
- }
- static void lis3dh_acc_report_values(struct lis3dh_acc_data *acc,
- int *xyz)
- {
- input_report_abs(acc->input_dev, ABS_X, xyz[0]);
- input_report_abs(acc->input_dev, ABS_Y, xyz[1]);
- input_report_abs(acc->input_dev, ABS_Z, xyz[2]);
- input_sync(acc->input_dev);
- }
- static int lis3dh_acc_enable(struct lis3dh_acc_data *acc)
- {
- int err;
- if (!atomic_cmpxchg(&acc->enabled, 0, 1)) {
- err = lis3dh_acc_device_power_on(acc);
- if (err < 0) {
- atomic_set(&acc->enabled, 0);
- return err;
- }
- schedule_delayed_work(&acc->input_work,
- msecs_to_jiffies(acc->pdata->poll_interval));
- }
- return 0;
- }
- static int lis3dh_acc_disable(struct lis3dh_acc_data *acc)
- {
- if (atomic_cmpxchg(&acc->enabled, 1, 0)) {
- cancel_delayed_work_sync(&acc->input_work);
- lis3dh_acc_device_power_off(acc);
- }
- return 0;
- }
- static ssize_t read_single_reg(struct device *dev, char *buf, u8 reg)
- {
- ssize_t ret;
- struct lis3dh_acc_data *acc = dev_get_drvdata(dev);
- int err;
- u8 data = reg;
- err = lis3dh_acc_i2c_read(acc, &data, 1);
- if (err < 0)
- return err;
- ret = snprintf(buf, 4, "0x%02x\n", data);
- return ret;
- }
- static int write_reg(struct device *dev, const char *buf, u8 reg,
- u8 mask, int resumeIndex)
- {
- int err = -1;
- struct lis3dh_acc_data *acc = dev_get_drvdata(dev);
- u8 x[2];
- u8 new_val;
- unsigned long val;
- if (kstrtoul(buf, 16, &val))
- return -EINVAL;
- new_val = ((u8) val & mask);
- x[0] = reg;
- x[1] = new_val;
- err = lis3dh_acc_register_write(acc, x, reg, new_val);
- if (err < 0)
- return err;
- acc->resume_state[resumeIndex] = new_val;
- return err;
- }
- static ssize_t attr_get_polling_rate(struct device *dev,
- struct device_attribute *attr,
- char *buf)
- {
- int val;
- struct lis3dh_acc_data *acc = dev_get_drvdata(dev);
- mutex_lock(&acc->lock);
- val = acc->pdata->poll_interval;
- mutex_unlock(&acc->lock);
- return snprintf(buf, 8, "%d\n", val);
- }
- static ssize_t attr_set_polling_rate(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t size)
- {
- struct lis3dh_acc_data *acc = dev_get_drvdata(dev);
- unsigned long interval_ms;
- if (kstrtoul(buf, 10, &interval_ms))
- return -EINVAL;
- if (!interval_ms)
- return -EINVAL;
- mutex_lock(&acc->lock);
- acc->pdata->poll_interval = interval_ms;
- lis3dh_acc_update_odr(acc, interval_ms);
- mutex_unlock(&acc->lock);
- return size;
- }
- static ssize_t attr_get_range(struct device *dev,
- struct device_attribute *attr, char *buf)
- {
- char val;
- struct lis3dh_acc_data *acc = dev_get_drvdata(dev);
- char range = 2;
- mutex_lock(&acc->lock);
- val = acc->pdata->g_range ;
- switch (val) {
- case LIS3DH_ACC_G_2G:
- range = 2;
- break;
- case LIS3DH_ACC_G_4G:
- range = 4;
- break;
- case LIS3DH_ACC_G_8G:
- range = 8;
- break;
- case LIS3DH_ACC_G_16G:
- range = 16;
- break;
- }
- mutex_unlock(&acc->lock);
- return snprintf(buf, 4, "%d\n", range);
- }
- static ssize_t attr_set_range(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t size)
- {
- struct lis3dh_acc_data *acc = dev_get_drvdata(dev);
- unsigned long val;
- if (kstrtoul(buf, 10, &val))
- return -EINVAL;
- mutex_lock(&acc->lock);
- acc->pdata->g_range = val;
- lis3dh_acc_update_g_range(acc, val);
- mutex_unlock(&acc->lock);
- return size;
- }
- static ssize_t attr_get_enable(struct device *dev,
- struct device_attribute *attr, char *buf)
- {
- struct lis3dh_acc_data *acc = dev_get_drvdata(dev);
- int val = atomic_read(&acc->enabled);
- return snprintf(buf, sizeof(val) + 2, "%d\n", val);
- }
- static ssize_t attr_set_enable(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t size)
- {
- struct lis3dh_acc_data *acc = dev_get_drvdata(dev);
- unsigned long val;
- if (kstrtoul(buf, 10, &val))
- return -EINVAL;
- if (val)
- lis3dh_acc_enable(acc);
- else
- lis3dh_acc_disable(acc);
- return size;
- }
- static ssize_t attr_set_intconfig1(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t size)
- {
- return write_reg(dev, buf, INT_CFG1, NO_MASK, RES_INT_CFG1);
- }
- static ssize_t attr_get_intconfig1(struct device *dev,
- struct device_attribute *attr, char *buf)
- {
- return read_single_reg(dev, buf, INT_CFG1);
- }
- static ssize_t attr_set_duration1(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t size)
- {
- return write_reg(dev, buf, INT_DUR1, INT1_DURATION_MASK, RES_INT_DUR1);
- }
- static ssize_t attr_get_duration1(struct device *dev,
- struct device_attribute *attr, char *buf)
- {
- return read_single_reg(dev, buf, INT_DUR1);
- }
- static ssize_t attr_set_thresh1(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t size)
- {
- return write_reg(dev, buf, INT_THS1, INT1_THRESHOLD_MASK, RES_INT_THS1);
- }
- static ssize_t attr_get_thresh1(struct device *dev,
- struct device_attribute *attr, char *buf)
- {
- return read_single_reg(dev, buf, INT_THS1);
- }
- static ssize_t attr_get_source1(struct device *dev,
- struct device_attribute *attr, char *buf)
- {
- return read_single_reg(dev, buf, INT_SRC1);
- }
- static ssize_t attr_set_click_cfg(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t size)
- {
- return write_reg(dev, buf, TT_CFG, TAP_CFG_MASK, RES_TT_CFG);
- }
- static ssize_t attr_get_click_cfg(struct device *dev,
- struct device_attribute *attr, char *buf)
- {
- return read_single_reg(dev, buf, TT_CFG);
- }
- static ssize_t attr_get_click_source(struct device *dev,
- struct device_attribute *attr, char *buf)
- {
- return read_single_reg(dev, buf, TT_SRC);
- }
- static ssize_t attr_set_click_ths(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t size)
- {
- return write_reg(dev, buf, TT_THS, TAP_THS_MASK, RES_TT_THS);
- }
- static ssize_t attr_get_click_ths(struct device *dev,
- struct device_attribute *attr, char *buf)
- {
- return read_single_reg(dev, buf, TT_THS);
- }
- static ssize_t attr_set_click_tlim(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t size)
- {
- return write_reg(dev, buf, TT_LIM, TAP_TLIM_MASK, RES_TT_LIM);
- }
- static ssize_t attr_get_click_tlim(struct device *dev,
- struct device_attribute *attr, char *buf)
- {
- return read_single_reg(dev, buf, TT_LIM);
- }
- static ssize_t attr_set_click_tlat(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t size)
- {
- return write_reg(dev, buf, TT_TLAT, TAP_TLAT_MASK, RES_TT_TLAT);
- }
- static ssize_t attr_get_click_tlat(struct device *dev,
- struct device_attribute *attr, char *buf)
- {
- return read_single_reg(dev, buf, TT_TLAT);
- }
- static ssize_t attr_set_click_tw(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t size)
- {
- return write_reg(dev, buf, TT_TLAT, TAP_TW_MASK, RES_TT_TLAT);
- }
- static ssize_t attr_get_click_tw(struct device *dev,
- struct device_attribute *attr, char *buf)
- {
- return read_single_reg(dev, buf, TT_TLAT);
- }
- #ifdef DEBUG
- /* PAY ATTENTION: These DEBUG funtions don't manage resume_state */
- static ssize_t attr_reg_set(struct device *dev, struct device_attribute *attr,
- const char *buf, size_t size)
- {
- int rc;
- struct lis3dh_acc_data *acc = dev_get_drvdata(dev);
- u8 x[2];
- unsigned long val;
- if (kstrtoul(buf, 16, &val))
- return -EINVAL;
- mutex_lock(&acc->lock);
- x[0] = acc->reg_addr;
- mutex_unlock(&acc->lock);
- x[1] = val;
- rc = lis3dh_acc_i2c_write(acc, x, 1);
- /*TODO: error need to be managed */
- return size;
- }
- static ssize_t attr_reg_get(struct device *dev, struct device_attribute *attr,
- char *buf)
- {
- ssize_t ret;
- struct lis3dh_acc_data *acc = dev_get_drvdata(dev);
- int rc;
- u8 data;
- mutex_lock(&acc->lock);
- data = acc->reg_addr;
- mutex_unlock(&acc->lock);
- rc = lis3dh_acc_i2c_read(acc, &data, 1);
- /* TODO: error need to be managed */
- ret = snprintf(buf, 8, "0x%02x\n", data);
- return ret;
- }
- static ssize_t attr_addr_set(struct device *dev, struct device_attribute *attr,
- const char *buf, size_t size)
- {
- struct lis3dh_acc_data *acc = dev_get_drvdata(dev);
- unsigned long val;
- if (kstrtoul(buf, 16, &val))
- return -EINVAL;
- mutex_lock(&acc->lock);
- acc->reg_addr = val;
- mutex_unlock(&acc->lock);
- return size;
- }
- #endif
- static struct device_attribute attributes[] = {
- __ATTR(poll_delay, 0664, attr_get_polling_rate,
- attr_set_polling_rate),
- __ATTR(range, 0664, attr_get_range, attr_set_range),
- __ATTR(enable, 0664, attr_get_enable, attr_set_enable),
- __ATTR(int1_config, 0664, attr_get_intconfig1, attr_set_intconfig1),
- __ATTR(int1_duration, 0664, attr_get_duration1, attr_set_duration1),
- __ATTR(int1_threshold, 0664, attr_get_thresh1, attr_set_thresh1),
- __ATTR(int1_source, 0444, attr_get_source1, NULL),
- __ATTR(click_config, 0664, attr_get_click_cfg, attr_set_click_cfg),
- __ATTR(click_source, 0444, attr_get_click_source, NULL),
- __ATTR(click_threshold, 0664, attr_get_click_ths, attr_set_click_ths),
- __ATTR(click_timelimit, 0664, attr_get_click_tlim,
- attr_set_click_tlim),
- __ATTR(click_timelatency, 0664, attr_get_click_tlat,
- attr_set_click_tlat),
- __ATTR(click_timewindow, 0664, attr_get_click_tw, attr_set_click_tw),
- #ifdef DEBUG
- __ATTR(reg_value, 0664, attr_reg_get, attr_reg_set),
- __ATTR(reg_addr, 0220, NULL, attr_addr_set),
- #endif
- };
- static int create_sysfs_interfaces(struct device *dev)
- {
- int i;
- int err;
- for (i = 0; i < ARRAY_SIZE(attributes); i++) {
- err = device_create_file(dev, attributes + i);
- if (err)
- goto error;
- }
- return 0;
- error:
- for ( ; i >= 0; i--)
- device_remove_file(dev, attributes + i);
- dev_err(dev, "%s:Unable to create interface\n", __func__);
- return err;
- }
- static int remove_sysfs_interfaces(struct device *dev)
- {
- int i;
- for (i = 0; i < ARRAY_SIZE(attributes); i++)
- device_remove_file(dev, attributes + i);
- return 0;
- }
- static void lis3dh_acc_input_work_func(struct work_struct *work)
- {
- struct lis3dh_acc_data *acc;
- int xyz[3] = { 0 };
- int err;
- acc = container_of((struct delayed_work *)work,
- struct lis3dh_acc_data, input_work);
- mutex_lock(&acc->lock);
- err = lis3dh_acc_get_acceleration_data(acc, xyz);
- if (err < 0)
- dev_err(&acc->client->dev, "get_acceleration_data failed\n");
- else
- lis3dh_acc_report_values(acc, xyz);
- schedule_delayed_work(&acc->input_work, msecs_to_jiffies(
- acc->pdata->poll_interval));
- mutex_unlock(&acc->lock);
- }
- int lis3dh_acc_input_open(struct input_dev *input)
- {
- struct lis3dh_acc_data *acc = input_get_drvdata(input);
- return lis3dh_acc_enable(acc);
- }
- void lis3dh_acc_input_close(struct input_dev *dev)
- {
- struct lis3dh_acc_data *acc = input_get_drvdata(dev);
- lis3dh_acc_disable(acc);
- }
- static int lis3dh_acc_validate_pdata(struct lis3dh_acc_data *acc)
- {
- acc->pdata->poll_interval = max(acc->pdata->poll_interval,
- acc->pdata->min_interval);
- if (acc->pdata->axis_map_x > 2 ||
- acc->pdata->axis_map_y > 2 ||
- acc->pdata->axis_map_z > 2) {
- dev_err(&acc->client->dev,
- "invalid axis_map value x:%u y:%u z%u\n",
- acc->pdata->axis_map_x,
- acc->pdata->axis_map_y, acc->pdata->axis_map_z);
- return -EINVAL;
- }
- /* Only allow 0 and 1 for negation boolean flag */
- if (acc->pdata->negate_x > 1 || acc->pdata->negate_y > 1
- || acc->pdata->negate_z > 1) {
- dev_err(&acc->client->dev,
- "invalid negate value x:%u y:%u z:%u\n",
- acc->pdata->negate_x,
- acc->pdata->negate_y, acc->pdata->negate_z);
- return -EINVAL;
- }
- /* Enforce minimum polling interval */
- if (acc->pdata->poll_interval < acc->pdata->min_interval) {
- dev_err(&acc->client->dev, "minimum poll interval violated\n");
- return -EINVAL;
- }
- return 0;
- }
- static int lis3dh_acc_input_init(struct lis3dh_acc_data *acc)
- {
- int err;
- INIT_DELAYED_WORK(&acc->input_work, lis3dh_acc_input_work_func);
- acc->input_dev = input_allocate_device();
- if (!acc->input_dev) {
- err = -ENOMEM;
- dev_err(&acc->client->dev, "input device allocation failed\n");
- goto err0;
- }
- acc->input_dev->open = lis3dh_acc_input_open;
- acc->input_dev->close = lis3dh_acc_input_close;
- acc->input_dev->name = ACCEL_INPUT_DEV_NAME;
- acc->input_dev->id.bustype = BUS_I2C;
- acc->input_dev->dev.parent = &acc->client->dev;
- input_set_drvdata(acc->input_dev, acc);
- set_bit(EV_ABS, acc->input_dev->evbit);
- /* next is used for interruptA sources data if the case */
- set_bit(ABS_MISC, acc->input_dev->absbit);
- /* next is used for interruptB sources data if the case */
- set_bit(ABS_WHEEL, acc->input_dev->absbit);
- input_set_abs_params(acc->input_dev, ABS_X, -G_MAX, G_MAX, FUZZ, FLAT);
- input_set_abs_params(acc->input_dev, ABS_Y, -G_MAX, G_MAX, FUZZ, FLAT);
- input_set_abs_params(acc->input_dev, ABS_Z, -G_MAX, G_MAX, FUZZ, FLAT);
- /* next is used for interruptA sources data if the case */
- input_set_abs_params(acc->input_dev, ABS_MISC, INT_MIN, INT_MAX, 0, 0);
- /* next is used for interruptB sources data if the case */
- input_set_abs_params(acc->input_dev, ABS_WHEEL, INT_MIN, INT_MAX, 0, 0);
- err = input_register_device(acc->input_dev);
- if (err) {
- dev_err(&acc->client->dev,
- "unable to register input device %s\n",
- acc->input_dev->name);
- goto err1;
- }
- return 0;
- err1:
- input_free_device(acc->input_dev);
- err0:
- return err;
- }
- static void lis3dh_acc_input_cleanup(struct lis3dh_acc_data *acc)
- {
- input_unregister_device(acc->input_dev);
- input_free_device(acc->input_dev);
- }
- static int lis3dh_acc_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
- {
- struct lis3dh_acc_data *acc;
- int err = -1;
- pr_info("%s: probe start.\n", LIS3DH_ACC_DEV_NAME);
- if (client->dev.platform_data == NULL) {
- dev_err(&client->dev, "platform data is NULL. exiting.\n");
- err = -ENODEV;
- goto exit_check_functionality_failed;
- }
- if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
- dev_err(&client->dev, "client not i2c capable\n");
- err = -ENODEV;
- goto exit_check_functionality_failed;
- }
- /*
- if (!i2c_check_functionality(client->adapter,
- I2C_FUNC_SMBUS_BYTE |
- I2C_FUNC_SMBUS_BYTE_DATA |
- I2C_FUNC_SMBUS_WORD_DATA)) {
- dev_err(&client->dev, "client not smb-i2c capable:2\n");
- err = -EIO;
- goto exit_check_functionality_failed;
- }
- if (!i2c_check_functionality(client->adapter,
- I2C_FUNC_SMBUS_I2C_BLOCK)) {
- dev_err(&client->dev, "client not smb-i2c capable:3\n");
- err = -EIO;
- goto exit_check_functionality_failed;
- }
- */
- acc = kzalloc(sizeof(struct lis3dh_acc_data), GFP_KERNEL);
- if (acc == NULL) {
- err = -ENOMEM;
- dev_err(&client->dev,
- "failed to allocate memory for module data: "
- "%d\n", err);
- goto exit_check_functionality_failed;
- }
- mutex_init(&acc->lock);
- mutex_lock(&acc->lock);
- acc->client = client;
- i2c_set_clientdata(client, acc);
- acc->pdata = kmalloc(sizeof(*acc->pdata), GFP_KERNEL);
- if (acc->pdata == NULL) {
- err = -ENOMEM;
- dev_err(&client->dev,
- "failed to allocate memory for pdata: %d\n",
- err);
- goto err_mutexunlock;
- }
- memcpy(acc->pdata, client->dev.platform_data, sizeof(*acc->pdata));
- err = lis3dh_acc_validate_pdata(acc);
- if (err < 0) {
- dev_err(&client->dev, "failed to validate platform data\n");
- goto exit_kfree_pdata;
- }
- if (acc->pdata->init) {
- err = acc->pdata->init();
- if (err < 0) {
- dev_err(&client->dev, "init failed: %d\n", err);
- goto err_pdata_init;
- }
- }
- if (acc->pdata->gpio_int1 >= 0) {
- acc->irq1 = gpio_to_irq(acc->pdata->gpio_int1);
- printk(KERN_INFO "%s: %s has set irq1 to irq: %d\n",
- LIS3DH_ACC_DEV_NAME, __func__, acc->irq1);
- printk(KERN_INFO "%s: %s has mapped irq1 on gpio: %d\n",
- LIS3DH_ACC_DEV_NAME, __func__,
- acc->pdata->gpio_int1);
- }
- if (acc->pdata->gpio_int2 >= 0) {
- acc->irq2 = gpio_to_irq(acc->pdata->gpio_int2);
- printk(KERN_INFO "%s: %s has set irq2 to irq: %d\n",
- LIS3DH_ACC_DEV_NAME, __func__, acc->irq2);
- printk(KERN_INFO "%s: %s has mapped irq2 on gpio: %d\n",
- LIS3DH_ACC_DEV_NAME, __func__,
- acc->pdata->gpio_int2);
- }
- memset(acc->resume_state, 0, ARRAY_SIZE(acc->resume_state));
- acc->resume_state[RES_CTRL_REG1] = LIS3DH_ACC_ENABLE_ALL_AXES;
- acc->resume_state[RES_CTRL_REG2] = 0x00;
- acc->resume_state[RES_CTRL_REG3] = 0x00;
- acc->resume_state[RES_CTRL_REG4] = 0x00;
- acc->resume_state[RES_CTRL_REG5] = 0x00;
- acc->resume_state[RES_CTRL_REG6] = 0x00;
- acc->resume_state[RES_TEMP_CFG_REG] = 0x00;
- acc->resume_state[RES_FIFO_CTRL_REG] = 0x00;
- acc->resume_state[RES_INT_CFG1] = 0x00;
- acc->resume_state[RES_INT_THS1] = 0x00;
- acc->resume_state[RES_INT_DUR1] = 0x00;
- acc->resume_state[RES_TT_CFG] = 0x00;
- acc->resume_state[RES_TT_THS] = 0x00;
- acc->resume_state[RES_TT_LIM] = 0x00;
- acc->resume_state[RES_TT_TLAT] = 0x00;
- acc->resume_state[RES_TT_TW] = 0x00;
- err = lis3dh_acc_device_power_on(acc);
- if (err < 0) {
- dev_err(&client->dev, "power on failed: %d\n", err);
- goto err_pdata_init;
- }
- atomic_set(&acc->enabled, 1);
- err = lis3dh_acc_update_g_range(acc, acc->pdata->g_range);
- if (err < 0) {
- dev_err(&client->dev, "update_g_range failed\n");
- goto err_power_off;
- }
- err = lis3dh_acc_update_odr(acc, acc->pdata->poll_interval);
- if (err < 0) {
- dev_err(&client->dev, "update_odr failed\n");
- goto err_power_off;
- }
- err = lis3dh_acc_input_init(acc);
- if (err < 0) {
- dev_err(&client->dev, "input init failed\n");
- goto err_power_off;
- }
- err = create_sysfs_interfaces(&client->dev);
- if (err < 0) {
- dev_err(&client->dev,
- "device LIS3DH_ACC_DEV_NAME sysfs register failed\n");
- goto err_input_cleanup;
- }
- lis3dh_acc_device_power_off(acc);
- /* As default, do not report information */
- atomic_set(&acc->enabled, 0);
- if (acc->pdata->gpio_int1 >= 0) {
- INIT_WORK(&acc->irq1_work, lis3dh_acc_irq1_work_func);
- acc->irq1_work_queue =
- create_singlethread_workqueue("lis3dh_acc_wq1");
- if (!acc->irq1_work_queue) {
- err = -ENOMEM;
- dev_err(&client->dev,
- "cannot create work queue1: %d\n", err);
- goto err_remove_sysfs_int;
- }
- err = request_irq(acc->irq1, lis3dh_acc_isr1,
- IRQF_TRIGGER_RISING, "lis3dh_acc_irq1", acc);
- if (err < 0) {
- dev_err(&client->dev, "request irq1 failed: %d\n", err);
- goto err_destoyworkqueue1;
- }
- disable_irq_nosync(acc->irq1);
- }
- if (acc->pdata->gpio_int2 >= 0) {
- INIT_WORK(&acc->irq2_work, lis3dh_acc_irq2_work_func);
- acc->irq2_work_queue =
- create_singlethread_workqueue("lis3dh_acc_wq2");
- if (!acc->irq2_work_queue) {
- err = -ENOMEM;
- dev_err(&client->dev,
- "cannot create work queue2: %d\n", err);
- goto err_free_irq1;
- }
- err = request_irq(acc->irq2, lis3dh_acc_isr2,
- IRQF_TRIGGER_RISING, "lis3dh_acc_irq2", acc);
- if (err < 0) {
- dev_err(&client->dev, "request irq2 failed: %d\n", err);
- goto err_destoyworkqueue2;
- }
- disable_irq_nosync(acc->irq2);
- }
- mutex_unlock(&acc->lock);
- dev_info(&client->dev, "%s: probed\n", LIS3DH_ACC_DEV_NAME);
- return 0;
- err_destoyworkqueue2:
- if (acc->pdata->gpio_int2 >= 0)
- destroy_workqueue(acc->irq2_work_queue);
- err_free_irq1:
- free_irq(acc->irq1, acc);
- err_destoyworkqueue1:
- if (acc->pdata->gpio_int1 >= 0)
- destroy_workqueue(acc->irq1_work_queue);
- err_remove_sysfs_int:
- remove_sysfs_interfaces(&client->dev);
- err_input_cleanup:
- lis3dh_acc_input_cleanup(acc);
- err_power_off:
- lis3dh_acc_device_power_off(acc);
- err_pdata_init:
- if (acc->pdata->exit)
- acc->pdata->exit();
- exit_kfree_pdata:
- kfree(acc->pdata);
- err_mutexunlock:
- mutex_unlock(&acc->lock);
- kfree(acc);
- exit_check_functionality_failed:
- printk(KERN_ERR "%s: Driver Init failed\n", LIS3DH_ACC_DEV_NAME);
- return err;
- }
- static int __devexit lis3dh_acc_remove(struct i2c_client *client)
- {
- struct lis3dh_acc_data *acc = i2c_get_clientdata(client);
- if (acc->pdata->gpio_int1 >= 0) {
- free_irq(acc->irq1, acc);
- gpio_free(acc->pdata->gpio_int1);
- destroy_workqueue(acc->irq1_work_queue);
- }
- if (acc->pdata->gpio_int2 >= 0) {
- free_irq(acc->irq2, acc);
- gpio_free(acc->pdata->gpio_int2);
- destroy_workqueue(acc->irq2_work_queue);
- }
- lis3dh_acc_input_cleanup(acc);
- lis3dh_acc_device_power_off(acc);
- remove_sysfs_interfaces(&client->dev);
- if (acc->pdata->exit)
- acc->pdata->exit();
- kfree(acc->pdata);
- kfree(acc);
- return 0;
- }
- #ifdef CONFIG_PM
- static int lis3dh_acc_resume(struct i2c_client *client)
- {
- struct lis3dh_acc_data *acc = i2c_get_clientdata(client);
- if (acc->on_before_suspend)
- return lis3dh_acc_enable(acc);
- return 0;
- }
- static int lis3dh_acc_suspend(struct i2c_client *client, pm_message_t mesg)
- {
- struct lis3dh_acc_data *acc = i2c_get_clientdata(client);
- acc->on_before_suspend = atomic_read(&acc->enabled);
- return lis3dh_acc_disable(acc);
- }
- #else
- #define lis3dh_acc_suspend NULL
- #define lis3dh_acc_resume NULL
- #endif /* CONFIG_PM */
- static const struct i2c_device_id lis3dh_acc_id[]
- = { { LIS3DH_ACC_DEV_NAME, 0 }, { }, };
- MODULE_DEVICE_TABLE(i2c, lis3dh_acc_id);
- static struct i2c_driver lis3dh_acc_driver = {
- .driver = {
- .owner = THIS_MODULE,
- .name = LIS3DH_ACC_DEV_NAME,
- },
- .probe = lis3dh_acc_probe,
- .remove = __devexit_p(lis3dh_acc_remove),
- .suspend = lis3dh_acc_suspend,
- .resume = lis3dh_acc_resume,
- .id_table = lis3dh_acc_id,
- };
- static int __init lis3dh_acc_init(void)
- {
- printk(KERN_INFO "%s accelerometer driver: init\n",
- LIS3DH_ACC_DEV_NAME);
- return i2c_add_driver(&lis3dh_acc_driver);
- }
- static void __exit lis3dh_acc_exit(void)
- {
- #ifdef DEBUG
- printk(KERN_INFO "%s accelerometer driver exit\n",
- LIS3DH_ACC_DEV_NAME);
- #endif /* DEBUG */
- i2c_del_driver(&lis3dh_acc_driver);
- return;
- }
- module_init(lis3dh_acc_init);
- module_exit(lis3dh_acc_exit);
- MODULE_DESCRIPTION("lis3dh digital accelerometer sysfs driver");
- MODULE_AUTHOR("Matteo Dameno, Carmine Iascone, Samuel Huo, STMicroelectronics");
- MODULE_LICENSE("GPL");
|