linux/drivers/platform/x86/dell-smo8800.c
<<
>>
Prefs
   1/*
   2 *  dell-smo8800.c - Dell Latitude ACPI SMO88XX freefall sensor driver
   3 *
   4 *  Copyright (C) 2012 Sonal Santan <sonal.santan@gmail.com>
   5 *  Copyright (C) 2014 Pali Roh\xC3\xA1r <pali.rohar@gmail.com>
   6 *
   7 *  This is loosely based on lis3lv02d driver.
   8 *
   9 *  This program is free software; you can redistribute it and/or modify
  10 *  it under the terms of the GNU General Public License as published by
  11 *  the Free Software Foundation; either version 2 of the License, or
  12 *  (at your option) any later version.
  13 *
  14 *  This program is distributed in the hope that it will be useful,
  15 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  16 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  17 *  GNU General Public License for more details.
  18 */
  19
  20#define DRIVER_NAME "smo8800"
  21
  22#include <linux/kernel.h>
  23#include <linux/module.h>
  24#include <linux/acpi.h>
  25#include <linux/interrupt.h>
  26#include <linux/miscdevice.h>
  27
  28struct smo8800_device {
  29        u32 irq;                     /* acpi device irq */
  30        atomic_t counter;            /* count after last read */
  31        struct miscdevice miscdev;   /* for /dev/freefall */
  32        unsigned long misc_opened;   /* whether the device is open */
  33        wait_queue_head_t misc_wait; /* Wait queue for the misc dev */
  34        struct device *dev;          /* acpi device */
  35};
  36
  37static irqreturn_t smo8800_interrupt_quick(int irq, void *data)
  38{
  39        struct smo8800_device *smo8800 = data;
  40
  41        atomic_inc(&smo8800->counter);
  42        wake_up_interruptible(&smo8800->misc_wait);
  43        return IRQ_WAKE_THREAD;
  44}
  45
  46static irqreturn_t smo8800_interrupt_thread(int irq, void *data)
  47{
  48        struct smo8800_device *smo8800 = data;
  49
  50        dev_info(smo8800->dev, "detected free fall\n");
  51        return IRQ_HANDLED;
  52}
  53
  54static acpi_status smo8800_get_resource(struct acpi_resource *resource,
  55                                        void *context)
  56{
  57        struct acpi_resource_extended_irq *irq;
  58
  59        if (resource->type != ACPI_RESOURCE_TYPE_EXTENDED_IRQ)
  60                return AE_OK;
  61
  62        irq = &resource->data.extended_irq;
  63        if (!irq || !irq->interrupt_count)
  64                return AE_OK;
  65
  66        *((u32 *)context) = irq->interrupts[0];
  67        return AE_CTRL_TERMINATE;
  68}
  69
  70static u32 smo8800_get_irq(struct acpi_device *device)
  71{
  72        u32 irq = 0;
  73        acpi_status status;
  74
  75        status = acpi_walk_resources(device->handle, METHOD_NAME__CRS,
  76                                     smo8800_get_resource, &irq);
  77        if (ACPI_FAILURE(status)) {
  78                dev_err(&device->dev, "acpi_walk_resources failed\n");
  79                return 0;
  80        }
  81
  82        return irq;
  83}
  84
  85static ssize_t smo8800_misc_read(struct file *file, char __user *buf,
  86                                 size_t count, loff_t *pos)
  87{
  88        struct smo8800_device *smo8800 = container_of(file->private_data,
  89                                         struct smo8800_device, miscdev);
  90
  91        u32 data = 0;
  92        unsigned char byte_data = 0;
  93        ssize_t retval = 1;
  94
  95        if (count < 1)
  96                return -EINVAL;
  97
  98        atomic_set(&smo8800->counter, 0);
  99        retval = wait_event_interruptible(smo8800->misc_wait,
 100                                (data = atomic_xchg(&smo8800->counter, 0)));
 101
 102        if (retval)
 103                return retval;
 104
 105        byte_data = 1;
 106        retval = 1;
 107
 108        if (data < 255)
 109                byte_data = data;
 110        else
 111                byte_data = 255;
 112
 113        if (put_user(byte_data, buf))
 114                retval = -EFAULT;
 115
 116        return retval;
 117}
 118
 119static int smo8800_misc_open(struct inode *inode, struct file *file)
 120{
 121        struct smo8800_device *smo8800 = container_of(file->private_data,
 122                                         struct smo8800_device, miscdev);
 123
 124        if (test_and_set_bit(0, &smo8800->misc_opened))
 125                return -EBUSY; /* already open */
 126
 127        atomic_set(&smo8800->counter, 0);
 128        return 0;
 129}
 130
 131static int smo8800_misc_release(struct inode *inode, struct file *file)
 132{
 133        struct smo8800_device *smo8800 = container_of(file->private_data,
 134                                         struct smo8800_device, miscdev);
 135
 136        clear_bit(0, &smo8800->misc_opened); /* release the device */
 137        return 0;
 138}
 139
 140static const struct file_operations smo8800_misc_fops = {
 141        .owner = THIS_MODULE,
 142        .read = smo8800_misc_read,
 143        .open = smo8800_misc_open,
 144        .release = smo8800_misc_release,
 145};
 146
 147static int smo8800_add(struct acpi_device *device)
 148{
 149        int err;
 150        struct smo8800_device *smo8800;
 151
 152        smo8800 = devm_kzalloc(&device->dev, sizeof(*smo8800), GFP_KERNEL);
 153        if (!smo8800) {
 154                dev_err(&device->dev, "failed to allocate device data\n");
 155                return -ENOMEM;
 156        }
 157
 158        smo8800->dev = &device->dev;
 159        smo8800->miscdev.minor = MISC_DYNAMIC_MINOR;
 160        smo8800->miscdev.name = "freefall";
 161        smo8800->miscdev.fops = &smo8800_misc_fops;
 162
 163        init_waitqueue_head(&smo8800->misc_wait);
 164
 165        err = misc_register(&smo8800->miscdev);
 166        if (err) {
 167                dev_err(&device->dev, "failed to register misc dev: %d\n", err);
 168                return err;
 169        }
 170
 171        device->driver_data = smo8800;
 172
 173        smo8800->irq = smo8800_get_irq(device);
 174        if (!smo8800->irq) {
 175                dev_err(&device->dev, "failed to obtain IRQ\n");
 176                err = -EINVAL;
 177                goto error;
 178        }
 179
 180        err = request_threaded_irq(smo8800->irq, smo8800_interrupt_quick,
 181                                   smo8800_interrupt_thread,
 182                                   IRQF_TRIGGER_RISING | IRQF_ONESHOT,
 183                                   DRIVER_NAME, smo8800);
 184        if (err) {
 185                dev_err(&device->dev,
 186                        "failed to request thread for IRQ %d: %d\n",
 187                        smo8800->irq, err);
 188                goto error;
 189        }
 190
 191        dev_dbg(&device->dev, "device /dev/freefall registered with IRQ %d\n",
 192                 smo8800->irq);
 193        return 0;
 194
 195error:
 196        misc_deregister(&smo8800->miscdev);
 197        return err;
 198}
 199
 200static int smo8800_remove(struct acpi_device *device)
 201{
 202        struct smo8800_device *smo8800 = device->driver_data;
 203
 204        free_irq(smo8800->irq, smo8800);
 205        misc_deregister(&smo8800->miscdev);
 206        dev_dbg(&device->dev, "device /dev/freefall unregistered\n");
 207        return 0;
 208}
 209
 210static const struct acpi_device_id smo8800_ids[] = {
 211        { "SMO8800", 0 },
 212        { "SMO8801", 0 },
 213        { "SMO8810", 0 },
 214        { "SMO8811", 0 },
 215        { "SMO8820", 0 },
 216        { "SMO8821", 0 },
 217        { "SMO8830", 0 },
 218        { "SMO8831", 0 },
 219        { "", 0 },
 220};
 221
 222MODULE_DEVICE_TABLE(acpi, smo8800_ids);
 223
 224static struct acpi_driver smo8800_driver = {
 225        .name = DRIVER_NAME,
 226        .class = "Latitude",
 227        .ids = smo8800_ids,
 228        .ops = {
 229                .add = smo8800_add,
 230                .remove = smo8800_remove,
 231        },
 232        .owner = THIS_MODULE,
 233};
 234
 235module_acpi_driver(smo8800_driver);
 236
 237MODULE_DESCRIPTION("Dell Latitude freefall driver (ACPI SMO88XX)");
 238MODULE_LICENSE("GPL");
 239MODULE_AUTHOR("Sonal Santan, Pali Roh\xC3\xA1r");
 240
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.