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