Path: blob/master/drivers/input/misc/cma3000_d0x.c
15109 views
/*1* VTI CMA3000_D0x Accelerometer driver2*3* Copyright (C) 2010 Texas Instruments4* Author: Hemanth V <[email protected]>5*6* This program is free software; you can redistribute it and/or modify it7* under the terms of the GNU General Public License version 2 as published by8* the Free Software Foundation.9*10* This program is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for13* more details.14*15* You should have received a copy of the GNU General Public License along with16* this program. If not, see <http://www.gnu.org/licenses/>.17*/1819#include <linux/types.h>20#include <linux/interrupt.h>21#include <linux/delay.h>22#include <linux/slab.h>23#include <linux/input.h>24#include <linux/input/cma3000.h>2526#include "cma3000_d0x.h"2728#define CMA3000_WHOAMI 0x0029#define CMA3000_REVID 0x0130#define CMA3000_CTRL 0x0231#define CMA3000_STATUS 0x0332#define CMA3000_RSTR 0x0433#define CMA3000_INTSTATUS 0x0534#define CMA3000_DOUTX 0x0635#define CMA3000_DOUTY 0x0736#define CMA3000_DOUTZ 0x0837#define CMA3000_MDTHR 0x0938#define CMA3000_MDFFTMR 0x0A39#define CMA3000_FFTHR 0x0B4041#define CMA3000_RANGE2G (1 << 7)42#define CMA3000_RANGE8G (0 << 7)43#define CMA3000_BUSI2C (0 << 4)44#define CMA3000_MODEMASK (7 << 1)45#define CMA3000_GRANGEMASK (1 << 7)4647#define CMA3000_STATUS_PERR 148#define CMA3000_INTSTATUS_FFDET (1 << 2)4950/* Settling time delay in ms */51#define CMA3000_SETDELAY 305253/* Delay for clearing interrupt in us */54#define CMA3000_INTDELAY 44555657/*58* Bit weights in mg for bit 0, other bits need59* multipy factor 2^n. Eight bit is the sign bit.60*/61#define BIT_TO_2G 1862#define BIT_TO_8G 716364struct cma3000_accl_data {65const struct cma3000_bus_ops *bus_ops;66const struct cma3000_platform_data *pdata;6768struct device *dev;69struct input_dev *input_dev;7071int bit_to_mg;72int irq;7374int g_range;75u8 mode;7677struct mutex mutex;78bool opened;79bool suspended;80};8182#define CMA3000_READ(data, reg, msg) \83(data->bus_ops->read(data->dev, reg, msg))84#define CMA3000_SET(data, reg, val, msg) \85((data)->bus_ops->write(data->dev, reg, val, msg))8687/*88* Conversion for each of the eight modes to g, depending89* on G range i.e 2G or 8G. Some modes always operate in90* 8G.91*/9293static int mode_to_mg[8][2] = {94{ 0, 0 },95{ BIT_TO_8G, BIT_TO_2G },96{ BIT_TO_8G, BIT_TO_2G },97{ BIT_TO_8G, BIT_TO_8G },98{ BIT_TO_8G, BIT_TO_8G },99{ BIT_TO_8G, BIT_TO_2G },100{ BIT_TO_8G, BIT_TO_2G },101{ 0, 0},102};103104static void decode_mg(struct cma3000_accl_data *data, int *datax,105int *datay, int *dataz)106{107/* Data in 2's complement, convert to mg */108*datax = ((s8)*datax) * data->bit_to_mg;109*datay = ((s8)*datay) * data->bit_to_mg;110*dataz = ((s8)*dataz) * data->bit_to_mg;111}112113static irqreturn_t cma3000_thread_irq(int irq, void *dev_id)114{115struct cma3000_accl_data *data = dev_id;116int datax, datay, dataz;117u8 ctrl, mode, range, intr_status;118119intr_status = CMA3000_READ(data, CMA3000_INTSTATUS, "interrupt status");120if (intr_status < 0)121return IRQ_NONE;122123/* Check if free fall is detected, report immediately */124if (intr_status & CMA3000_INTSTATUS_FFDET) {125input_report_abs(data->input_dev, ABS_MISC, 1);126input_sync(data->input_dev);127} else {128input_report_abs(data->input_dev, ABS_MISC, 0);129}130131datax = CMA3000_READ(data, CMA3000_DOUTX, "X");132datay = CMA3000_READ(data, CMA3000_DOUTY, "Y");133dataz = CMA3000_READ(data, CMA3000_DOUTZ, "Z");134135ctrl = CMA3000_READ(data, CMA3000_CTRL, "ctrl");136mode = (ctrl & CMA3000_MODEMASK) >> 1;137range = (ctrl & CMA3000_GRANGEMASK) >> 7;138139data->bit_to_mg = mode_to_mg[mode][range];140141/* Interrupt not for this device */142if (data->bit_to_mg == 0)143return IRQ_NONE;144145/* Decode register values to milli g */146decode_mg(data, &datax, &datay, &dataz);147148input_report_abs(data->input_dev, ABS_X, datax);149input_report_abs(data->input_dev, ABS_Y, datay);150input_report_abs(data->input_dev, ABS_Z, dataz);151input_sync(data->input_dev);152153return IRQ_HANDLED;154}155156static int cma3000_reset(struct cma3000_accl_data *data)157{158int val;159160/* Reset sequence */161CMA3000_SET(data, CMA3000_RSTR, 0x02, "Reset");162CMA3000_SET(data, CMA3000_RSTR, 0x0A, "Reset");163CMA3000_SET(data, CMA3000_RSTR, 0x04, "Reset");164165/* Settling time delay */166mdelay(10);167168val = CMA3000_READ(data, CMA3000_STATUS, "Status");169if (val < 0) {170dev_err(data->dev, "Reset failed\n");171return val;172}173174if (val & CMA3000_STATUS_PERR) {175dev_err(data->dev, "Parity Error\n");176return -EIO;177}178179return 0;180}181182static int cma3000_poweron(struct cma3000_accl_data *data)183{184const struct cma3000_platform_data *pdata = data->pdata;185u8 ctrl = 0;186int ret;187188if (data->g_range == CMARANGE_2G) {189ctrl = (data->mode << 1) | CMA3000_RANGE2G;190} else if (data->g_range == CMARANGE_8G) {191ctrl = (data->mode << 1) | CMA3000_RANGE8G;192} else {193dev_info(data->dev,194"Invalid G range specified, assuming 8G\n");195ctrl = (data->mode << 1) | CMA3000_RANGE8G;196}197198ctrl |= data->bus_ops->ctrl_mod;199200CMA3000_SET(data, CMA3000_MDTHR, pdata->mdthr,201"Motion Detect Threshold");202CMA3000_SET(data, CMA3000_MDFFTMR, pdata->mdfftmr,203"Time register");204CMA3000_SET(data, CMA3000_FFTHR, pdata->ffthr,205"Free fall threshold");206ret = CMA3000_SET(data, CMA3000_CTRL, ctrl, "Mode setting");207if (ret < 0)208return -EIO;209210msleep(CMA3000_SETDELAY);211212return 0;213}214215static int cma3000_poweroff(struct cma3000_accl_data *data)216{217int ret;218219ret = CMA3000_SET(data, CMA3000_CTRL, CMAMODE_POFF, "Mode setting");220msleep(CMA3000_SETDELAY);221222return ret;223}224225static int cma3000_open(struct input_dev *input_dev)226{227struct cma3000_accl_data *data = input_get_drvdata(input_dev);228229mutex_lock(&data->mutex);230231if (!data->suspended)232cma3000_poweron(data);233234data->opened = true;235236mutex_unlock(&data->mutex);237238return 0;239}240241static void cma3000_close(struct input_dev *input_dev)242{243struct cma3000_accl_data *data = input_get_drvdata(input_dev);244245mutex_lock(&data->mutex);246247if (!data->suspended)248cma3000_poweroff(data);249250data->opened = false;251252mutex_unlock(&data->mutex);253}254255void cma3000_suspend(struct cma3000_accl_data *data)256{257mutex_lock(&data->mutex);258259if (!data->suspended && data->opened)260cma3000_poweroff(data);261262data->suspended = true;263264mutex_unlock(&data->mutex);265}266EXPORT_SYMBOL(cma3000_suspend);267268269void cma3000_resume(struct cma3000_accl_data *data)270{271mutex_lock(&data->mutex);272273if (data->suspended && data->opened)274cma3000_poweron(data);275276data->suspended = false;277278mutex_unlock(&data->mutex);279}280EXPORT_SYMBOL(cma3000_resume);281282struct cma3000_accl_data *cma3000_init(struct device *dev, int irq,283const struct cma3000_bus_ops *bops)284{285const struct cma3000_platform_data *pdata = dev->platform_data;286struct cma3000_accl_data *data;287struct input_dev *input_dev;288int rev;289int error;290291if (!pdata) {292dev_err(dev, "platform data not found\n");293error = -EINVAL;294goto err_out;295}296297298/* if no IRQ return error */299if (irq == 0) {300error = -EINVAL;301goto err_out;302}303304data = kzalloc(sizeof(struct cma3000_accl_data), GFP_KERNEL);305input_dev = input_allocate_device();306if (!data || !input_dev) {307error = -ENOMEM;308goto err_free_mem;309}310311data->dev = dev;312data->input_dev = input_dev;313data->bus_ops = bops;314data->pdata = pdata;315data->irq = irq;316mutex_init(&data->mutex);317318data->mode = pdata->mode;319if (data->mode < CMAMODE_DEFAULT || data->mode > CMAMODE_POFF) {320data->mode = CMAMODE_MOTDET;321dev_warn(dev,322"Invalid mode specified, assuming Motion Detect\n");323}324325data->g_range = pdata->g_range;326if (data->g_range != CMARANGE_2G && data->g_range != CMARANGE_8G) {327dev_info(dev,328"Invalid G range specified, assuming 8G\n");329data->g_range = CMARANGE_8G;330}331332input_dev->name = "cma3000-accelerometer";333input_dev->id.bustype = bops->bustype;334input_dev->open = cma3000_open;335input_dev->close = cma3000_close;336337__set_bit(EV_ABS, input_dev->evbit);338339input_set_abs_params(input_dev, ABS_X,340-data->g_range, data->g_range, pdata->fuzz_x, 0);341input_set_abs_params(input_dev, ABS_Y,342-data->g_range, data->g_range, pdata->fuzz_y, 0);343input_set_abs_params(input_dev, ABS_Z,344-data->g_range, data->g_range, pdata->fuzz_z, 0);345input_set_abs_params(input_dev, ABS_MISC, 0, 1, 0, 0);346347input_set_drvdata(input_dev, data);348349error = cma3000_reset(data);350if (error)351goto err_free_mem;352353rev = CMA3000_READ(data, CMA3000_REVID, "Revid");354if (rev < 0) {355error = rev;356goto err_free_mem;357}358359pr_info("CMA3000 Accelerometer: Revision %x\n", rev);360361error = request_threaded_irq(irq, NULL, cma3000_thread_irq,362pdata->irqflags | IRQF_ONESHOT,363"cma3000_d0x", data);364if (error) {365dev_err(dev, "request_threaded_irq failed\n");366goto err_free_mem;367}368369error = input_register_device(data->input_dev);370if (error) {371dev_err(dev, "Unable to register input device\n");372goto err_free_irq;373}374375return data;376377err_free_irq:378free_irq(irq, data);379err_free_mem:380input_free_device(input_dev);381kfree(data);382err_out:383return ERR_PTR(error);384}385EXPORT_SYMBOL(cma3000_init);386387void cma3000_exit(struct cma3000_accl_data *data)388{389free_irq(data->irq, data);390input_unregister_device(data->input_dev);391kfree(data);392}393EXPORT_SYMBOL(cma3000_exit);394395MODULE_DESCRIPTION("CMA3000-D0x Accelerometer Driver");396MODULE_LICENSE("GPL");397MODULE_AUTHOR("Hemanth V <[email protected]>");398399400