Lis3lv02dq spi.c

This is drivers/spi/lis3lv02dq_spi.c /* * Copyright (C) 2006 Andrei Rylin * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ term papers


 * 1) include 
 * 2) include 
 * 3) include 
 * 4) include 
 * 5) include 
 * 6) include 
 * 7) include 
 * 8) include 
 * 9) include 
 * 10) include 
 * 11) include 
 * 12) include 
 * 13) include 

" linear accelerometer"
 * 1) define DEV_NAME "lis3lv02dq-spi"
 * 2) define DEV_NAME_LONG "STMicroelectronics LIS3LV02DQ 3-axis +-2/6g" \
 * 1) define DRV_VERSION_STR "version 01.00"

MODULE_AUTHOR("Andrei Rylin "); MODULE_DESCRIPTION(DEV_NAME_LONG); MODULE_LICENSE("GPL");

struct this_reg { unsigned char addr; unsigned char mask; const char*  name; const char*  description; };


 * 1) define PROC_MODE   0x80
 * 2) define PROC_STAT   0x81
 * 3) define PROC_PERF   0x82
 * 4) define PROC_PERF1  0x83
 * 5) define PROC_WHOAMI 0x84


 * 1) define DECL_BEGIN static struct this_reg this_regs[] = {


 * 1) ifdef DEBUG
 * 2) define DECL_REG(reg,mask,description) {reg, mask, #reg, description},
 * 3) define DECL_RE_(reg,mask,description) {reg, mask, #mask, description},
 * 4) endif

{PROC_MODE,   0, "mode",    0}, \ {PROC_STAT,   0, "stat",    0}, \ {PROC_PERF,   0, "perf",    0}, \ {PROC_PERF1,  0, "perf1",   0}, \ {PROC_WHOAMI, 0, "whoami",  0}, \ {CTRL_REG1, CTRL_REG1_DF, "decimation_factor", 0}, \ {CTRL_REG2, CTRL_REG2_BDU, "block_data_update", 0}, \ };
 * 1) define DECL_END                    \


 * 1) include "lis3lv02dq.h"


 * 1) define ALIGNED(x) __attribute__((aligned(x)))
 * 2) define IS_DMA_ALIGNED(x) (((u32)(x)&7)==0)
 * 3) define N_BUFS 2

/* * LIS3LV02DQ often gets into a state when interrupt pin does not go * down after read of all data registes. Perhaps this happens on data * overrun - the next sample immideately replaces the one that was * just read and 'data ready' is not de-asserted even for a moment. * In order to insure that interrupts keep coming, in response to * an interrupt we do not just read data registers, but first * write 0 to CTRL_REG2[DRDY], then read the data, then re-enable DRDY pin. * It's all done by one spi_message with 4 spi_transfers: * 1 to disable DRDY: write 2 bytes - address followed by value * 2 to read data: write address, then read 8 bytes auto-incrementing address * 1 to re-enable DRDY */

/* * Another problem that needs to be understood is that sometimes interrupts * stop coming, sensor status is 0x77 and only reading of OUTZ_L brings * the sensor back to normal, for a while. To reproduce, comment out * BUG_WORKAROUND below. */
 * 1) define BUG_WORKAROUND 1

struct this_data { struct this_dev* dev; struct spi_message message; struct spi_transfer disable_drdy; struct spi_transfer addr; struct spi_transfer data; struct spi_transfer enable_drdy; ALIGNED(8) u8 buf[8]; /* 1 unused + 1 status + 6 data */ };

struct this_dev { struct list_head link; int minor; int flags; struct class_device *class_dev; struct spi_device* spi;
 * 1) define FLAGS_MODE_HEX 0x01
 * 2) define FLAGS_MODE_BIN 0x02

spinlock_t lock; struct this_data* data; struct list_head free_data; struct list_head pending_data;

struct list_head clients; wait_queue_head_t waitq;

ALIGNED(8) u8 disable_drdy_buf[2]; ALIGNED(8) u8 read_addr; ALIGNED(8) u8 enable_drdy_buf[2];

/* local register cache */ u8 ctrl_reg1; u8 ctrl_reg2;

unsigned long sensor_samples; unsigned long sensor_overruns; unsigned long sensor_errors; unsigned long user_samples; unsigned long user_overruns;

struct proc_dir_entry *proc_parent; struct proc_dir_entry *proc_reg[ARRAY_SIZE(this_regs)]; };

struct this_client { struct list_head link; struct this_dev* dev; }; static const char* dev_mode[] = { "dec", "hex", "bin", "bin" }; static const char* dev_mode_fmt[] = { "%hd %hd %hd\n", "%#.4hx %#.4hx %#.4hx\n", };

static struct class* this_dev_class; static int this_major; static LIST_HEAD(this_dev_list); static DEFINE_SPINLOCK(this_dev_list_lock);

static inline int _read_reg(struct spi_device* spi, u8 addr) {   return spi_w8r8(spi, 0x80 | addr); }

static inline int _write_reg(struct spi_device* spi, u8 addr, u8 val) {   u8 tx[2] = { addr, val }; return spi_write(spi, tx, 2); }

struct this_proc_data { struct this_reg* reg; struct this_dev* dev; };

/* return index of the least significant non-zero bit */ static inline int lsb_idx(int n) { int i = 0; BUG_ON(n == 0); while ((n&1) == 0) { n>>=1; i++; } return i; }

static int this_read_proc(char *p, char **start, off_t off,   int count, int *eof, void *_data) {   struct this_proc_data* data = _data; struct this_dev* dev = data->dev; unsigned long flags, ss, so, se, us, uo; int i;

*eof = 1; *start = p;

if (off && data->reg->addr != PROC_PERF) return 0;

if ((data->reg->addr & 0x80) == 0) { i = _read_reg(dev->spi, data->reg->addr); if (i<0) p += sprintf(p, "%s read error %d\n", data->reg->name, i); else { int n;           if (data->reg->description) { p += sprintf(p, "%s", data->reg->name); for (n=0;n<20-strlen(data->reg->name);n++) p += sprintf(p, " "); }           if (data->reg->mask != 0xff) { u8 mask = data->reg->mask; int shift = lsb_idx(mask); i &= mask; *(unsigned*)&i >>= shift; p += data->reg->description ? sprintf(p, "%4d %s\n", i, data->reg->description) : sprintf(p, "%d\n", i); } else p += data->reg->description ? sprintf(p, "%#.2x %s\n", i, data->reg->description) : sprintf(p, "%#.2xd\n", i); }       return p-*start; }

switch (data->reg->addr) { case PROC_WHOAMI: p += sprintf(p, "device: " DEV_NAME_LONG "\n"); p += sprintf(p, "driver: " DRV_VERSION_STR "\n"); break; case PROC_MODE: spin_lock_irqsave(&dev->lock, flags); i = dev->flags & (FLAGS_MODE_BIN|FLAGS_MODE_HEX); spin_unlock_irqrestore(&dev->lock, flags); p += sprintf(p, "%s\n", dev_mode[i]); break; case PROC_STAT: spin_lock_irqsave(&dev->lock, flags); ss = dev->sensor_samples; so = dev->sensor_overruns; se = dev->sensor_errors; us = dev->user_samples; uo = dev->user_overruns; spin_unlock_irqrestore(&dev->lock, flags); p += sprintf(p,           "sensor: %ld samples %ld overruns %ld errors, "            "user: %ld samples %ld overruns\n",            ss, so, se, us, uo); break; case PROC_PERF: case PROC_PERF1: spin_lock_irqsave(&dev->lock, flags); ss = dev->sensor_samples; so = dev->sensor_overruns; se = dev->sensor_errors; us = dev->user_samples; uo = dev->user_overruns; spin_unlock_irqrestore(&dev->lock, flags); flags = schedule_timeout_interruptible(msecs_to_jiffies(1000)); if (flags) return flags; spin_lock_irqsave(&dev->lock, flags); ss = dev->sensor_samples - ss; so = dev->sensor_overruns - so; se = dev->sensor_errors - se; us = dev->user_samples - us; uo = dev->user_overruns - uo; spin_unlock_irqrestore(&dev->lock, flags); p += sprintf(p,           "sensor: %ld samples %ld overruns %ld errors, "            "user: %ld samples %ld overruns per second\n",            ss, so, se, us, uo); break; default: return -EINVAL; }   return p-*start; }

static int this_write_proc(struct file *file, const char __user *buf,   unsigned long count, void *_data) {   struct this_proc_data* data = _data; struct this_dev* dev = data->dev; char c, lbuf[16], *p = lbuf; unsigned long flags; int ret;

if (!capable(CAP_SYS_ADMIN)) return -EACCES; if (count>15) return -EINVAL; if (copy_from_user(lbuf, buf, count)) return -EFAULT; lbuf[count] = 0;

while(*p && !isalnum(*p)) p++;

if ((data->reg->addr & 0x80) == 0) { c = simple_strtoul(p, NULL, 0); if (data->reg->mask != 0xff) { u8 mask = data->reg->mask; c <<= lsb_idx(mask); c &= mask; ret = _read_reg(dev->spi, data->reg->addr); if (ret < 0) return ret; c |= ((u8)ret) & ~mask; }       ret = _write_reg(dev->spi, data->reg->addr, c); if (ret) return ret; else if (data->reg->addr == CTRL_REG1) dev->ctrl_reg1 = c;       else if (data->reg->addr == CTRL_REG2) { dev->ctrl_reg2 = c;           dev->enable_drdy_buf[1]  = c | CTRL_REG2_DRDY; dev->disable_drdy_buf[1] = c & ~CTRL_REG2_DRDY; }       return count; }

spin_lock_irqsave(&dev->lock, flags); switch (data->reg->addr) { case PROC_MODE: switch (tolower(*p)) { case 'b': dev->flags |= FLAGS_MODE_BIN; break; case 'h': dev->flags &= ~FLAGS_MODE_BIN; dev->flags |= FLAGS_MODE_HEX; break; case 'd': dev->flags &= ~(FLAGS_MODE_BIN|FLAGS_MODE_HEX); break; default: dev_info(&dev->spi->dev, "expect [dec|hex|bin]\n"); count = -EINVAL; }       break; case PROC_STAT: dev->sensor_samples = 0; dev->sensor_overruns = 0; dev->sensor_errors = 0; dev->user_samples = 0; dev->user_overruns = 0; break; default: count = -EINVAL; }   spin_unlock_irqrestore(&dev->lock, flags); return count; }

void this_data_ready(void *context) {   unsigned long flags; struct this_data* data = context; struct this_dev* dev = data->dev; u8 status_reg;

spin_lock_irqsave(&dev->lock, flags); dev->sensor_samples++; status_reg = data->buf[1]; if (((status_reg & STATUS_REG_DA  ) == 0) !=        ((status_reg & STATUS_REG_ZYXDA) == 0)) dev_warn(&dev->spi->dev,           "strange data available, STATUS_REG %#.2x\n", status_reg); if (status_reg & (STATUS_REG_OR | STATUS_REG_ZYXOR)) { dev->sensor_overruns++; if (((status_reg & STATUS_REG_OR  ) == 0) !=            ((status_reg & STATUS_REG_ZYXOR) == 0)) dev_dbg(&dev->spi->dev,               "strange data overrun, STATUS_REG %#.2x\n", status_reg); }   BUG_ON(data->message.status); BUG_ON(!list_empty(&data->message.queue)); list_add_tail(&data->message.queue, &dev->pending_data); wake_up_interruptible(&dev->waitq); spin_unlock_irqrestore(&dev->lock, flags); }
 * 1) ifdef DEBUG
 * 1) endif
 * 1) ifdef DEBUG
 * 1) endif

static inline struct this_dev* __get_this_dev_by_minor(int minor) {   struct this_dev* dev; list_for_each_entry(dev, &this_dev_list, link) if (dev->minor == minor) return dev; return NULL; }

static inline struct this_dev* get_this_dev_by_minor(int minor) {   struct this_dev* dev; spin_lock(&this_dev_list_lock); dev = __get_this_dev_by_minor(minor); spin_unlock(&this_dev_list_lock); return dev; }


 * 1) define DELAY_uSECS 0

static void this_data_init(struct this_data* data, struct this_dev* dev) {   spi_message_init(&data->message); data->message.complete = this_data_ready; data->message.context = data; data->message.is_dma_mapped = 1; data->dev = dev;

data->disable_drdy.len = 2; data->disable_drdy.tx_buf = dev->disable_drdy_buf; data->disable_drdy.cs_change = 1; data->disable_drdy.delay_usecs = DELAY_uSECS; data->disable_drdy.tx_dma = dma_map_single(&dev->spi->dev,       (void*)data->disable_drdy.tx_buf, 2, DMA_FROM_DEVICE);

data->addr.len = 1; data->addr.tx_buf = &dev->read_addr; BUG_ON(!IS_DMA_ALIGNED(data->addr.tx_buf)); data->addr.delay_usecs = DELAY_uSECS; data->addr.tx_dma = dma_map_single(&dev->spi->dev,       (void*)data->addr.tx_buf, 1, DMA_FROM_DEVICE);

data->data.len = 8; data->data.rx_buf = data->buf; BUG_ON(!IS_DMA_ALIGNED(data->data.rx_buf)); data->data.cs_change = 1; data->data.delay_usecs = DELAY_uSECS; data->data.rx_dma = dma_map_single(&dev->spi->dev,       data->data.rx_buf, 8, DMA_TO_DEVICE);

data->enable_drdy.len = 2; data->enable_drdy.tx_buf = dev->enable_drdy_buf; data->enable_drdy.cs_change = 1; data->enable_drdy.delay_usecs = DELAY_uSECS; data->enable_drdy.tx_dma = dma_map_single(&dev->spi->dev,       (void*)data->enable_drdy.tx_buf, 2, DMA_FROM_DEVICE);

spi_message_add_tail(&data->disable_drdy, &data->message); spi_message_add_tail(&data->addr, &data->message); spi_message_add_tail(&data->data, &data->message); spi_message_add_tail(&data->enable_drdy, &data->message);

list_add_tail(&data->message.queue, &dev->free_data); }

static void this_data_unmap(struct this_data* data) {   struct device* dev = &data->dev->spi->dev; dma_unmap_single(dev, data->disable_drdy.tx_dma, 2, DMA_FROM_DEVICE); dma_unmap_single(dev, data->addr.tx_dma, 1, DMA_FROM_DEVICE); dma_unmap_single(dev, data->data.rx_dma, 8, DMA_TO_DEVICE); dma_unmap_single(dev, data->enable_drdy.tx_dma, 2, DMA_FROM_DEVICE); }

static struct this_dev* new_this_dev(struct spi_device* spi) {   int i;    struct this_dev* dev; dev = kzalloc(sizeof *dev, GFP_KERNEL); if (!dev) return ERR_PTR(-ENOMEM);

dev->spi = spi;

spin_lock_init(&dev->lock); INIT_LIST_HEAD(&dev->free_data); INIT_LIST_HEAD(&dev->pending_data); dev->data = kzalloc(N_BUFS * sizeof(struct this_data), GFP_DMA); if (!dev->data) { kfree(dev); return ERR_PTR(-ENOMEM); }

dev->enable_drdy_buf[0] = CTRL_REG2; BUG_ON(!IS_DMA_ALIGNED(dev->enable_drdy_buf));

dev->disable_drdy_buf[0] = CTRL_REG2; BUG_ON(!IS_DMA_ALIGNED(dev->disable_drdy_buf));

dev->read_addr = 0xc0 | (STATUS_REG - 1);

for (i=0; idata[i], dev);

INIT_LIST_HEAD(&dev->clients); init_waitqueue_head(&dev->waitq);

spin_lock(&this_dev_list_lock); for(dev->minor = 0; dev->minor < 256; dev->minor++) if(!__get_this_dev_by_minor(dev->minor)) break; if (dev->minor < 256) list_add_tail(&dev->link, &this_dev_list); else { kfree(dev->data); kfree(dev); dev = NULL; }   spin_unlock(&this_dev_list_lock); return dev; }

static inline void return_this_dev(struct this_dev* dev) {   int i;    spin_lock(&this_dev_list_lock); list_del(&dev->link); spin_unlock(&this_dev_list_lock); for(i=0; idata[i]); kfree(dev->data); }

static inline struct this_dev* get_this_dev_by_spi(   struct spi_device *spi) {   struct this_dev* dev; spin_lock(&this_dev_list_lock); list_for_each_entry(dev, &this_dev_list, link) { if (dev->spi == spi) { spin_unlock(&this_dev_list_lock); return dev; }   }    spin_unlock(&this_dev_list_lock); return NULL; }

static ssize_t show_dev_name(struct class_device *class_dev, char *buf) {   struct this_dev* dev = get_this_dev_by_minor(MINOR(class_dev->devt)); if (!dev) return -ENODEV; return sprintf(buf, DEV_NAME "%d.%d\n",       dev->spi->master->bus_num, dev->spi->chip_select); }  static CLASS_DEVICE_ATTR(name, S_IRUGO, show_dev_name, NULL);

static ssize_t this_read(struct file *file, char __user *buf,    size_t count, loff_t *offset) {   int ret; unsigned long flags; struct this_client *client = file->private_data; struct this_dev *dev = client->dev; struct this_data* data; struct list_head* elm; u8 d[8]; /* dummy, status, 6 data */

again: if (list_empty(&dev->pending_data) && file->f_flags & O_NONBLOCK) return -EAGAIN;

ret = wait_event_interruptible_timeout(dev->waitq,       !list_empty(&dev->pending_data), msecs_to_jiffies(10)); if (ret == -ERESTARTSYS) return ret; if (ret == 0) { /* timed out */ u8 status = _read_reg(dev->spi, STATUS_REG); if (status == 0x77) { /* sucker */ spin_lock_irqsave(&dev->lock, flags); dev->sensor_errors++; spin_unlock_irqrestore(&dev->lock, flags); _read_reg(dev->spi, OUTZ_L); }       goto again; }   ret = wait_event_interruptible(dev->waitq,        !list_empty(&dev->pending_data)); if (ret) return ret;
 * 1) ifdef BUG_WORKAROUND
 * 1) else
 * 1) endif

spin_lock_irqsave(&dev->lock, flags); if (list_empty(&dev->pending_data)) { spin_unlock_irqrestore(&dev->lock, flags); goto again; }   elm = dev->pending_data.next; list_del_init(elm); data = list_entry(elm, struct this_data, message.queue); memcpy(d, data->buf, 8); list_add_tail(elm, &dev->free_data); spin_unlock_irqrestore(&dev->lock, flags);

if ((d[1] & (STATUS_REG_DA | STATUS_REG_ZYXDA)) == 0) { dev_warn(&dev->spi->dev, "bogus data\n"); goto again; }

dev->user_samples++; if (dev->flags & FLAGS_MODE_BIN) { copy_to_user(buf, d+2, 6); ret = 6; } else { ret = sprintf(buf, dev_mode_fmt[dev->flags & FLAGS_MODE_HEX],           *(u16*)&d[2], *(u16*)&d[4], *(u16*)&d[6]); }   *offset = 0; BUG_ON(ret > count); return ret; }

static unsigned int this_poll(struct file *file, poll_table *wait) {   struct this_client *client = file->private_data; struct this_dev *dev = client->dev;

poll_wait(file, &dev->waitq, wait);

if (!list_empty(&dev->pending_data)) return POLLIN | POLLRDNORM;

return 0; }

static int this_open(struct inode *inode, struct file *file) {   struct this_dev* dev; struct this_client* client; int first = 0;

dev = get_this_dev_by_minor(iminor(inode)); if (!dev) return -ENODEV; client = kzalloc(sizeof(*client), GFP_KERNEL); if (!client) return -ENOMEM; client->dev = dev; file->private_data = client; INIT_LIST_HEAD(&client->link); spin_lock(&dev->lock); if (list_empty(&dev->clients)) first = 1; list_add_tail(&client->link, &dev->clients); spin_unlock(&dev->lock); if (first) { dev->ctrl_reg1 |= CTRL_REG1_PD; _write_reg(dev->spi, CTRL_REG1, dev->ctrl_reg1); dev->ctrl_reg2 |= CTRL_REG2_DRDY; _write_reg(dev->spi, CTRL_REG2, dev->ctrl_reg2); }   return 0; }

static int this_release(struct inode *inode, struct file *file) {   struct this_client *client = file->private_data; struct this_dev *dev = client->dev; int last = 0;

spin_lock(&dev->lock); list_del(&client->link); if (list_empty(&dev->clients)) last = 1; spin_unlock(&dev->lock); kfree(client); file->private_data = NULL; if (last) { dev->ctrl_reg1 &= ~CTRL_REG1_PD; _write_reg(dev->spi, CTRL_REG1, dev->ctrl_reg1); dev->ctrl_reg2 &= ~CTRL_REG2_DRDY; _write_reg(dev->spi, CTRL_REG2, dev->ctrl_reg2); _read_reg(dev->spi, OUTX_L); _read_reg(dev->spi, OUTX_H); _read_reg(dev->spi, OUTY_L); _read_reg(dev->spi, OUTY_H); _read_reg(dev->spi, OUTZ_L); _read_reg(dev->spi, OUTZ_H); }   return 0; }

static irqreturn_t this_interrupt(int irq, void* context, struct pt_regs* regs) {   struct this_dev* dev = context; struct this_data* data; struct list_head* elm;

spin_lock(dev->lock); if (list_empty(&dev->free_data)) { dev->user_overruns++; BUG_ON(list_empty(&dev->pending_data)); elm = dev->pending_data.next; } else elm = dev->free_data.next; list_del_init(elm); spin_unlock(dev->lock);

data = list_entry(elm, struct this_data, message.queue); BUG_ON(data->message.status); if (spi_async(dev->spi, &data->message)) dev_err(&dev->spi->dev, "spi_async failed\n");

return IRQ_HANDLED; }

static int this_probe(struct spi_device *spi) {   struct this_dev* dev; int i;

//spi->max_speed_hz = 3686400/5; i = spi_setup(spi); if (i) { dev_err(&spi->dev, "spi_setup failed with status %d\n", i); return i;   } /*    * FIXME: after system restart, first read returns garbage. * Perhaps an indication of a bigger problem. */   i = _read_reg(spi, WHO_AM_I); i = _read_reg(spi, WHO_AM_I); if (i < 0) { dev_err(&spi->dev, "probing: SPI IO failed, status %d\n", i); return i;   } if (i != I_AM_LIS3LV02DQ) { dev_warn(&spi->dev, "WHO_AM_I is %#.2x, expected %#.2x\n",           i, I_AM_LIS3LV02DQ); return -ENODEV; } else dev_info(&spi->dev, "device found\n");

dev = new_this_dev(spi); if (IS_ERR(dev)) { dev_err(&spi->dev, "failed to allocate dev\n"); return PTR_ERR(dev); }

dev->class_dev = class_device_create(this_dev_class, NULL,       MKDEV(this_major, dev->minor), &spi->dev,        DEV_NAME "%d.%d", spi->master->bus_num, spi->chip_select); if (!dev->class_dev) { dev_err(&spi->dev, "failed to create class\n"); goto error; }   class_device_create_file(dev->class_dev, &class_device_attr_name);

i = _read_reg(spi, CTRL_REG1); if (i<0) goto error_class_destroy; dev->ctrl_reg1 = i & ~CTRL_REG1_PD; if (_write_reg(spi, CTRL_REG1, dev->ctrl_reg1) < 0) goto error_class_destroy;

i = _read_reg(spi, CTRL_REG2); if (i<0) goto error_class_destroy; dev->ctrl_reg2 = i & ~CTRL_REG2_DRDY; dev->enable_drdy_buf[1] = i | CTRL_REG2_DRDY; dev->disable_drdy_buf[1] = i & ~CTRL_REG2_DRDY; if (_write_reg(spi, CTRL_REG2, dev->ctrl_reg2) < 0) goto error_class_destroy;

_read_reg(spi, OUTX_L); _read_reg(spi, OUTX_H); _read_reg(spi, OUTY_L); _read_reg(spi, OUTY_H); _read_reg(spi, OUTZ_L); _read_reg(spi, OUTZ_H);

i = request_irq(spi->irq, this_interrupt, IRQF_TRIGGER_RISING,                   dev->class_dev->class_id, dev); if (i) { dev_err(&spi->dev, "request_irq failed with status %d\n", i); goto error_class_destroy; }

dev->proc_parent = create_proc_entry(dev->class_dev->class_id,       S_IFDIR | S_IRUGO | S_IXUGO, NULL); if (!dev->proc_parent) { dev_err(&spi->dev, "failed to create /proc/%s\n",           dev->class_dev->class_id); goto error_free_irq; }   for(i=0; i<ARRAY_SIZE(this_regs); i++) { dev->proc_reg[i] = create_proc_entry(            this_regs[i].name, 0644, dev->proc_parent); if (dev->proc_reg[i]) { struct this_proc_data* data = kmalloc(sizeof *data, GFP_KERNEL); if (data) { data->reg = &this_regs[i]; data->dev = dev; }           dev->proc_reg[i]->data = data; dev->proc_reg[i]->read_proc = this_read_proc; dev->proc_reg[i]->write_proc = this_write_proc; } else dev_err(&spi->dev, "failed to create /proc/%s/%s\n",               dev->class_dev->class_id, this_regs[i].name); }

return 0;

error_free_irq: free_irq(spi->irq, dev); error_class_destroy: class_device_destroy(this_dev_class, MKDEV(this_major, dev->minor)); error: return_this_dev(dev); kfree(dev); return -ENODEV; }

static void this_shutdown(struct spi_device* spi) {   dev_dbg(&spi->dev, "shutdown\n"); }

static int this_suspend(struct spi_device* spi, pm_message_t state) {   struct this_dev* dev = get_this_dev_by_spi(spi); switch (state.event) { case PM_EVENT_ON: dev_dbg(&spi->dev, "power on\n"); dev->ctrl_reg1 |= CTRL_REG1_PD; return _write_reg(dev->spi, CTRL_REG1, dev->ctrl_reg1); case PM_EVENT_FREEZE: dev_dbg(&spi->dev, "power freeze\n"); dev->ctrl_reg2 &= ~CTRL_REG2_DRDY; return _write_reg(dev->spi, CTRL_REG2, dev->ctrl_reg2); case PM_EVENT_SUSPEND: dev_dbg(&spi->dev, "power suspend\n"); dev->ctrl_reg1 &= ~CTRL_REG1_PD; return _write_reg(dev->spi, CTRL_REG1, dev->ctrl_reg1); default: dev_dbg(&spi->dev, "power %d\n", state.event); BUG_ON(1); }   return 0; }
 * 1) ifdef CONFIG_PM
 * 2) warning TODO all power management

static int this_resume(struct spi_device *spi) {   dev_dbg(&spi->dev, "power resume\n"); return this_suspend(spi, PMSG_ON); }


 * 1) else
 * 2) define this_suspend NULL
 * 3) define this_resume NULL
 * 4) endif

int this_remove(struct spi_device* spi) {   struct this_dev* dev = get_this_dev_by_spi(spi); unsigned long flags; int i;   if (!dev) return -ENODEV; spin_lock_irqsave(&dev->lock, flags); if (!list_empty(&dev->clients)) { spin_unlock_irqrestore(&dev->lock, flags); return -EBUSY; }   free_irq(dev->spi->irq, dev); return_this_dev(dev); spin_unlock_irqrestore(&dev->lock, flags);

BUG_ON(!dev->proc_parent); for(i=0; i < ARRAY_SIZE(this_regs); i++) { if (dev->proc_reg[i]) { kfree(dev->proc_reg[i]->data); remove_proc_entry(this_regs[i].name, dev->proc_parent); }   }    remove_proc_entry(dev->class_dev->class_id, NULL);

class_device_destroy(this_dev_class, MKDEV(this_major, dev->minor)); kfree(dev); return 0; }

static struct file_operations this_fops = { .owner     = THIS_MODULE, .llseek    = no_llseek, .read      = this_read, .open      = this_open, .release   = this_release, .poll      = this_poll, };

static struct spi_driver this_driver = { .driver = { .name = DEV_NAME, .bus = &spi_bus_type, .owner = THIS_MODULE, },    .probe = this_probe, .remove = __devexit_p(this_remove), .shutdown = this_shutdown, .suspend = this_suspend, .resume = this_resume, };

static int __init this_init(void) {   int res; res = register_chrdev(0, DEV_NAME, &this_fops); if (IS_ERR_VALUE(res)) goto out; this_major = res; this_dev_class = class_create(THIS_MODULE, DEV_NAME "-dev"); if (IS_ERR(this_dev_class)) goto out_unreg_chrdev; res = spi_register_driver(&this_driver); if (res) goto out_unreg_class; return 0;

out_unreg_class: class_destroy(this_dev_class); out_unreg_chrdev: unregister_chrdev(this_major, DEV_NAME); out: printk(KERN_ERR DEV_NAME ": driver init failed\n"); return res; } module_init(this_init);

static void __exit this_exit(void) {   spi_unregister_driver(&this_driver); class_destroy(this_dev_class); unregister_chrdev(this_major, DEV_NAME); } module_exit(this_exit);