linux/drivers/input/misc/cma3000_d0x.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * VTI CMA3000_D0x Accelerometer driver
   4 *
   5 * Copyright (C) 2010 Texas Instruments
   6 * Author: Hemanth V <hemanthv@ti.com>
   7 */
   8
   9#include <linux/types.h>
  10#include <linux/interrupt.h>
  11#include <linux/delay.h>
  12#include <linux/slab.h>
  13#include <linux/input.h>
  14#include <linux/input/cma3000.h>
  15#include <linux/module.h>
  16
  17#include "cma3000_d0x.h"
  18
  19#define CMA3000_WHOAMI      0x00
  20#define CMA3000_REVID       0x01
  21#define CMA3000_CTRL        0x02
  22#define CMA3000_STATUS      0x03
  23#define CMA3000_RSTR        0x04
  24#define CMA3000_INTSTATUS   0x05
  25#define CMA3000_DOUTX       0x06
  26#define CMA3000_DOUTY       0x07
  27#define CMA3000_DOUTZ       0x08
  28#define CMA3000_MDTHR       0x09
  29#define CMA3000_MDFFTMR     0x0A
  30#define CMA3000_FFTHR       0x0B
  31
  32#define CMA3000_RANGE2G    (1 << 7)
  33#define CMA3000_RANGE8G    (0 << 7)
  34#define CMA3000_BUSI2C     (0 << 4)
  35#define CMA3000_MODEMASK   (7 << 1)
  36#define CMA3000_GRANGEMASK (1 << 7)
  37
  38#define CMA3000_STATUS_PERR    1
  39#define CMA3000_INTSTATUS_FFDET (1 << 2)
  40
  41/* Settling time delay in ms */
  42#define CMA3000_SETDELAY    30
  43
  44/* Delay for clearing interrupt in us */
  45#define CMA3000_INTDELAY    44
  46
  47
  48/*
  49 * Bit weights in mg for bit 0, other bits need
  50 * multiply factor 2^n. Eight bit is the sign bit.
  51 */
  52#define BIT_TO_2G  18
  53#define BIT_TO_8G  71
  54
  55struct cma3000_accl_data {
  56        const struct cma3000_bus_ops *bus_ops;
  57        const struct cma3000_platform_data *pdata;
  58
  59        struct device *dev;
  60        struct input_dev *input_dev;
  61
  62        int bit_to_mg;
  63        int irq;
  64
  65        int g_range;
  66        u8 mode;
  67
  68        struct mutex mutex;
  69        bool opened;
  70        bool suspended;
  71};
  72
  73#define CMA3000_READ(data, reg, msg) \
  74        (data->bus_ops->read(data->dev, reg, msg))
  75#define CMA3000_SET(data, reg, val, msg) \
  76        ((data)->bus_ops->write(data->dev, reg, val, msg))
  77
  78/*
  79 * Conversion for each of the eight modes to g, depending
  80 * on G range i.e 2G or 8G. Some modes always operate in
  81 * 8G.
  82 */
  83
  84static int mode_to_mg[8][2] = {
  85        { 0, 0 },
  86        { BIT_TO_8G, BIT_TO_2G },
  87        { BIT_TO_8G, BIT_TO_2G },
  88        { BIT_TO_8G, BIT_TO_8G },
  89        { BIT_TO_8G, BIT_TO_8G },
  90        { BIT_TO_8G, BIT_TO_2G },
  91        { BIT_TO_8G, BIT_TO_2G },
  92        { 0, 0},
  93};
  94
  95static void decode_mg(struct cma3000_accl_data *data, int *datax,
  96                                int *datay, int *dataz)
  97{
  98        /* Data in 2's complement, convert to mg */
  99        *datax = ((s8)*datax) * data->bit_to_mg;
 100        *datay = ((s8)*datay) * data->bit_to_mg;
 101        *dataz = ((s8)*dataz) * data->bit_to_mg;
 102}
 103
 104static irqreturn_t cma3000_thread_irq(int irq, void *dev_id)
 105{
 106        struct cma3000_accl_data *data = dev_id;
 107        int datax, datay, dataz, intr_status;
 108        u8 ctrl, mode, range;
 109
 110        intr_status = CMA3000_READ(data, CMA3000_INTSTATUS, "interrupt status");
 111        if (intr_status < 0)
 112                return IRQ_NONE;
 113
 114        /* Check if free fall is detected, report immediately */
 115        if (intr_status & CMA3000_INTSTATUS_FFDET) {
 116                input_report_abs(data->input_dev, ABS_MISC, 1);
 117                input_sync(data->input_dev);
 118        } else {
 119                input_report_abs(data->input_dev, ABS_MISC, 0);
 120        }
 121
 122        datax = CMA3000_READ(data, CMA3000_DOUTX, "X");
 123        datay = CMA3000_READ(data, CMA3000_DOUTY, "Y");
 124        dataz = CMA3000_READ(data, CMA3000_DOUTZ, "Z");
 125
 126        ctrl = CMA3000_READ(data, CMA3000_CTRL, "ctrl");
 127        mode = (ctrl & CMA3000_MODEMASK) >> 1;
 128        range = (ctrl & CMA3000_GRANGEMASK) >> 7;
 129
 130        data->bit_to_mg = mode_to_mg[mode][range];
 131
 132        /* Interrupt not for this device */
 133        if (data->bit_to_mg == 0)
 134                return IRQ_NONE;
 135
 136        /* Decode register values to milli g */
 137        decode_mg(data, &datax, &datay, &dataz);
 138
 139        input_report_abs(data->input_dev, ABS_X, datax);
 140        input_report_abs(data->input_dev, ABS_Y, datay);
 141        input_report_abs(data->input_dev, ABS_Z, dataz);
 142        input_sync(data->input_dev);
 143
 144        return IRQ_HANDLED;
 145}
 146
 147static int cma3000_reset(struct cma3000_accl_data *data)
 148{
 149        int val;
 150
 151        /* Reset sequence */
 152        CMA3000_SET(data, CMA3000_RSTR, 0x02, "Reset");
 153        CMA3000_SET(data, CMA3000_RSTR, 0x0A, "Reset");
 154        CMA3000_SET(data, CMA3000_RSTR, 0x04, "Reset");
 155
 156        /* Settling time delay */
 157        mdelay(10);
 158
 159        val = CMA3000_READ(data, CMA3000_STATUS, "Status");
 160        if (val < 0) {
 161                dev_err(data->dev, "Reset failed\n");
 162                return val;
 163        }
 164
 165        if (val & CMA3000_STATUS_PERR) {
 166                dev_err(data->dev, "Parity Error\n");
 167                return -EIO;
 168        }
 169
 170        return 0;
 171}
 172
 173static int cma3000_poweron(struct cma3000_accl_data *data)
 174{
 175        const struct cma3000_platform_data *pdata = data->pdata;
 176        u8 ctrl = 0;
 177        int ret;
 178
 179        if (data->g_range == CMARANGE_2G) {
 180                ctrl = (data->mode << 1) | CMA3000_RANGE2G;
 181        } else if (data->g_range == CMARANGE_8G) {
 182                ctrl = (data->mode << 1) | CMA3000_RANGE8G;
 183        } else {
 184                dev_info(data->dev,
 185                         "Invalid G range specified, assuming 8G\n");
 186                ctrl = (data->mode << 1) | CMA3000_RANGE8G;
 187        }
 188
 189        ctrl |= data->bus_ops->ctrl_mod;
 190
 191        CMA3000_SET(data, CMA3000_MDTHR, pdata->mdthr,
 192                    "Motion Detect Threshold");
 193        CMA3000_SET(data, CMA3000_MDFFTMR, pdata->mdfftmr,
 194                    "Time register");
 195        CMA3000_SET(data, CMA3000_FFTHR, pdata->ffthr,
 196                    "Free fall threshold");
 197        ret = CMA3000_SET(data, CMA3000_CTRL, ctrl, "Mode setting");
 198        if (ret < 0)
 199                return -EIO;
 200
 201        msleep(CMA3000_SETDELAY);
 202
 203        return 0;
 204}
 205
 206static int cma3000_poweroff(struct cma3000_accl_data *data)
 207{
 208        int ret;
 209
 210        ret = CMA3000_SET(data, CMA3000_CTRL, CMAMODE_POFF, "Mode setting");
 211        msleep(CMA3000_SETDELAY);
 212
 213        return ret;
 214}
 215
 216static int cma3000_open(struct input_dev *input_dev)
 217{
 218        struct cma3000_accl_data *data = input_get_drvdata(input_dev);
 219
 220        mutex_lock(&data->mutex);
 221
 222        if (!data->suspended)
 223                cma3000_poweron(data);
 224
 225        data->opened = true;
 226
 227        mutex_unlock(&data->mutex);
 228
 229        return 0;
 230}
 231
 232static void cma3000_close(struct input_dev *input_dev)
 233{
 234        struct cma3000_accl_data *data = input_get_drvdata(input_dev);
 235
 236        mutex_lock(&data->mutex);
 237
 238        if (!data->suspended)
 239                cma3000_poweroff(data);
 240
 241        data->opened = false;
 242
 243        mutex_unlock(&data->mutex);
 244}
 245
 246void cma3000_suspend(struct cma3000_accl_data *data)
 247{
 248        mutex_lock(&data->mutex);
 249
 250        if (!data->suspended && data->opened)
 251                cma3000_poweroff(data);
 252
 253        data->suspended = true;
 254
 255        mutex_unlock(&data->mutex);
 256}
 257EXPORT_SYMBOL(cma3000_suspend);
 258
 259
 260void cma3000_resume(struct cma3000_accl_data *data)
 261{
 262        mutex_lock(&data->mutex);
 263
 264        if (data->suspended && data->opened)
 265                cma3000_poweron(data);
 266
 267        data->suspended = false;
 268
 269        mutex_unlock(&data->mutex);
 270}
 271EXPORT_SYMBOL(cma3000_resume);
 272
 273struct cma3000_accl_data *cma3000_init(struct device *dev, int irq,
 274                                       const struct cma3000_bus_ops *bops)
 275{
 276        const struct cma3000_platform_data *pdata = dev_get_platdata(dev);
 277        struct cma3000_accl_data *data;
 278        struct input_dev *input_dev;
 279        int rev;
 280        int error;
 281
 282        if (!pdata) {
 283                dev_err(dev, "platform data not found\n");
 284                error = -EINVAL;
 285                goto err_out;
 286        }
 287
 288
 289        /* if no IRQ return error */
 290        if (irq == 0) {
 291                error = -EINVAL;
 292                goto err_out;
 293        }
 294
 295        data = kzalloc(sizeof(struct cma3000_accl_data), GFP_KERNEL);
 296        input_dev = input_allocate_device();
 297        if (!data || !input_dev) {
 298                error = -ENOMEM;
 299                goto err_free_mem;
 300        }
 301
 302        data->dev = dev;
 303        data->input_dev = input_dev;
 304        data->bus_ops = bops;
 305        data->pdata = pdata;
 306        data->irq = irq;
 307        mutex_init(&data->mutex);
 308
 309        data->mode = pdata->mode;
 310        if (data->mode > CMAMODE_POFF) {
 311                data->mode = CMAMODE_MOTDET;
 312                dev_warn(dev,
 313                         "Invalid mode specified, assuming Motion Detect\n");
 314        }
 315
 316        data->g_range = pdata->g_range;
 317        if (data->g_range != CMARANGE_2G && data->g_range != CMARANGE_8G) {
 318                dev_info(dev,
 319                         "Invalid G range specified, assuming 8G\n");
 320                data->g_range = CMARANGE_8G;
 321        }
 322
 323        input_dev->name = "cma3000-accelerometer";
 324        input_dev->id.bustype = bops->bustype;
 325        input_dev->open = cma3000_open;
 326        input_dev->close = cma3000_close;
 327
 328         __set_bit(EV_ABS, input_dev->evbit);
 329
 330        input_set_abs_params(input_dev, ABS_X,
 331                        -data->g_range, data->g_range, pdata->fuzz_x, 0);
 332        input_set_abs_params(input_dev, ABS_Y,
 333                        -data->g_range, data->g_range, pdata->fuzz_y, 0);
 334        input_set_abs_params(input_dev, ABS_Z,
 335                        -data->g_range, data->g_range, pdata->fuzz_z, 0);
 336        input_set_abs_params(input_dev, ABS_MISC, 0, 1, 0, 0);
 337
 338        input_set_drvdata(input_dev, data);
 339
 340        error = cma3000_reset(data);
 341        if (error)
 342                goto err_free_mem;
 343
 344        rev = CMA3000_READ(data, CMA3000_REVID, "Revid");
 345        if (rev < 0) {
 346                error = rev;
 347                goto err_free_mem;
 348        }
 349
 350        pr_info("CMA3000 Accelerometer: Revision %x\n", rev);
 351
 352        error = request_threaded_irq(irq, NULL, cma3000_thread_irq,
 353                                     pdata->irqflags | IRQF_ONESHOT,
 354                                     "cma3000_d0x", data);
 355        if (error) {
 356                dev_err(dev, "request_threaded_irq failed\n");
 357                goto err_free_mem;
 358        }
 359
 360        error = input_register_device(data->input_dev);
 361        if (error) {
 362                dev_err(dev, "Unable to register input device\n");
 363                goto err_free_irq;
 364        }
 365
 366        return data;
 367
 368err_free_irq:
 369        free_irq(irq, data);
 370err_free_mem:
 371        input_free_device(input_dev);
 372        kfree(data);
 373err_out:
 374        return ERR_PTR(error);
 375}
 376EXPORT_SYMBOL(cma3000_init);
 377
 378void cma3000_exit(struct cma3000_accl_data *data)
 379{
 380        free_irq(data->irq, data);
 381        input_unregister_device(data->input_dev);
 382        kfree(data);
 383}
 384EXPORT_SYMBOL(cma3000_exit);
 385
 386MODULE_DESCRIPTION("CMA3000-D0x Accelerometer Driver");
 387MODULE_LICENSE("GPL");
 388MODULE_AUTHOR("Hemanth V <hemanthv@ti.com>");
 389