Sorry all,

I was a bit over enthusiastic when posting this, there are quite a few 
bugs in the removal code.
I'll post a working version in a day or two.

Jonathan
> from Jonathan Cameron <[EMAIL PROTECTED]>
>
> Initial support for ST Microelectronics LISS3L02DQ accelerometer via SPI
>
> Signed-off-by: Jonathan Cameron <[EMAIL PROTECTED]>
>
> ---
>
> This is my first attempt at writing a driver, so I would appreciate 
> any feedback / suggestions people may wish to offer.
>
>  drivers/spi/Kconfig           |   15
>  drivers/spi/LIS3L02DQ.c       |  597 ++++++++++++++++++++++++++++++++
>  include/linux/spi/LIS3L02DQ.h |  140 +++++++
>  3 files changed, 752 insertions(+)
>
> --- a/include/linux/spi/LIS3L02DQ.h   1970-01-01 01:00:00.000000000 +0100
> +++ b/include/linux/spi/LIS3L02DQ.h   2008-04-25 15:55:16.000000000 +0100
> @@ -0,0 +1,140 @@
> + 
> +
> +#ifndef _LIS3L02DQ_H_
> +#define _LIS3L02DQ_H_
> +#define LIS3L02DQ_READ_REG(a) a | 0x80
> +#define LIS3L02DQ_WRITE_REG(a) a 
> +
> +/* Calibration parameters */
> +#define LIS3L02DQ_REG_OFFSET_X_ADDRESS 0x16
> +#define LIS3L02DQ_REG_OFFSET_Y_ADDRESS 0x17
> +#define LIS3L02DQ_REG_OFFSET_Z_ADDRESS 0x18
> +
> +#define LIS3L02DQ_REG_GAIN_X_ADDRESS 0x19
> +#define LIS3L02DQ_REG_GAIN_Y_ADDRESS 0x1A
> +#define LIS3L02DQ_REG_GAIN_Z_ADDRESS 0x1B
> +
> +/* Control Register (1 of 2) */
> +#define LIS3L02DQ_REG_CTRL_1_ADDRESS 0x20
> +/* Power ctrl - either bit set corresponds to on*/
> +#define LIS3L02DQ_REG_CTRL_1_PD_ON 0xC0
> +
> +/* Decimation Factor  */
> +#define LIS3L02DQ_REG_CTRL_1_DF_128 0x00
> +#define LIS3L02DQ_REG_CTRL_1_DF_64  0x10
> +#define LIS3L02DQ_REG_CTRL_1_DF_32  0x20
> +#define LIS3L02DQ_REG_CTRL_1_DF_8   0x10 | 0x20
> +
> +/* Self Test Enable */ 
> +#define LIS3L02DQ_REG_CTRL_1_SELF_TEST_ON 0x08
> +
> +/* Axes enable ctrls */
> +#define LIS3L02DQ_REG_CTRL_1_AXES_Z_ENABLE  0x04
> +#define LIS3L02DQ_REG_CTRL_1_AXES_Y_ENABLE  0x02
> +#define LIS3L02DQ_REG_CTRL_1_AXES_X_ENABLE  0x01
> +
> +/* Control Register (2 of 2) */
> +#define LIS3L02DQ_REG_CTRL_2_ADDRESS 0x21
> +
> +/* Block Data Update only after MSB and LSB read */
> +#define LIS3L02DQ_REG_CTRL_2_BLOCK_UPDATE 0x40
> + 
> +/* Set to big endian output */
> +#define LIS3L02DQ_REG_CTRL_2_BIG_ENDIAN   0x20
> +
> +/* Reboot memory content */
> +#define LIS3L02DQ_REG_CTRL_2_REBOOT_MEMORY 0x10
> +
> +/* Interupt Enable - applies data ready to the RDY pad */
> +#define LIS3L02DQ_REG_CTRL_2_ENABLE_INTERUPT 0x08
> +
> +/* Enable Data Ready Generation - relationship with previous unclear in docs 
> */
> +#define LIS3L02DQ_REG_CTRL_2_ENABLE_DATA_READY_GENERATION 0x04
> +
> +/* SPI 3 wire mode */
> +#define LIS3L02DQ_REG_CTRL_2_THREE_WIRE_SPI_MODE 0x02
> +
> +/* Data alignment, default is 12 bit right justified  - option for 16 bit 
> left justified */
> +#define LIS3L02DQ_REG_CTRL_2_DATA_ALIGNMENT_16_BIT_LEFT_JUSTIFIED 0x01
> +
> +/* Interupt related stuff */
> +#define LIS3L02DQ_REG_WAKE_UP_CFG_ADDRESS 0x23
> +
> +/* Switch from or combination fo conditions to and */
> +#define LIS3L02DQ_REG_WAKE_UP_CFG_BOOLEAN_AND 0x80
> +
> +/* Latch interupt request, if on ack must be given by reading the ack 
> register */
> +#define LIS3L02DQ_REG_WAKE_UP_CFG_LATCH_SRC 0x40
> +
> +/* Z Interupt on High (above threshold)*/
> +#define LIS3L02DQ_REG_WAKE_UP_CFG_INTERRUPT_Z_HIGH 0x20
> +/* Z Interupt on Low */
> +#define LIS3L02DQ_REG_WAKE_UP_CFG_INTERRUPT_Z_LOW 0x10
> +/* Y Interupt on High */
> +#define LIS3L02DQ_REG_WAKE_UP_CFG_INTERRUPT_Y_HIGH 0x08
> +/* Y Interupt on Low */
> +#define LIS3L02DQ_REG_WAKE_UP_CFG_INTERRUPT_Y_LOW 0x04
> +/* X Interupt on Hight */
> +#define LIS3L02DQ_REG_WAKE_UP_CFG_INTERRUPT_X_HGIH 0x02
> +/* X Interupt on Low */
> +#define LIS3L02DQ_REG_WAKT_UP_CFG_INTERRUPT_X_LOW 0x01
> +
> +/* Register that gives description of what caused interupt - latched if set 
> in CFG_ADDRES */
> +#define LIS3L02DQ_REG_WAKE_UP_SRC_ADDRESS 0x24
> +/* top bit ignored */
> +/* Interupt Active */
> +#define LIS3L02DQ_REG_WAKE_UP_SRC_INTERRUPT_ACTIVATED 0x40
> +/* Interupts that have been triggered */
> +#define LIS3L02DQ_REG_WAKE_UP_SRC_INTERRUPT_Z_HIGH 0x20
> +#define LIS3L02DQ_REG_WAKE_UP_SRC_INTERRUPT_Z_LOW 0x10
> +#define LIS3L02DQ_REG_WAKE_UP_SRC_INTERRUPT_Y_HIGH 0x08
> +#define LIS3L02DQ_REG_WAKE_UP_SRC_INTERRUPT_Y_LOW 0x04
> +#define LIS3L02DQ_REG_WAKE_UP_SRC_INTERRUPT_X_HIGH 0x02
> +#define LIS3L02DQ_REG_WAKE_UP_SRC_INTERRUPT_X_LOW 0x01
> +
> +#define LIS3L02DQ_REG_WAKE_UP_ACK_ADDRESS 0x25
> +
> +/* Status register */
> +#define LIS3L02DQ_REG_STATUS_ADDRESS 0x27
> +/* XYZ axis data overrun - first is all overrun? */
> +#define LIS3L02DQ_REG_STATUS_XYZ_OVERRUN 0x80
> +#define LIS3L02DQ_REG_STATUS_Z_OVERRUN 0x40
> +#define LIS3L02DQ_REG_STATUS_Y_OVERRUN 0x20
> +#define LIS3L02DQ_REG_STATUS_X_OVERRUN 0x10
> +/* XYZ new data available - first is all 3 available? */
> +#define LIS3L02DQ_REG_STATUS_XYZ_NEW_DATA 0x08
> +#define LIS3L02DQ_REG_STATUS_Z_NEW_DATA 0x04
> +#define LIS3L02DQ_REG_STATUS_Y_NEW_DATA 0x02
> +#define LIS3L02DQ_REG_STATUS_X_NEW_DATA 0x01
> +
> +/* The accelerometer readings - low and high bytes.
> +Form of high byte dependant on justification set in ctrl reg */
> +#define LIS3L02DQ_REG_OUT_X_L_ADDRESS 0x28
> +#define LIS3L02DQ_REG_OUT_X_H_ADDRESS 0x29
> +#define LIS3L02DQ_REG_OUT_Y_L_ADDRESS 0x2A
> +#define LIS3L02DQ_REG_OUT_Y_H_ADDRESS 0x2B
> +#define LIS3L02DQ_REG_OUT_Z_L_ADDRESS 0x2C
> +#define LIS3L02DQ_REG_OUT_Z_H_ADDRESS 0x2D
> +
> +/* Threshold values for all axes and both above and below thresholds - i.e. 
> there is only one value */
> +#define LIS3L02DQ_REG_THS_L_ADDRESS 0x2E
> +#define LIS3L02DQ_REG_THS_H_ADDRESS 0x2D
> +
> +
> +/* SPI MODE */
> +#define LIS3L02DQ_SPI_MODE SPI_MODE_3
> +
> +#define LIS3L02DQ_DEFAULT_CTRL1 LIS3L02DQ_REG_CTRL_1_PD_ON \
> +     | LIS3L02DQ_REG_CTRL_1_AXES_Z_ENABLE \
> +     | LIS3L02DQ_REG_CTRL_1_AXES_Y_ENABLE \
> +     | LIS3L02DQ_REG_CTRL_1_AXES_X_ENABLE \
> +     | LIS3L02DQ_REG_CTRL_1_DF_128 
> +
> +#define LIS3L02DQ_DEFAULT_CTRL2 
> LIS3L02DQ_REG_CTRL_2_ENABLE_DATA_READY_GENERATION \
> +     | LIS3L02DQ_REG_CTRL_2_BLOCK_UPDATE
> +
> +struct LIS3L02DQ_platform_data
> +{
> +  unsigned data_ready_gpio;
> +};
> +#endif /* _LIS3L02DQ_H_ */ 
> --- a/drivers/spi/Kconfig     2008-04-17 03:49:44.000000000 +0100
> +++ b/drivers/spi/Kconfig     2008-04-24 19:16:32.000000000 +0100
> @@ -239,6 +239,21 @@ config SPI_TLE62X0
>         sysfs interface, with each line presented as a kind of GPIO
>         exposing both switch control and diagnostic feedback.
>  
> +config SPI_LIS3L02DQ
> +       tristate "STMicroelectronics LIS3L02DQ Accelerometer" 
> +       depends on SPI_MASTER && SYSFS 
> +       help
> +         SPI driver for the STMicroelectrincs LIS3L02DQ 3-Axis 2g digital 
> +      output linear accelerometer.  This provides a sysfs interface.
> +
> +config SPI_LIS3L02DQ_GPIO_INTERRUPT
> +       bool "Use data ready interrupt in conjunction with a ring buffer"
> +       depends on SPI_LIS3L02DQ && GENERIC_GPIO
> +       help
> +      Select this option if you want to capture the maximum possible 
> +      amount of data from you accelerometer.
> +       
> +       
>  #
>  # Add new SPI protocol masters in alphabetical order above this line
>  #
> --- a/drivers/spi/LIS3L02DQ.c 1970-01-01 01:00:00.000000000 +0100
> +++ b/drivers/spi/LIS3L02DQ.c 2008-04-25 15:55:59.000000000 +0100
> @@ -0,0 +1,597 @@
> +/* 
> + * LISL02DQ.c -- support STMicroelectronics LISD02DQ 
> + *               3d 2g Linear Accelerometers via SPI
> + *
> + * Copyright (c) 2007 Jonathan Cameron <[EMAIL PROTECTED]>
> + *
> + * Loosely based upon tle62x0.c 
> + *
> + * 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.
> + *
> + * This driver has two modes, one is interrupt based, the other on demand */
> +
> +#include <linux/device.h>
> +#include <linux/kernel.h>
> +#include <linux/spi/spi.h>
> +#include <linux/spi/LIS3L02DQ.h>
> +
> +
> +#ifdef CONFIG_SPI_LIS3L02DQ_GPIO_INTERRUPT
> +#include <linux/interrupt.h>
> +#include <linux/irq.h>
> +#include <linux/gpio.h>
> +#include <linux/workqueue.h>
> +
> +#define LIS3L02DQ_BUFFER_LENGTH 100
> +#endif /* CONFIG_SPI_LIS3L02DQ_GPIO_INTERRUPT */
> +
> +/* Driver state including the rx / tx buffers and interrupt work structs + 
> ring buffer pointers */
> +struct LIS3L02DQ_state {
> +     struct spi_device*     us;
> +     unsigned char          tx_buff[2*6];
> +     unsigned char          rx_buff[2*6];
> +     
> +#ifdef CONFIG_SPI_LIS3L02DQ_GPIO_INTERRUPT
> +     struct work_struct     work;
> +     bool                   inter;
> +     uint8_t                ring_buffer[LIS3L02DQ_BUFFER_LENGTH*6];
> +     uint8_t*               read_pointer;
> +     uint8_t*               write_pointer;
> +     uint8_t*               last_written_pointer;
> +#endif /* CONFIG_SPI_LIS3L02DQ_GPIO_INTERRUPT */
> +};
> +
> +static const char read_all_tx_array[12] =
> +{
> +     0, LIS3L02DQ_READ_REG(LIS3L02DQ_REG_OUT_X_L_ADDRESS),
> +     0, LIS3L02DQ_READ_REG(LIS3L02DQ_REG_OUT_X_H_ADDRESS),
> +     0, LIS3L02DQ_READ_REG(LIS3L02DQ_REG_OUT_Y_L_ADDRESS),
> +     0, LIS3L02DQ_READ_REG(LIS3L02DQ_REG_OUT_Y_H_ADDRESS),
> +     0, LIS3L02DQ_READ_REG(LIS3L02DQ_REG_OUT_Z_L_ADDRESS),
> +     0, LIS3L02DQ_READ_REG(LIS3L02DQ_REG_OUT_Z_H_ADDRESS),   
> +};
> +
> +
> +static int LIS3L02DQ_read_all(struct LIS3L02DQ_state* st)
> +{
> +     /* Sadly the device appears to require deselection between reading the 
> different registers */
> +     struct spi_transfer xfers[] = {
> +             /* x low byte */
> +             {
> +                     .tx_buf = read_all_tx_array,
> +                     .rx_buf = st->rx_buff,
> +                     .bits_per_word = 16,
> +                     .len = 2,
> +             },
> +             /* x high byte */
> +             {
> +                     .tx_buf = read_all_tx_array+2,
> +                     .rx_buf = st->rx_buff+2,
> +                     .bits_per_word = 16,
> +                     .len = 2,
> +             },
> +             /* y low byte */
> +             {
> +                     .tx_buf = read_all_tx_array+4,
> +                     .rx_buf = st->rx_buff+4,
> +                     .bits_per_word = 16,
> +                     .len = 2,
> +             },
> +             /* y high byte */
> +             {
> +                     .tx_buf = read_all_tx_array+6,
> +                     .rx_buf = st->rx_buff+6,
> +                     .bits_per_word = 16,
> +                     .len = 2,
> +             },
> +             /* z low byte */
> +             {
> +                     .tx_buf = read_all_tx_array+8,
> +                     .rx_buf = st->rx_buff+8,
> +                     .bits_per_word = 16,
> +                     .len = 2,
> +             },
> +             /* z high byte */
> +             {
> +                     .tx_buf = read_all_tx_array+10,
> +                     .rx_buf = st->rx_buff+10,
> +                     .bits_per_word = 16,
> +                     .len = 2,
> +             },
> +     };
> +     struct spi_message msg;
> +     int ret;
> +     memset(st->rx_buff, 0, sizeof st->rx_buff);
> +     
> +     /* After these are trasmitted, the rx_buff should have values in 
> alternate bytes */
> +     spi_message_init(&msg);
> +     
> +     spi_message_add_tail(&xfers[0], &msg);
> +     spi_message_add_tail(&xfers[2], &msg);
> +     spi_message_add_tail(&xfers[4], &msg);
> +     spi_message_add_tail(&xfers[1], &msg);
> +     spi_message_add_tail(&xfers[3], &msg);
> +     spi_message_add_tail(&xfers[5], &msg);
> +     ret = spi_sync(st->us, &msg);
> +     if(ret) {
> +             dev_err(&st->us->dev, "problem with get all accels");
> +             goto err_ret;
> +     }
> +err_ret:
> +     return ret;
> +}
> +
> +#ifdef CONFIG_SPI_LIS3L02DQ_GPIO_INTERRUPT
> +
> +/* A fairly inellegant way of ripping the contents of the ring buffer and 
> ensuring only a 
> + * valid set of readings are output */
> +static ssize_t LIS3L02DQ_rip_buffer(struct device* dev, struct 
> device_attribute *attr, char *buf)
> +{
> +     int len = 0, elements, i, dead_offset = 0;
> +     uint8_t data_dump[LIS3L02DQ_BUFFER_LENGTH*6];
> +     uint8_t *initial_read_p, *initial_write_p, *current_read_p, *end_read_p;
> +     struct LIS3L02DQ_state *st = dev_get_drvdata(dev);
> +     uint16_t temp;
> +     
> +     /* Get a consistent pair of read and write pointers */
> +     initial_read_p = st->read_pointer;
> +     
> +     /* Occurs if nothing has yet been placed in the ring buffer */
> +     if(unlikely(initial_read_p == 0))
> +             goto err;
> +     
> +     initial_write_p = st->write_pointer;
> +     while( initial_read_p != st->read_pointer || initial_write_p != 
> st->write_pointer)      {
> +             initial_read_p = st->read_pointer;
> +             initial_write_p = st->write_pointer;
> +     }
> +     if ( initial_write_p > initial_read_p ) { 
> +             elements = (initial_write_p - initial_read_p); /* No of bytes 
> to copy */
> +             memcpy(data_dump, initial_read_p, elements);
> +     } else {
> +             elements = st->ring_buffer + LIS3L02DQ_BUFFER_LENGTH*6 - 
> initial_read_p;
> +             memcpy(data_dump, initial_read_p, elements);
> +             
> +             memcpy(data_dump+elements, st->ring_buffer, initial_write_p - 
> st->ring_buffer);
> +             elements += initial_write_p - st->ring_buffer;
> +     }
> +     
> +     end_read_p = st->read_pointer;
> +     
> +     if( initial_read_p <= end_read_p )
> +             dead_offset = end_read_p - initial_read_p;
> +     else
> +             dead_offset = st->ring_buffer + LIS3L02DQ_BUFFER_LENGTH*6 - 
> initial_read_p 
> +                     + end_read_p - st->ring_buffer; 
> +     
> +     /* Possible issue here is the readpointer may have changed.
> +      * It could in theory have passed the initial write pointer.*/
> +     st->read_pointer = initial_write_p;
> +     
> +     for(current_read_p = data_dump + dead_offset; current_read_p < 
> data_dump + elements;  current_read_p+=6) {
> +             for(i = 0; i < 3; i++) {
> +                     temp = (((uint16_t)((current_read_p[2*i+1]))) << 8) | 
> (uint16_t)(current_read_p[2*i]);
> +                     len += sprintf(len+buf, "%d ", *((int16_t*)(&temp)));
> +             }       
> +     }
> +     len += sprintf(len+buf, "\n");
> +     return len;
> +err:
> +     return 0;
> +}
> +#endif /* CONFIG_SPI_LIS3L02DQ_GPIO_INTERRUPT */
> +
> +
> +/* If in interrupt triggered mode this sysfs function will output the latest 
> finished element from the ringbuffer */
> +/* If in direct access mode it will simply read the output registers of the 
> device. 
> + * Be aware that the device may be in blocking mode so results are a little 
> unpredictable */
> +
> +static ssize_t LIS3L02DQ_scan(struct device *dev, struct device_attribute 
> *attr, char *buf)
> +{
> +     int i,len = 0;
> +     uint16_t temp;
> +     struct LIS3L02DQ_state *st = dev_get_drvdata(dev);
> +#ifdef CONFIG_SPI_LIS3L02DQ_GPIO_INTERRUPT
> +     if(likely(st->last_written_pointer !=0)) {
> +             /* conditions in which this may go wrong ?*/
> +             uint8_t* written_p = st->last_written_pointer;
> +             for(i = 0; i < 3; i++) {
> +                     temp = (((uint16_t)((written_p[2*i+1]))) << 8) | 
> (uint16_t)(written_p[2*i]);
> +                     len += sprintf(len+buf, "%d ", *((int16_t*)(&temp)));
> +             }
> +     }
> +#else
> +     LIS3L02DQ_read_all(st);
> +     for(i = 0; i < 3; i++) {
> +             temp = (((uint16_t)((st->rx_buff[4*i+2]))) << 8) | 
> (uint16_t)(st->rx_buff[4*i]);
> +             len += sprintf(len+buf, "%d ", *((int16_t*)(&temp)));
> +     }
> +#endif /* else CONFIG_SPI_LIS3L02DQ_GPIO_INTERRUPT */
> +     len += sprintf(len+buf, "\n");
> +     return len;
> +}
> +
> +static int8_t LIS3L02DQ_read_register_int8_t(struct device *dev, uint8_t 
> reg_address)
> +{
> +     int8_t val, ret;
> +     struct spi_message msg;
> +     struct LIS3L02DQ_state *st = dev_get_drvdata(dev);
> +     struct spi_transfer xfer = {
> +             .tx_buf = st->tx_buff,
> +             .rx_buf = st->rx_buff,
> +             .bits_per_word = 16,
> +             .len = 2,
> +     };
> +     st->tx_buff[1] = LIS3L02DQ_READ_REG(reg_address);
> +     spi_message_init(&msg);
> +     spi_message_add_tail(&xfer, &msg);
> +     ret = spi_sync(st->us, &msg);
> +     if( ret ) {
> +             dev_err(&st->us->dev, "problem with get x offset");
> +             goto err_ret;
> +     }
> +     val = st->rx_buff[0];
> +     return val;
> +     /* Unfortunately the result can be negative so passing error back not a 
> good idea */
> +err_ret:
> +     return 0;  
> +}
> +
> +static int LIS3L02DQ_write_register_int8_t(struct device* dev, uint8_t 
> reg_address, int8_t value)
> +{
> +     struct spi_message msg;
> +     struct LIS3L02DQ_state *st = dev_get_drvdata(dev);
> +     struct spi_transfer xfer = {
> +             .tx_buf = st->tx_buff,
> +             .rx_buf = NULL,
> +             .bits_per_word = 16,
> +             .len = 2,
> +     };
> +     int ret;
> +     st->tx_buff[1] = LIS3L02DQ_WRITE_REG(reg_address);
> +     st->tx_buff[0] = value;
> +     spi_message_init(&msg);
> +     spi_message_add_tail(&xfer, &msg);
> +     ret = spi_sync(st->us, &msg);
> +     if( ret ) {
> +             dev_err(&st->us->dev, "problem with writing 8 bit register");
> +             goto err_ret;
> +     }
> +     return 0;
> +err_ret:
> +     return ret;
> +}
> +
> +static ssize_t LIS3L02DQ_read_x_offset(struct device *dev, struct 
> device_attribute *attr, char *buf)
> +{    
> +     int val, len ;
> +     val = LIS3L02DQ_read_register_int8_t(dev, 
> LIS3L02DQ_REG_OFFSET_X_ADDRESS);
> +     len = sprintf(buf, "%d\n", val);
> +     return len;
> +}
> +
> +static ssize_t LIS3L02DQ_read_y_offset(struct device *dev, struct 
> device_attribute *attr, char *buf)
> +{
> +     int val, len;
> +     val = LIS3L02DQ_read_register_int8_t(dev, 
> LIS3L02DQ_REG_OFFSET_Y_ADDRESS);
> +     len = sprintf(buf, "%d\n", val);
> +     return len;
> +}
> +
> +static ssize_t LIS3L02DQ_read_z_offset(struct device *dev, struct 
> device_attribute *attr, char *buf)
> +{
> +     int val, len;
> +     val = LIS3L02DQ_read_register_int8_t(dev, 
> LIS3L02DQ_REG_OFFSET_Z_ADDRESS);
> +     len = sprintf(buf, "%d\n", val);
> +     return len;
> +}
> +
> +static ssize_t LIS3L02DQ_read_x_gain(struct device *dev, struct 
> device_attribute *attr, char *buf)
> +{
> +     int val, len;
> +     val = LIS3L02DQ_read_register_int8_t(dev, LIS3L02DQ_REG_GAIN_X_ADDRESS);
> +     len = sprintf(buf, "%d\n", val);
> +     return len;
> +}
> +
> +static ssize_t LIS3L02DQ_read_y_gain(struct device *dev, struct 
> device_attribute *attr, char *buf)
> +{
> +     int val, len;
> +     val = LIS3L02DQ_read_register_int8_t(dev, LIS3L02DQ_REG_GAIN_Y_ADDRESS);
> +     len = sprintf(buf, "%d\n", val);
> +     return len;
> +}
> +
> +static ssize_t LIS3L02DQ_read_z_gain(struct device *dev, struct 
> device_attribute *attr, char *buf)
> +{
> +     int val, len;
> +     val = LIS3L02DQ_read_register_int8_t(dev, LIS3L02DQ_REG_GAIN_Z_ADDRESS);
> +     len = sprintf(buf, "%d\n", val);
> +     return len;
> +}
> +
> +static ssize_t LIS3L02DQ_write_x_offset(struct device *dev, struct 
> device_attribute *attr, const char *buf, size_t len)
> +{
> +     int ret,val;
> +     char *endp;
> +     val = simple_strtol(buf, &endp, 0);
> +     ret = LIS3L02DQ_write_register_int8_t(dev, 
> LIS3L02DQ_REG_OFFSET_X_ADDRESS, val);
> +     if( ret )
> +             goto err_ret;
> +     return len;
> +err_ret:
> +     return ret;
> +}
> +
> +static ssize_t LIS3L02DQ_write_y_offset(struct device *dev, struct 
> device_attribute *attr, const char *buf, size_t len)
> +{
> +     int ret,val;
> +     char *endp;
> +     val = simple_strtol(buf, &endp, 0);
> +     ret = LIS3L02DQ_write_register_int8_t(dev, 
> LIS3L02DQ_REG_OFFSET_Y_ADDRESS, val);
> +     if( ret )
> +             goto err_ret;
> +     return len;
> +err_ret:
> +     return ret;
> +
> +}
> +
> +static ssize_t LIS3L02DQ_write_z_offset(struct device *dev, struct 
> device_attribute *attr, const char *buf, size_t len)
> +{
> +     int ret,val;
> +     char *endp;
> +     val = simple_strtol(buf, &endp, 0);
> +     ret = LIS3L02DQ_write_register_int8_t(dev, 
> LIS3L02DQ_REG_OFFSET_Z_ADDRESS, val);
> +     if( ret )
> +             goto err_ret;
> +     return len;
> +err_ret:
> +     return ret;
> +}
> +
> +static ssize_t LIS3L02DQ_write_x_gain(struct device *dev, struct 
> device_attribute *attr, const char *buf, size_t len)
> +{
> +     int ret,val;
> +     char *endp;
> +     val = simple_strtol(buf, &endp, 0);
> +     ret = LIS3L02DQ_write_register_int8_t(dev, 
> LIS3L02DQ_REG_GAIN_X_ADDRESS, val);
> +     if( ret )
> +             goto err_ret;
> +     return len;
> +err_ret:
> +     return ret;
> +}
> +
> +static ssize_t LIS3L02DQ_write_y_gain(struct device *dev, struct 
> device_attribute *attr, const char *buf, size_t len)
> +{
> +     int ret,val;
> +     char *endp;
> +     val = simple_strtol(buf, &endp, 0);
> +     ret = LIS3L02DQ_write_register_int8_t(dev, 
> LIS3L02DQ_REG_GAIN_Y_ADDRESS, val);
> +     if( ret )
> +             goto err_ret;
> +     return len;
> +err_ret:
> +     return ret;
> +}
> +
> +static ssize_t LIS3L02DQ_write_z_gain(struct device *dev, struct 
> device_attribute *attr, const char *buf, size_t len)
> +{
> +     int ret,val;
> +     char *endp;
> +     val = simple_strtol(buf, &endp, 0);
> +     ret = LIS3L02DQ_write_register_int8_t(dev, 
> LIS3L02DQ_REG_GAIN_Z_ADDRESS, val);
> +     if( ret )
> +             goto err_ret;
> +     return len;
> +err_ret:
> +     return ret;
> +}
> +
> +
> +static int LIS3L02DQ_initial_setup(struct LIS3L02DQ_state* st)
> +{
> +     int ret;
> +     memset(st->tx_buff, 0, sizeof st->tx_buff);
> +     memset(st->rx_buff, 0, sizeof st->rx_buff);
> +     st->us->mode = SPI_MODE_3;
> +     spi_setup(st->us);
> +     
> +     /* Write suitable defaults to ctrl1 */
> +     ret = LIS3L02DQ_write_register_int8_t(&st->us->dev, 
> LIS3L02DQ_REG_CTRL_1_ADDRESS, LIS3L02DQ_DEFAULT_CTRL1);
> +     if(ret) {
> +             dev_err(&st->us->dev, "problem with setup control register 1");
> +             goto err_ret;
> +     }
> +     ret = LIS3L02DQ_write_register_int8_t(&st->us->dev, 
> LIS3L02DQ_REG_CTRL_2_ADDRESS, LIS3L02DQ_DEFAULT_CTRL2);
> +     if(ret) {
> +             dev_err(&st->us->dev, "problem with setup control register 2");
> +             goto err_ret;
> +     }
> +err_ret:
> +     return ret;
> +}
> +
> +/* put pointers to these in a table to simplify adding / removing them? */
> +static DEVICE_ATTR(scan,       S_IRUGO,           LIS3L02DQ_scan,          
> NULL);
> +static DEVICE_ATTR(x_offset,   S_IWUSR | S_IRUGO, LIS3L02DQ_read_x_offset, 
> LIS3L02DQ_write_x_offset);
> +static DEVICE_ATTR(y_offset,   S_IWUSR | S_IRUGO, LIS3L02DQ_read_y_offset, 
> LIS3L02DQ_write_y_offset);
> +static DEVICE_ATTR(z_offset,   S_IWUSR | S_IRUGO, LIS3L02DQ_read_z_offset, 
> LIS3L02DQ_write_z_offset);
> +static DEVICE_ATTR(x_gain,     S_IWUSR | S_IRUGO, LIS3L02DQ_read_x_gain,   
> LIS3L02DQ_write_x_gain);
> +static DEVICE_ATTR(y_gain,     S_IWUSR | S_IRUGO, LIS3L02DQ_read_y_gain,   
> LIS3L02DQ_write_y_gain);
> +static DEVICE_ATTR(z_gain,     S_IWUSR | S_IRUGO, LIS3L02DQ_read_z_gain,   
> LIS3L02DQ_write_z_gain);
> +#ifdef CONFIG_SPI_LIS3L02DQ_GPIO_INTERRUPT
> +static DEVICE_ATTR(rip_buffer, S_IRUGO,           LIS3L02DQ_rip_buffer,    
> NULL);
> +#endif 
> +static struct device_attribute *LIS3L02DQ_attrs[] = {
> +     &dev_attr_scan,
> +     &dev_attr_x_offset,
> +     &dev_attr_y_offset,
> +     &dev_attr_z_offset,
> +     &dev_attr_x_gain,
> +     &dev_attr_y_gain,
> +     &dev_attr_z_gain,
> +#ifdef CONFIG_SPI_LIS3L02DQ_GPIO_INTERRUPT
> +     &dev_attr_rip_buffer,
> +#endif 
> +};
> +
> +#ifdef CONFIG_SPI_LIS3L02DQ_GPIO_INTERRUPT
> +static irqreturn_t interrupthandler(int irq, void* _state)
> +{
> +     struct LIS3L02DQ_state* st = _state;
> +     disable_irq_nosync(irq);
> +     schedule_work(&st->work);
> +     st->inter = 1;
> +     return IRQ_HANDLED;     
> +}
> +
> +static void LIS3L02DQ_store_to_ring(struct LIS3L02DQ_state* st)
> +{
> +     int i;
> +     bool initread = true;
> +     /* First use of ring */
> +     if(unlikely(st->write_pointer==0)) {
> +             st->write_pointer = st->ring_buffer;
> +             initread = false;
> +     }
> +     /*probably unnecessary */
> +     barrier();
> +     /*save data */
> +     for(i = 0; i < 3; i++) {
> +             st->write_pointer[i*2] = st->rx_buff[i*4];
> +             st->write_pointer[i*2+1] = st->rx_buff[i*4+2]; 
> +     }
> +     barrier();
> +     st->last_written_pointer = st->write_pointer;
> +     barrier();
> +     st->write_pointer += 6;
> +     if(unlikely(st->write_pointer == st->ring_buffer + 
> 6*LIS3L02DQ_BUFFER_LENGTH))
> +             st->write_pointer = st->ring_buffer;
> +     
> +     if(unlikely(st->read_pointer==0))
> +             st->read_pointer = st->ring_buffer;
> +     else if (st->write_pointer == st->read_pointer) {
> +             
> +             if(unlikely((st->read_pointer+6 == st->ring_buffer + 
> 6*LIS3L02DQ_BUFFER_LENGTH)))
> +                     st->read_pointer = st->ring_buffer;
> +             else 
> +                     st->read_pointer += 6;
> +     }
> +     return;
> +}  
> +
> +static void LIS3L02DQ_data_ready_work(struct work_struct *work_s)
> +{
> +     
> +     struct LIS3L02DQ_state* st = container_of(work_s, struct 
> LIS3L02DQ_state, work);
> +     struct LIS3L02DQ_platform_data* pdata = st->us->dev.platform_data;
> +     
> +     LIS3L02DQ_read_all(st);
> +     LIS3L02DQ_store_to_ring(st);
> +     st->inter = 0;
> +try_again:
> +     while(gpio_get_value(pdata->data_ready_gpio)) {
> +             LIS3L02DQ_read_all(st);
> +             LIS3L02DQ_store_to_ring(st);
> +     }
> +     /* If we are lucky gpio should not be set now - try renabling interrupt 
> */
> +     enable_irq(gpio_to_irq(pdata->data_ready_gpio));
> +     /* verify that either the gpio has not risen or that the interrupt 
> handler caught it */
> +     if(gpio_get_value(pdata->data_ready_gpio))
> +             if( st->inter == 0 ) {
> +                     disable_irq_nosync(gpio_to_irq(pdata->data_ready_gpio));
> +                     goto try_again;
> +             }       
> +     return;
> +}
> +
> +#endif /* CONFIG_SPI_LIS3L02DQ_GPIO_INTERRUPT */
> +
> +static int __devinit LIS3L02DQ_probe(struct spi_device *spi)
> +{
> +     struct LIS3L02DQ_state* st;
> +     struct LIS3L02DQ_platform_data* pdata;
> +     int ret,i ;
> +     
> +     pdata = spi->dev.platform_data;
> +     st = kzalloc(sizeof(struct LIS3L02DQ_state), GFP_KERNEL);
> +     st->us = spi;
> +     
> +     if(pdata == NULL) {
> +             dev_err(&spi->dev, "no device data specified\n");
> +             return -EINVAL;
> +     }
> +     for(i = 0; i < ARRAY_SIZE(LIS3L02DQ_attrs); i++)
> +             ret = device_create_file(&spi->dev, LIS3L02DQ_attrs[i]);
> +     if (ret) {
> +             dev_err(&spi->dev, "cannot create attribute\n");
> +             goto err_status;
> +     }
> +     
> +     spi_set_drvdata(spi, st);
> +     
> +
> +#ifdef CONFIG_SPI_LIS3L02DQ_GPIO_INTERRUPT
> +     INIT_WORK(&st->work, LIS3L02DQ_data_ready_work);
> +     st->inter = 0;
> +     /* enable an interrupt on the data ready line */
> +     ret = request_irq(gpio_to_irq(pdata->data_ready_gpio),interrupthandler, 
> IRQF_DISABLED, 
> +                       "LIS3L02DQ data ready", st);
> +     set_irq_type(gpio_to_irq(pdata->data_ready_gpio), IRQT_RISING);
> +#endif /* CONFIG_SPI_LIS3L02DQ_GPIO_INTERRUPT */     
> +     /* This setup enables data ready generation (amongst other things)*/
> +     ret = LIS3L02DQ_initial_setup(st);
> +     
> +     return ret;     
> +     
> +err_status:
> +     device_remove_file(&spi->dev, &dev_attr_scan);
> +     device_remove_file(&spi->dev, &dev_attr_x_offset);
> +     device_remove_file(&spi->dev, &dev_attr_y_offset);
> +     device_remove_file(&spi->dev, &dev_attr_z_offset);
> +     return ret;
> +}
> +
> +static int LIS3L02DQ_remove(struct spi_device *spi)
> +{
> +     device_remove_file(&spi->dev, &dev_attr_scan);
> +     device_remove_file(&spi->dev, &dev_attr_x_offset);
> +     device_remove_file(&spi->dev, &dev_attr_y_offset);
> +     device_remove_file(&spi->dev, &dev_attr_z_offset);
> +#ifdef CONFIG_SPI_LIS3L02DQ_GPIO_INTERRUPT
> +     flush_scheduled_work();
> +     free_irq(gpio_to_irq(((struct 
> LIS3L02DQ_platform_data*)(spi->dev.platform_data))->data_ready_gpio),&spi->dev);
> +#endif /* CONFIG_SPI_LIS3L02DQ_GPIO_INTERRUPT */
> +     return 0;
> +}
> +
> +static struct spi_driver LIS3L02DQ_driver = {
> +     .driver = {
> +             .name = "LIS3L02DQ",
> +             .owner = THIS_MODULE,
> +     },
> +     .probe = LIS3L02DQ_probe,
> +     .remove = LIS3L02DQ_remove,
> +};
> +
> +static __init int LIS3L02DQ_init(void)
> +{
> +     return spi_register_driver(&LIS3L02DQ_driver);
> +}
> +
> +static __exit void LIS3L02DQ_exit(void)
> +{
> +     spi_unregister_driver(&LIS3L02DQ_driver);
> +     return;
> +}
> +
> +module_init(LIS3L02DQ_init);
> +module_exit(LIS3L02DQ_exit);
> +
> +MODULE_AUTHOR("Jonathan Cameron <[EMAIL PROTECTED]>");
> +MODULE_DESCRIPTION("ST LIS3L02DQ Accelerometer SPI driver");
> +MODULE_LICENSE("GPL v2");
>
>
>
> -------------------------------------------------------------------------
> This SF.net email is sponsored by the 2008 JavaOne(SM) Conference 
> Don't miss this year's exciting event. There's still time to save $100. 
> Use priority code J8TL2D2. 
> http://ad.doubleclick.net/clk;198757673;13503038;p?http://java.sun.com/javaone
> _______________________________________________
> spi-devel-general mailing list
> spi-devel-general@lists.sourceforge.net
> https://lists.sourceforge.net/lists/listinfo/spi-devel-general
>
>   


-------------------------------------------------------------------------
This SF.net email is sponsored by the 2008 JavaOne(SM) Conference 
Don't miss this year's exciting event. There's still time to save $100. 
Use priority code J8TL2D2. 
http://ad.doubleclick.net/clk;198757673;13503038;p?http://java.sun.com/javaone
_______________________________________________
spi-devel-general mailing list
spi-devel-general@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/spi-devel-general

Reply via email to