diff options
| author | Barry Song <Barry.Song@analog.com> | 2010-05-12 14:34:06 -0400 |
|---|---|---|
| committer | Greg Kroah-Hartman <gregkh@suse.de> | 2010-05-14 16:20:35 -0400 |
| commit | 089a41985c6c7e69c8fe043c0dd397da628254f5 (patch) | |
| tree | c99efb55efd5eaa3704c60473101e7c1835b144b /drivers/staging | |
| parent | 935e99fb0739aa64d0dd7e8a0bc82faec5d8f830 (diff) | |
staging: iio: adis16260 digital gyro driver
Signed-off-by: Barry Song <Barry.Song@analog.com>
Signed-off-by: Jonathan Cameron <jic23@cam.ac.uk>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/staging')
| -rw-r--r-- | drivers/staging/iio/Kconfig | 1 | ||||
| -rw-r--r-- | drivers/staging/iio/Makefile | 1 | ||||
| -rw-r--r-- | drivers/staging/iio/gyro/Kconfig | 13 | ||||
| -rw-r--r-- | drivers/staging/iio/gyro/Makefile | 7 | ||||
| -rw-r--r-- | drivers/staging/iio/gyro/adis16260.h | 175 | ||||
| -rw-r--r-- | drivers/staging/iio/gyro/adis16260_core.c | 661 | ||||
| -rw-r--r-- | drivers/staging/iio/gyro/adis16260_ring.c | 256 | ||||
| -rw-r--r-- | drivers/staging/iio/gyro/adis16260_trigger.c | 124 | ||||
| -rw-r--r-- | drivers/staging/iio/gyro/gyro.h | 2 |
9 files changed, 1240 insertions, 0 deletions
diff --git a/drivers/staging/iio/Kconfig b/drivers/staging/iio/Kconfig index ed38ef4db444..b0e62449c621 100644 --- a/drivers/staging/iio/Kconfig +++ b/drivers/staging/iio/Kconfig | |||
| @@ -41,6 +41,7 @@ config IIO_TRIGGER | |||
| 41 | 41 | ||
| 42 | source "drivers/staging/iio/accel/Kconfig" | 42 | source "drivers/staging/iio/accel/Kconfig" |
| 43 | source "drivers/staging/iio/adc/Kconfig" | 43 | source "drivers/staging/iio/adc/Kconfig" |
| 44 | source "drivers/staging/iio/gyro/Kconfig" | ||
| 44 | source "drivers/staging/iio/imu/Kconfig" | 45 | source "drivers/staging/iio/imu/Kconfig" |
| 45 | source "drivers/staging/iio/light/Kconfig" | 46 | source "drivers/staging/iio/light/Kconfig" |
| 46 | 47 | ||
diff --git a/drivers/staging/iio/Makefile b/drivers/staging/iio/Makefile index 92b81c2b895d..3502b39f0847 100644 --- a/drivers/staging/iio/Makefile +++ b/drivers/staging/iio/Makefile | |||
| @@ -11,6 +11,7 @@ obj-$(CONFIG_IIO_SW_RING) += ring_sw.o | |||
| 11 | 11 | ||
| 12 | obj-y += accel/ | 12 | obj-y += accel/ |
| 13 | obj-y += adc/ | 13 | obj-y += adc/ |
| 14 | obj-y += gyro/ | ||
| 14 | obj-y += imu/ | 15 | obj-y += imu/ |
| 15 | obj-y += light/ | 16 | obj-y += light/ |
| 16 | 17 | ||
diff --git a/drivers/staging/iio/gyro/Kconfig b/drivers/staging/iio/gyro/Kconfig new file mode 100644 index 000000000000..c4043610c0df --- /dev/null +++ b/drivers/staging/iio/gyro/Kconfig | |||
| @@ -0,0 +1,13 @@ | |||
| 1 | # | ||
| 2 | # IIO Digital Gyroscope Sensor drivers configuration | ||
| 3 | # | ||
| 4 | comment "Digital gyroscope sensors" | ||
| 5 | |||
| 6 | config ADIS16260 | ||
| 7 | tristate "Analog Devices ADIS16260/5 Digital Gyroscope Sensor SPI driver" | ||
| 8 | depends on SPI | ||
| 9 | select IIO_TRIGGER if IIO_RING_BUFFER | ||
| 10 | select IIO_SW_RING if IIO_RING_BUFFER | ||
| 11 | help | ||
| 12 | Say yes here to build support for Analog Devices adis16260/5 | ||
| 13 | programmable digital gyroscope sensor. | ||
diff --git a/drivers/staging/iio/gyro/Makefile b/drivers/staging/iio/gyro/Makefile new file mode 100644 index 000000000000..6d2c547686cb --- /dev/null +++ b/drivers/staging/iio/gyro/Makefile | |||
| @@ -0,0 +1,7 @@ | |||
| 1 | |||
| 2 | # Makefile for digital gyroscope sensor drivers | ||
| 3 | # | ||
| 4 | |||
| 5 | adis16260-y := adis16260_core.o | ||
| 6 | adis16260-$(CONFIG_IIO_RING_BUFFER) += adis16260_ring.o adis16260_trigger.o | ||
| 7 | obj-$(CONFIG_ADIS16260) += adis16260.o | ||
diff --git a/drivers/staging/iio/gyro/adis16260.h b/drivers/staging/iio/gyro/adis16260.h new file mode 100644 index 000000000000..f19efb4c91ce --- /dev/null +++ b/drivers/staging/iio/gyro/adis16260.h | |||
| @@ -0,0 +1,175 @@ | |||
| 1 | #ifndef SPI_ADIS16260_H_ | ||
| 2 | #define SPI_ADIS16260_H_ | ||
| 3 | |||
| 4 | #define ADIS16260_STARTUP_DELAY 220 /* ms */ | ||
| 5 | |||
| 6 | #define ADIS16260_READ_REG(a) a | ||
| 7 | #define ADIS16260_WRITE_REG(a) ((a) | 0x80) | ||
| 8 | |||
| 9 | #define ADIS16260_FLASH_CNT 0x00 /* Flash memory write count */ | ||
| 10 | #define ADIS16260_SUPPLY_OUT 0x02 /* Power supply measurement */ | ||
| 11 | #define ADIS16260_GYRO_OUT 0x04 /* X-axis gyroscope output */ | ||
| 12 | #define ADIS16260_AUX_ADC 0x0A /* analog input channel measurement */ | ||
| 13 | #define ADIS16260_TEMP_OUT 0x0C /* internal temperature measurement */ | ||
| 14 | #define ADIS16260_ANGL_OUT 0x0E /* angle displacement */ | ||
| 15 | #define ADIS16260_GYRO_OFF 0x14 /* Calibration, offset/bias adjustment */ | ||
| 16 | #define ADIS16260_GYRO_SCALE 0x16 /* Calibration, scale adjustment */ | ||
| 17 | #define ADIS16260_ALM_MAG1 0x20 /* Alarm 1 magnitude/polarity setting */ | ||
| 18 | #define ADIS16260_ALM_MAG2 0x22 /* Alarm 2 magnitude/polarity setting */ | ||
| 19 | #define ADIS16260_ALM_SMPL1 0x24 /* Alarm 1 dynamic rate of change setting */ | ||
| 20 | #define ADIS16260_ALM_SMPL2 0x26 /* Alarm 2 dynamic rate of change setting */ | ||
| 21 | #define ADIS16260_ALM_CTRL 0x28 /* Alarm control */ | ||
| 22 | #define ADIS16260_AUX_DAC 0x30 /* Auxiliary DAC data */ | ||
| 23 | #define ADIS16260_GPIO_CTRL 0x32 /* Control, digital I/O line */ | ||
| 24 | #define ADIS16260_MSC_CTRL 0x34 /* Control, data ready, self-test settings */ | ||
| 25 | #define ADIS16260_SMPL_PRD 0x36 /* Control, internal sample rate */ | ||
| 26 | #define ADIS16260_SENS_AVG 0x38 /* Control, dynamic range, filtering */ | ||
| 27 | #define ADIS16260_SLP_CNT 0x3A /* Control, sleep mode initiation */ | ||
| 28 | #define ADIS16260_DIAG_STAT 0x3C /* Diagnostic, error flags */ | ||
| 29 | #define ADIS16260_GLOB_CMD 0x3E /* Control, global commands */ | ||
| 30 | #define ADIS16260_LOT_ID1 0x52 /* Lot Identification Code 1 */ | ||
| 31 | #define ADIS16260_LOT_ID2 0x54 /* Lot Identification Code 2 */ | ||
| 32 | #define ADIS16260_PROD_ID 0x56 /* Product identifier; | ||
| 33 | * convert to decimal = 16,265/16,260 */ | ||
| 34 | #define ADIS16260_SERIAL_NUM 0x58 /* Serial number */ | ||
| 35 | |||
| 36 | #define ADIS16260_OUTPUTS 5 | ||
| 37 | |||
| 38 | #define ADIS16260_ERROR_ACTIVE (1<<14) | ||
| 39 | #define ADIS16260_NEW_DATA (1<<15) | ||
| 40 | |||
| 41 | /* MSC_CTRL */ | ||
| 42 | #define ADIS16260_MSC_CTRL_MEM_TEST (1<<11) | ||
| 43 | /* Internal self-test enable */ | ||
| 44 | #define ADIS16260_MSC_CTRL_INT_SELF_TEST (1<<10) | ||
| 45 | #define ADIS16260_MSC_CTRL_NEG_SELF_TEST (1<<9) | ||
| 46 | #define ADIS16260_MSC_CTRL_POS_SELF_TEST (1<<8) | ||
| 47 | #define ADIS16260_MSC_CTRL_DATA_RDY_EN (1<<2) | ||
| 48 | #define ADIS16260_MSC_CTRL_DATA_RDY_POL_HIGH (1<<1) | ||
| 49 | #define ADIS16260_MSC_CTRL_DATA_RDY_DIO2 (1<<0) | ||
| 50 | |||
| 51 | /* SMPL_PRD */ | ||
| 52 | /* Time base (tB): 0 = 1.953 ms, 1 = 60.54 ms */ | ||
| 53 | #define ADIS16260_SMPL_PRD_TIME_BASE (1<<7) | ||
| 54 | #define ADIS16260_SMPL_PRD_DIV_MASK 0x7F | ||
| 55 | |||
| 56 | /* SLP_CNT */ | ||
| 57 | #define ADIS16260_SLP_CNT_POWER_OFF 0x80 | ||
| 58 | |||
| 59 | /* DIAG_STAT */ | ||
| 60 | #define ADIS16260_DIAG_STAT_ALARM2 (1<<9) | ||
| 61 | #define ADIS16260_DIAG_STAT_ALARM1 (1<<8) | ||
| 62 | #define ADIS16260_DIAG_STAT_FLASH_CHK (1<<6) | ||
| 63 | #define ADIS16260_DIAG_STAT_SELF_TEST (1<<5) | ||
| 64 | #define ADIS16260_DIAG_STAT_OVERFLOW (1<<4) | ||
| 65 | #define ADIS16260_DIAG_STAT_SPI_FAIL (1<<3) | ||
| 66 | #define ADIS16260_DIAG_STAT_FLASH_UPT (1<<2) | ||
| 67 | #define ADIS16260_DIAG_STAT_POWER_HIGH (1<<1) | ||
| 68 | #define ADIS16260_DIAG_STAT_POWER_LOW (1<<0) | ||
| 69 | |||
| 70 | /* GLOB_CMD */ | ||
| 71 | #define ADIS16260_GLOB_CMD_SW_RESET (1<<7) | ||
| 72 | #define ADIS16260_GLOB_CMD_FLASH_UPD (1<<3) | ||
| 73 | #define ADIS16260_GLOB_CMD_DAC_LATCH (1<<2) | ||
| 74 | #define ADIS16260_GLOB_CMD_FAC_CALIB (1<<1) | ||
| 75 | #define ADIS16260_GLOB_CMD_AUTO_NULL (1<<0) | ||
| 76 | |||
| 77 | #define ADIS16260_MAX_TX 24 | ||
| 78 | #define ADIS16260_MAX_RX 24 | ||
| 79 | |||
| 80 | #define ADIS16260_SPI_SLOW (u32)(300 * 1000) | ||
| 81 | #define ADIS16260_SPI_BURST (u32)(1000 * 1000) | ||
| 82 | #define ADIS16260_SPI_FAST (u32)(2000 * 1000) | ||
| 83 | |||
| 84 | /** | ||
| 85 | * struct adis16260_state - device instance specific data | ||
| 86 | * @us: actual spi_device | ||
| 87 | * @work_trigger_to_ring: bh for triggered event handling | ||
| 88 | * @work_cont_thresh: CLEAN | ||
| 89 | * @inter: used to check if new interrupt has been triggered | ||
| 90 | * @last_timestamp: passing timestamp from th to bh of interrupt handler | ||
| 91 | * @indio_dev: industrial I/O device structure | ||
| 92 | * @trig: data ready trigger registered with iio | ||
| 93 | * @tx: transmit buffer | ||
| 94 | * @rx: recieve buffer | ||
| 95 | * @buf_lock: mutex to protect tx and rx | ||
| 96 | **/ | ||
| 97 | struct adis16260_state { | ||
| 98 | struct spi_device *us; | ||
| 99 | struct work_struct work_trigger_to_ring; | ||
| 100 | struct iio_work_cont work_cont_thresh; | ||
| 101 | s64 last_timestamp; | ||
| 102 | struct iio_dev *indio_dev; | ||
| 103 | struct iio_trigger *trig; | ||
| 104 | u8 *tx; | ||
| 105 | u8 *rx; | ||
| 106 | struct mutex buf_lock; | ||
| 107 | }; | ||
| 108 | |||
| 109 | int adis16260_set_irq(struct device *dev, bool enable); | ||
| 110 | |||
| 111 | #ifdef CONFIG_IIO_RING_BUFFER | ||
| 112 | /* At the moment triggers are only used for ring buffer | ||
| 113 | * filling. This may change! | ||
| 114 | */ | ||
| 115 | |||
| 116 | enum adis16260_scan { | ||
| 117 | ADIS16260_SCAN_SUPPLY, | ||
| 118 | ADIS16260_SCAN_GYRO, | ||
| 119 | ADIS16260_SCAN_AUX_ADC, | ||
| 120 | ADIS16260_SCAN_TEMP, | ||
| 121 | ADIS16260_SCAN_ANGL, | ||
| 122 | }; | ||
| 123 | |||
| 124 | void adis16260_remove_trigger(struct iio_dev *indio_dev); | ||
| 125 | int adis16260_probe_trigger(struct iio_dev *indio_dev); | ||
| 126 | |||
| 127 | ssize_t adis16260_read_data_from_ring(struct device *dev, | ||
| 128 | struct device_attribute *attr, | ||
| 129 | char *buf); | ||
| 130 | |||
| 131 | |||
| 132 | int adis16260_configure_ring(struct iio_dev *indio_dev); | ||
| 133 | void adis16260_unconfigure_ring(struct iio_dev *indio_dev); | ||
| 134 | |||
| 135 | int adis16260_initialize_ring(struct iio_ring_buffer *ring); | ||
| 136 | void adis16260_uninitialize_ring(struct iio_ring_buffer *ring); | ||
| 137 | #else /* CONFIG_IIO_RING_BUFFER */ | ||
| 138 | |||
| 139 | static inline void adis16260_remove_trigger(struct iio_dev *indio_dev) | ||
| 140 | { | ||
| 141 | } | ||
| 142 | |||
| 143 | static inline int adis16260_probe_trigger(struct iio_dev *indio_dev) | ||
| 144 | { | ||
| 145 | return 0; | ||
| 146 | } | ||
| 147 | |||
| 148 | static inline ssize_t | ||
| 149 | adis16260_read_data_from_ring(struct device *dev, | ||
| 150 | struct device_attribute *attr, | ||
| 151 | char *buf) | ||
| 152 | { | ||
| 153 | return 0; | ||
| 154 | } | ||
| 155 | |||
| 156 | static int adis16260_configure_ring(struct iio_dev *indio_dev) | ||
| 157 | { | ||
| 158 | return 0; | ||
| 159 | } | ||
| 160 | |||
| 161 | static inline void adis16260_unconfigure_ring(struct iio_dev *indio_dev) | ||
| 162 | { | ||
| 163 | } | ||
| 164 | |||
| 165 | static inline int adis16260_initialize_ring(struct iio_ring_buffer *ring) | ||
| 166 | { | ||
| 167 | return 0; | ||
| 168 | } | ||
| 169 | |||
| 170 | static inline void adis16260_uninitialize_ring(struct iio_ring_buffer *ring) | ||
| 171 | { | ||
| 172 | } | ||
| 173 | |||
| 174 | #endif /* CONFIG_IIO_RING_BUFFER */ | ||
| 175 | #endif /* SPI_ADIS16260_H_ */ | ||
diff --git a/drivers/staging/iio/gyro/adis16260_core.c b/drivers/staging/iio/gyro/adis16260_core.c new file mode 100644 index 000000000000..c93f4d580fce --- /dev/null +++ b/drivers/staging/iio/gyro/adis16260_core.c | |||
| @@ -0,0 +1,661 @@ | |||
| 1 | /* | ||
| 2 | * ADIS16260 Programmable Digital Gyroscope Sensor Driver | ||
| 3 | * | ||
| 4 | * Copyright 2010 Analog Devices Inc. | ||
| 5 | * | ||
| 6 | * Licensed under the GPL-2 or later. | ||
| 7 | */ | ||
| 8 | |||
| 9 | #include <linux/interrupt.h> | ||
| 10 | #include <linux/irq.h> | ||
| 11 | #include <linux/gpio.h> | ||
| 12 | #include <linux/delay.h> | ||
| 13 | #include <linux/mutex.h> | ||
| 14 | #include <linux/device.h> | ||
| 15 | #include <linux/kernel.h> | ||
| 16 | #include <linux/spi/spi.h> | ||
| 17 | |||
| 18 | #include <linux/sysfs.h> | ||
| 19 | #include <linux/list.h> | ||
| 20 | |||
| 21 | #include "../iio.h" | ||
| 22 | #include "../sysfs.h" | ||
| 23 | #include "../adc/adc.h" | ||
| 24 | #include "gyro.h" | ||
| 25 | |||
| 26 | #include "adis16260.h" | ||
| 27 | |||
| 28 | #define DRIVER_NAME "adis16260" | ||
| 29 | |||
| 30 | static int adis16260_check_status(struct device *dev); | ||
| 31 | |||
| 32 | /** | ||
| 33 | * adis16260_spi_write_reg_8() - write single byte to a register | ||
| 34 | * @dev: device associated with child of actual device (iio_dev or iio_trig) | ||
| 35 | * @reg_address: the address of the register to be written | ||
| 36 | * @val: the value to write | ||
| 37 | **/ | ||
| 38 | static int adis16260_spi_write_reg_8(struct device *dev, | ||
| 39 | u8 reg_address, | ||
| 40 | u8 val) | ||
| 41 | { | ||
| 42 | int ret; | ||
| 43 | struct iio_dev *indio_dev = dev_get_drvdata(dev); | ||
| 44 | struct adis16260_state *st = iio_dev_get_devdata(indio_dev); | ||
| 45 | |||
| 46 | mutex_lock(&st->buf_lock); | ||
| 47 | st->tx[0] = ADIS16260_WRITE_REG(reg_address); | ||
| 48 | st->tx[1] = val; | ||
| 49 | |||
| 50 | ret = spi_write(st->us, st->tx, 2); | ||
| 51 | mutex_unlock(&st->buf_lock); | ||
| 52 | |||
| 53 | return ret; | ||
| 54 | } | ||
| 55 | |||
| 56 | /** | ||
| 57 | * adis16260_spi_write_reg_16() - write 2 bytes to a pair of registers | ||
| 58 | * @dev: device associated with child of actual device (iio_dev or iio_trig) | ||
| 59 | * @reg_address: the address of the lower of the two registers. Second register | ||
| 60 | * is assumed to have address one greater. | ||
| 61 | * @val: value to be written | ||
| 62 | **/ | ||
| 63 | static int adis16260_spi_write_reg_16(struct device *dev, | ||
| 64 | u8 lower_reg_address, | ||
| 65 | u16 value) | ||
| 66 | { | ||
| 67 | int ret; | ||
| 68 | struct spi_message msg; | ||
| 69 | struct iio_dev *indio_dev = dev_get_drvdata(dev); | ||
| 70 | struct adis16260_state *st = iio_dev_get_devdata(indio_dev); | ||
| 71 | struct spi_transfer xfers[] = { | ||
| 72 | { | ||
| 73 | .tx_buf = st->tx, | ||
| 74 | .bits_per_word = 8, | ||
| 75 | .len = 2, | ||
| 76 | .cs_change = 1, | ||
| 77 | .delay_usecs = 20, | ||
| 78 | }, { | ||
| 79 | .tx_buf = st->tx + 2, | ||
| 80 | .bits_per_word = 8, | ||
| 81 | .len = 2, | ||
| 82 | .cs_change = 1, | ||
| 83 | .delay_usecs = 20, | ||
| 84 | }, | ||
| 85 | }; | ||
| 86 | |||
| 87 | mutex_lock(&st->buf_lock); | ||
| 88 | st->tx[0] = ADIS16260_WRITE_REG(lower_reg_address); | ||
| 89 | st->tx[1] = value & 0xFF; | ||
| 90 | st->tx[2] = ADIS16260_WRITE_REG(lower_reg_address + 1); | ||
| 91 | st->tx[3] = (value >> 8) & 0xFF; | ||
| 92 | |||
| 93 | spi_message_init(&msg); | ||
| 94 | spi_message_add_tail(&xfers[0], &msg); | ||
| 95 | spi_message_add_tail(&xfers[1], &msg); | ||
| 96 | ret = spi_sync(st->us, &msg); | ||
| 97 | mutex_unlock(&st->buf_lock); | ||
| 98 | |||
| 99 | return ret; | ||
| 100 | } | ||
| 101 | |||
| 102 | /** | ||
| 103 | * adis16260_spi_read_reg_16() - read 2 bytes from a 16-bit register | ||
| 104 | * @dev: device associated with child of actual device (iio_dev or iio_trig) | ||
| 105 | * @reg_address: the address of the lower of the two registers. Second register | ||
| 106 | * is assumed to have address one greater. | ||
| 107 | * @val: somewhere to pass back the value read | ||
| 108 | **/ | ||
| 109 | static int adis16260_spi_read_reg_16(struct device *dev, | ||
| 110 | u8 lower_reg_address, | ||
| 111 | u16 *val) | ||
| 112 | { | ||
| 113 | struct spi_message msg; | ||
| 114 | struct iio_dev *indio_dev = dev_get_drvdata(dev); | ||
| 115 | struct adis16260_state *st = iio_dev_get_devdata(indio_dev); | ||
| 116 | int ret; | ||
| 117 | struct spi_transfer xfers[] = { | ||
| 118 | { | ||
| 119 | .tx_buf = st->tx, | ||
| 120 | .bits_per_word = 8, | ||
| 121 | .len = 2, | ||
| 122 | .cs_change = 1, | ||
| 123 | .delay_usecs = 30, | ||
| 124 | }, { | ||
| 125 | .rx_buf = st->rx, | ||
| 126 | .bits_per_word = 8, | ||
| 127 | .len = 2, | ||
| 128 | .cs_change = 1, | ||
| 129 | .delay_usecs = 30, | ||
| 130 | }, | ||
| 131 | }; | ||
| 132 | |||
| 133 | mutex_lock(&st->buf_lock); | ||
| 134 | st->tx[0] = ADIS16260_READ_REG(lower_reg_address); | ||
| 135 | st->tx[1] = 0; | ||
| 136 | st->tx[2] = 0; | ||
| 137 | st->tx[3] = 0; | ||
| 138 | |||
| 139 | spi_message_init(&msg); | ||
| 140 | spi_message_add_tail(&xfers[0], &msg); | ||
| 141 | spi_message_add_tail(&xfers[1], &msg); | ||
| 142 | ret = spi_sync(st->us, &msg); | ||
| 143 | if (ret) { | ||
| 144 | dev_err(&st->us->dev, | ||
| 145 | "problem when reading 16 bit register 0x%02X", | ||
| 146 | lower_reg_address); | ||
| 147 | goto error_ret; | ||
| 148 | } | ||
| 149 | *val = (st->rx[0] << 8) | st->rx[1]; | ||
| 150 | |||
| 151 | error_ret: | ||
| 152 | mutex_unlock(&st->buf_lock); | ||
| 153 | return ret; | ||
| 154 | } | ||
| 155 | |||
| 156 | static ssize_t adis16260_spi_read_signed(struct device *dev, | ||
| 157 | struct device_attribute *attr, | ||
| 158 | char *buf, | ||
| 159 | unsigned bits) | ||
| 160 | { | ||
| 161 | int ret; | ||
| 162 | s16 val = 0; | ||
| 163 | unsigned shift = 16 - bits; | ||
| 164 | struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); | ||
| 165 | |||
| 166 | ret = adis16260_spi_read_reg_16(dev, this_attr->address, (u16 *)&val); | ||
| 167 | if (ret) | ||
| 168 | return ret; | ||
| 169 | |||
| 170 | if (val & ADIS16260_ERROR_ACTIVE) | ||
| 171 | adis16260_check_status(dev); | ||
| 172 | val = ((s16)(val << shift) >> shift); | ||
| 173 | return sprintf(buf, "%d\n", val); | ||
| 174 | } | ||
| 175 | |||
| 176 | static ssize_t adis16260_read_12bit_unsigned(struct device *dev, | ||
| 177 | struct device_attribute *attr, | ||
| 178 | char *buf) | ||
| 179 | { | ||
| 180 | int ret; | ||
| 181 | u16 val = 0; | ||
| 182 | struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); | ||
| 183 | |||
| 184 | ret = adis16260_spi_read_reg_16(dev, this_attr->address, &val); | ||
| 185 | if (ret) | ||
| 186 | return ret; | ||
| 187 | |||
| 188 | if (val & ADIS16260_ERROR_ACTIVE) | ||
| 189 | adis16260_check_status(dev); | ||
| 190 | |||
| 191 | return sprintf(buf, "%u\n", val & 0x0FFF); | ||
| 192 | } | ||
| 193 | |||
| 194 | static ssize_t adis16260_read_12bit_signed(struct device *dev, | ||
| 195 | struct device_attribute *attr, | ||
| 196 | char *buf) | ||
| 197 | { | ||
| 198 | struct iio_dev *indio_dev = dev_get_drvdata(dev); | ||
| 199 | ssize_t ret; | ||
| 200 | |||
| 201 | /* Take the iio_dev status lock */ | ||
| 202 | mutex_lock(&indio_dev->mlock); | ||
| 203 | ret = adis16260_spi_read_signed(dev, attr, buf, 12); | ||
| 204 | mutex_unlock(&indio_dev->mlock); | ||
| 205 | |||
| 206 | return ret; | ||
| 207 | } | ||
| 208 | |||
| 209 | static ssize_t adis16260_read_14bit_signed(struct device *dev, | ||
| 210 | struct device_attribute *attr, | ||
| 211 | char *buf) | ||
| 212 | { | ||
| 213 | struct iio_dev *indio_dev = dev_get_drvdata(dev); | ||
| 214 | ssize_t ret; | ||
| 215 | |||
| 216 | /* Take the iio_dev status lock */ | ||
| 217 | mutex_lock(&indio_dev->mlock); | ||
| 218 | ret = adis16260_spi_read_signed(dev, attr, buf, 14); | ||
| 219 | mutex_unlock(&indio_dev->mlock); | ||
| 220 | |||
| 221 | return ret; | ||
| 222 | } | ||
| 223 | |||
| 224 | static ssize_t adis16260_write_16bit(struct device *dev, | ||
| 225 | struct device_attribute *attr, | ||
| 226 | const char *buf, | ||
| 227 | size_t len) | ||
| 228 | { | ||
| 229 | struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); | ||
| 230 | int ret; | ||
| 231 | long val; | ||
| 232 | |||
| 233 | ret = strict_strtol(buf, 10, &val); | ||
| 234 | if (ret) | ||
| 235 | goto error_ret; | ||
| 236 | ret = adis16260_spi_write_reg_16(dev, this_attr->address, val); | ||
| 237 | |||
| 238 | error_ret: | ||
| 239 | return ret ? ret : len; | ||
| 240 | } | ||
| 241 | |||
| 242 | static ssize_t adis16260_read_frequency(struct device *dev, | ||
| 243 | struct device_attribute *attr, | ||
| 244 | char *buf) | ||
| 245 | { | ||
| 246 | int ret, len = 0; | ||
| 247 | u16 t; | ||
| 248 | int sps; | ||
| 249 | ret = adis16260_spi_read_reg_16(dev, | ||
| 250 | ADIS16260_SMPL_PRD, | ||
| 251 | &t); | ||
| 252 | if (ret) | ||
| 253 | return ret; | ||
| 254 | sps = (t & ADIS16260_SMPL_PRD_TIME_BASE) ? 66 : 2048; | ||
| 255 | sps /= (t & ADIS16260_SMPL_PRD_DIV_MASK) + 1; | ||
| 256 | len = sprintf(buf, "%d SPS\n", sps); | ||
| 257 | return len; | ||
| 258 | } | ||
| 259 | |||
| 260 | static ssize_t adis16260_write_frequency(struct device *dev, | ||
| 261 | struct device_attribute *attr, | ||
| 262 | const char *buf, | ||
| 263 | size_t len) | ||
| 264 | { | ||
| 265 | struct iio_dev *indio_dev = dev_get_drvdata(dev); | ||
| 266 | struct adis16260_state *st = iio_dev_get_devdata(indio_dev); | ||
| 267 | long val; | ||
| 268 | int ret; | ||
| 269 | u8 t; | ||
| 270 | |||
| 271 | ret = strict_strtol(buf, 10, &val); | ||
| 272 | if (ret) | ||
| 273 | return ret; | ||
| 274 | |||
| 275 | mutex_lock(&indio_dev->mlock); | ||
| 276 | |||
| 277 | t = (2048 / val); | ||
| 278 | if (t > 0) | ||
| 279 | t--; | ||
| 280 | t &= ADIS16260_SMPL_PRD_DIV_MASK; | ||
| 281 | if ((t & ADIS16260_SMPL_PRD_DIV_MASK) >= 0x0A) | ||
| 282 | st->us->max_speed_hz = ADIS16260_SPI_SLOW; | ||
| 283 | else | ||
| 284 | st->us->max_speed_hz = ADIS16260_SPI_FAST; | ||
| 285 | |||
| 286 | ret = adis16260_spi_write_reg_8(dev, | ||
| 287 | ADIS16260_SMPL_PRD, | ||
| 288 | t); | ||
| 289 | |||
| 290 | mutex_unlock(&indio_dev->mlock); | ||
| 291 | |||
| 292 | return ret ? ret : len; | ||
| 293 | } | ||
| 294 | |||
| 295 | static int adis16260_reset(struct device *dev) | ||
| 296 | { | ||
| 297 | int ret; | ||
| 298 | ret = adis16260_spi_write_reg_8(dev, | ||
| 299 | ADIS16260_GLOB_CMD, | ||
| 300 | ADIS16260_GLOB_CMD_SW_RESET); | ||
| 301 | if (ret) | ||
| 302 | dev_err(dev, "problem resetting device"); | ||
| 303 | |||
| 304 | return ret; | ||
| 305 | } | ||
| 306 | |||
| 307 | static ssize_t adis16260_write_reset(struct device *dev, | ||
| 308 | struct device_attribute *attr, | ||
| 309 | const char *buf, size_t len) | ||
| 310 | { | ||
| 311 | if (len < 1) | ||
| 312 | return -EINVAL; | ||
| 313 | switch (buf[0]) { | ||
| 314 | case '1': | ||
| 315 | case 'y': | ||
| 316 | case 'Y': | ||
| 317 | return adis16260_reset(dev); | ||
| 318 | } | ||
| 319 | return -EINVAL; | ||
| 320 | } | ||
| 321 | |||
| 322 | int adis16260_set_irq(struct device *dev, bool enable) | ||
| 323 | { | ||
| 324 | int ret; | ||
| 325 | u16 msc; | ||
| 326 | ret = adis16260_spi_read_reg_16(dev, ADIS16260_MSC_CTRL, &msc); | ||
| 327 | if (ret) | ||
| 328 | goto error_ret; | ||
| 329 | |||
| 330 | msc |= ADIS16260_MSC_CTRL_DATA_RDY_POL_HIGH; | ||
| 331 | if (enable) | ||
| 332 | msc |= ADIS16260_MSC_CTRL_DATA_RDY_EN; | ||
| 333 | else | ||
| 334 | msc &= ~ADIS16260_MSC_CTRL_DATA_RDY_EN; | ||
| 335 | |||
| 336 | ret = adis16260_spi_write_reg_16(dev, ADIS16260_MSC_CTRL, msc); | ||
| 337 | if (ret) | ||
| 338 | goto error_ret; | ||
| 339 | |||
| 340 | error_ret: | ||
| 341 | return ret; | ||
| 342 | } | ||
| 343 | |||
| 344 | /* Power down the device */ | ||
| 345 | static int adis16260_stop_device(struct device *dev) | ||
| 346 | { | ||
| 347 | int ret; | ||
| 348 | u16 val = ADIS16260_SLP_CNT_POWER_OFF; | ||
| 349 | |||
| 350 | ret = adis16260_spi_write_reg_16(dev, ADIS16260_SLP_CNT, val); | ||
| 351 | if (ret) | ||
| 352 | dev_err(dev, "problem with turning device off: SLP_CNT"); | ||
| 353 | |||
| 354 | return ret; | ||
| 355 | } | ||
| 356 | |||
| 357 | static int adis16260_self_test(struct device *dev) | ||
| 358 | { | ||
| 359 | int ret; | ||
| 360 | ret = adis16260_spi_write_reg_16(dev, | ||
| 361 | ADIS16260_MSC_CTRL, | ||
| 362 | ADIS16260_MSC_CTRL_MEM_TEST); | ||
| 363 | if (ret) { | ||
| 364 | dev_err(dev, "problem starting self test"); | ||
| 365 | goto err_ret; | ||
| 366 | } | ||
| 367 | |||
| 368 | adis16260_check_status(dev); | ||
| 369 | |||
| 370 | err_ret: | ||
| 371 | return ret; | ||
| 372 | } | ||
| 373 | |||
| 374 | static int adis16260_check_status(struct device *dev) | ||
| 375 | { | ||
| 376 | u16 status; | ||
| 377 | int ret; | ||
| 378 | |||
| 379 | ret = adis16260_spi_read_reg_16(dev, ADIS16260_DIAG_STAT, &status); | ||
| 380 | |||
| 381 | if (ret < 0) { | ||
| 382 | dev_err(dev, "Reading status failed\n"); | ||
| 383 | goto error_ret; | ||
| 384 | } | ||
| 385 | ret = status & 0x7F; | ||
| 386 | if (status & ADIS16260_DIAG_STAT_FLASH_CHK) | ||
| 387 | dev_err(dev, "Flash checksum error\n"); | ||
| 388 | if (status & ADIS16260_DIAG_STAT_SELF_TEST) | ||
| 389 | dev_err(dev, "Self test error\n"); | ||
| 390 | if (status & ADIS16260_DIAG_STAT_OVERFLOW) | ||
| 391 | dev_err(dev, "Sensor overrange\n"); | ||
| 392 | if (status & ADIS16260_DIAG_STAT_SPI_FAIL) | ||
| 393 | dev_err(dev, "SPI failure\n"); | ||
| 394 | if (status & ADIS16260_DIAG_STAT_FLASH_UPT) | ||
| 395 | dev_err(dev, "Flash update failed\n"); | ||
| 396 | if (status & ADIS16260_DIAG_STAT_POWER_HIGH) | ||
| 397 | dev_err(dev, "Power supply above 5.25V\n"); | ||
| 398 | if (status & ADIS16260_DIAG_STAT_POWER_LOW) | ||
| 399 | dev_err(dev, "Power supply below 4.75V\n"); | ||
| 400 | |||
| 401 | error_ret: | ||
| 402 | return ret; | ||
| 403 | } | ||
| 404 | |||
| 405 | static int adis16260_initial_setup(struct adis16260_state *st) | ||
| 406 | { | ||
| 407 | int ret; | ||
| 408 | struct device *dev = &st->indio_dev->dev; | ||
| 409 | |||
| 410 | /* Disable IRQ */ | ||
| 411 | ret = adis16260_set_irq(dev, false); | ||
| 412 | if (ret) { | ||
| 413 | dev_err(dev, "disable irq failed"); | ||
| 414 | goto err_ret; | ||
| 415 | } | ||
| 416 | |||
| 417 | /* Do self test */ | ||
| 418 | ret = adis16260_self_test(dev); | ||
| 419 | if (ret) { | ||
| 420 | dev_err(dev, "self test failure"); | ||
| 421 | goto err_ret; | ||
| 422 | } | ||
| 423 | |||
| 424 | /* Read status register to check the result */ | ||
| 425 | ret = adis16260_check_status(dev); | ||
| 426 | if (ret) { | ||
| 427 | adis16260_reset(dev); | ||
| 428 | dev_err(dev, "device not playing ball -> reset"); | ||
| 429 | msleep(ADIS16260_STARTUP_DELAY); | ||
| 430 | ret = adis16260_check_status(dev); | ||
| 431 | if (ret) { | ||
| 432 | dev_err(dev, "giving up"); | ||
| 433 | goto err_ret; | ||
| 434 | } | ||
| 435 | } | ||
| 436 | |||
| 437 | printk(KERN_INFO DRIVER_NAME ": at CS%d (irq %d)\n", | ||
| 438 | st->us->chip_select, st->us->irq); | ||
| 439 | |||
| 440 | err_ret: | ||
| 441 | return ret; | ||
| 442 | } | ||
| 443 | |||
| 444 | static IIO_DEV_ATTR_IN_NAMED_RAW(supply, | ||
| 445 | adis16260_read_12bit_unsigned, | ||
| 446 | ADIS16260_SUPPLY_OUT); | ||
| 447 | static IIO_CONST_ATTR(in_supply_scale, "0.0018315"); | ||
| 448 | |||
| 449 | static IIO_DEV_ATTR_GYRO(adis16260_read_14bit_signed, | ||
| 450 | ADIS16260_GYRO_OUT); | ||
| 451 | static IIO_DEV_ATTR_GYRO_SCALE(S_IWUSR | S_IRUGO, | ||
| 452 | adis16260_read_14bit_signed, | ||
| 453 | adis16260_write_16bit, | ||
| 454 | ADIS16260_GYRO_SCALE); | ||
| 455 | static IIO_DEV_ATTR_GYRO_OFFSET(S_IWUSR | S_IRUGO, | ||
| 456 | adis16260_read_12bit_signed, | ||
| 457 | adis16260_write_16bit, | ||
| 458 | ADIS16260_GYRO_OFF); | ||
| 459 | |||
| 460 | static IIO_DEV_ATTR_TEMP_RAW(adis16260_read_12bit_unsigned); | ||
| 461 | static IIO_CONST_ATTR(temp_offset, "25"); | ||
| 462 | static IIO_CONST_ATTR(temp_scale, "0.1453"); | ||
| 463 | |||
| 464 | static IIO_DEV_ATTR_IN_RAW(0, adis16260_read_12bit_unsigned, | ||
| 465 | ADIS16260_AUX_ADC); | ||
| 466 | static IIO_CONST_ATTR(in0_scale, "0.0006105"); | ||
| 467 | |||
| 468 | static IIO_DEV_ATTR_SAMP_FREQ(S_IWUSR | S_IRUGO, | ||
| 469 | adis16260_read_frequency, | ||
| 470 | adis16260_write_frequency); | ||
| 471 | static IIO_DEV_ATTR_ANGL(adis16260_read_14bit_signed, | ||
| 472 | ADIS16260_ANGL_OUT); | ||
| 473 | |||
| 474 | static IIO_DEVICE_ATTR(reset, S_IWUSR, NULL, adis16260_write_reset, 0); | ||
| 475 | |||
| 476 | static IIO_CONST_ATTR_AVAIL_SAMP_FREQ("256 2048"); | ||
| 477 | |||
| 478 | static IIO_CONST_ATTR(name, "adis16260"); | ||
| 479 | |||
| 480 | static struct attribute *adis16260_event_attributes[] = { | ||
| 481 | NULL | ||
| 482 | }; | ||
| 483 | |||
| 484 | static struct attribute_group adis16260_event_attribute_group = { | ||
| 485 | .attrs = adis16260_event_attributes, | ||
| 486 | }; | ||
| 487 | |||
| 488 | static struct attribute *adis16260_attributes[] = { | ||
| 489 | &iio_dev_attr_in_supply_raw.dev_attr.attr, | ||
| 490 | &iio_const_attr_in_supply_scale.dev_attr.attr, | ||
| 491 | &iio_dev_attr_gyro_raw.dev_attr.attr, | ||
| 492 | &iio_dev_attr_gyro_scale.dev_attr.attr, | ||
| 493 | &iio_dev_attr_gyro_offset.dev_attr.attr, | ||
| 494 | &iio_dev_attr_angl_raw.dev_attr.attr, | ||
| 495 | &iio_dev_attr_temp_raw.dev_attr.attr, | ||
| 496 | &iio_const_attr_temp_offset.dev_attr.attr, | ||
| 497 | &iio_const_attr_temp_scale.dev_attr.attr, | ||
| 498 | &iio_dev_attr_in0_raw.dev_attr.attr, | ||
| 499 | &iio_const_attr_in0_scale.dev_attr.attr, | ||
| 500 | &iio_dev_attr_sampling_frequency.dev_attr.attr, | ||
| 501 | &iio_const_attr_available_sampling_frequency.dev_attr.attr, | ||
| 502 | &iio_dev_attr_reset.dev_attr.attr, | ||
| 503 | &iio_const_attr_name.dev_attr.attr, | ||
| 504 | NULL | ||
| 505 | }; | ||
| 506 | |||
| 507 | static const struct attribute_group adis16260_attribute_group = { | ||
| 508 | .attrs = adis16260_attributes, | ||
| 509 | }; | ||
| 510 | |||
| 511 | static int __devinit adis16260_probe(struct spi_device *spi) | ||
| 512 | { | ||
| 513 | int ret, regdone = 0; | ||
| 514 | struct adis16260_state *st = kzalloc(sizeof *st, GFP_KERNEL); | ||
| 515 | if (!st) { | ||
| 516 | ret = -ENOMEM; | ||
| 517 | goto error_ret; | ||
| 518 | } | ||
| 519 | /* this is only used for removal purposes */ | ||
| 520 | spi_set_drvdata(spi, st); | ||
| 521 | |||
| 522 | /* Allocate the comms buffers */ | ||
| 523 | st->rx = kzalloc(sizeof(*st->rx)*ADIS16260_MAX_RX, GFP_KERNEL); | ||
| 524 | if (st->rx == NULL) { | ||
| 525 | ret = -ENOMEM; | ||
| 526 | goto error_free_st; | ||
| 527 | } | ||
| 528 | st->tx = kzalloc(sizeof(*st->tx)*ADIS16260_MAX_TX, GFP_KERNEL); | ||
| 529 | if (st->tx == NULL) { | ||
| 530 | ret = -ENOMEM; | ||
| 531 | goto error_free_rx; | ||
| 532 | } | ||
| 533 | st->us = spi; | ||
| 534 | mutex_init(&st->buf_lock); | ||
| 535 | /* setup the industrialio driver allocated elements */ | ||
| 536 | st->indio_dev = iio_allocate_device(); | ||
| 537 | if (st->indio_dev == NULL) { | ||
| 538 | ret = -ENOMEM; | ||
| 539 | goto error_free_tx; | ||
| 540 | } | ||
| 541 | |||
| 542 | st->indio_dev->dev.parent = &spi->dev; | ||
| 543 | st->indio_dev->num_interrupt_lines = 1; | ||
| 544 | st->indio_dev->event_attrs = &adis16260_event_attribute_group; | ||
| 545 | st->indio_dev->attrs = &adis16260_attribute_group; | ||
| 546 | st->indio_dev->dev_data = (void *)(st); | ||
| 547 | st->indio_dev->driver_module = THIS_MODULE; | ||
| 548 | st->indio_dev->modes = INDIO_DIRECT_MODE; | ||
| 549 | |||
| 550 | ret = adis16260_configure_ring(st->indio_dev); | ||
| 551 | if (ret) | ||
| 552 | goto error_free_dev; | ||
| 553 | |||
| 554 | ret = iio_device_register(st->indio_dev); | ||
| 555 | if (ret) | ||
| 556 | goto error_unreg_ring_funcs; | ||
| 557 | regdone = 1; | ||
| 558 | |||
| 559 | ret = adis16260_initialize_ring(st->indio_dev->ring); | ||
| 560 | if (ret) { | ||
| 561 | printk(KERN_ERR "failed to initialize the ring\n"); | ||
| 562 | goto error_unreg_ring_funcs; | ||
| 563 | } | ||
| 564 | |||
| 565 | if (spi->irq) { | ||
| 566 | ret = iio_register_interrupt_line(spi->irq, | ||
| 567 | st->indio_dev, | ||
| 568 | 0, | ||
| 569 | IRQF_TRIGGER_RISING, | ||
| 570 | "adis16260"); | ||
| 571 | if (ret) | ||
| 572 | goto error_uninitialize_ring; | ||
| 573 | |||
| 574 | ret = adis16260_probe_trigger(st->indio_dev); | ||
| 575 | if (ret) | ||
| 576 | goto error_unregister_line; | ||
| 577 | } | ||
| 578 | |||
| 579 | /* Get the device into a sane initial state */ | ||
| 580 | ret = adis16260_initial_setup(st); | ||
| 581 | if (ret) | ||
| 582 | goto error_remove_trigger; | ||
| 583 | return 0; | ||
| 584 | |||
| 585 | error_remove_trigger: | ||
| 586 | adis16260_remove_trigger(st->indio_dev); | ||
| 587 | error_unregister_line: | ||
| 588 | if (spi->irq) | ||
| 589 | iio_unregister_interrupt_line(st->indio_dev, 0); | ||
| 590 | error_uninitialize_ring: | ||
| 591 | adis16260_uninitialize_ring(st->indio_dev->ring); | ||
| 592 | error_unreg_ring_funcs: | ||
| 593 | adis16260_unconfigure_ring(st->indio_dev); | ||
| 594 | error_free_dev: | ||
| 595 | if (regdone) | ||
| 596 | iio_device_unregister(st->indio_dev); | ||
| 597 | else | ||
| 598 | iio_free_device(st->indio_dev); | ||
| 599 | error_free_tx: | ||
| 600 | kfree(st->tx); | ||
| 601 | error_free_rx: | ||
| 602 | kfree(st->rx); | ||
| 603 | error_free_st: | ||
| 604 | kfree(st); | ||
| 605 | error_ret: | ||
| 606 | return ret; | ||
| 607 | } | ||
| 608 | |||
| 609 | static int adis16260_remove(struct spi_device *spi) | ||
| 610 | { | ||
| 611 | int ret; | ||
| 612 | struct adis16260_state *st = spi_get_drvdata(spi); | ||
| 613 | struct iio_dev *indio_dev = st->indio_dev; | ||
| 614 | |||
| 615 | ret = adis16260_stop_device(&(indio_dev->dev)); | ||
| 616 | if (ret) | ||
| 617 | goto err_ret; | ||
| 618 | |||
| 619 | flush_scheduled_work(); | ||
| 620 | |||
| 621 | adis16260_remove_trigger(indio_dev); | ||
| 622 | if (spi->irq) | ||
| 623 | iio_unregister_interrupt_line(indio_dev, 0); | ||
| 624 | |||
| 625 | adis16260_uninitialize_ring(indio_dev->ring); | ||
| 626 | iio_device_unregister(indio_dev); | ||
| 627 | adis16260_unconfigure_ring(indio_dev); | ||
| 628 | kfree(st->tx); | ||
| 629 | kfree(st->rx); | ||
| 630 | kfree(st); | ||
| 631 | |||
| 632 | return 0; | ||
| 633 | |||
| 634 | err_ret: | ||
| 635 | return ret; | ||
| 636 | } | ||
| 637 | |||
| 638 | static struct spi_driver adis16260_driver = { | ||
| 639 | .driver = { | ||
| 640 | .name = "adis16260", | ||
| 641 | .owner = THIS_MODULE, | ||
| 642 | }, | ||
| 643 | .probe = adis16260_probe, | ||
| 644 | .remove = __devexit_p(adis16260_remove), | ||
| 645 | }; | ||
| 646 | |||
| 647 | static __init int adis16260_init(void) | ||
| 648 | { | ||
| 649 | return spi_register_driver(&adis16260_driver); | ||
| 650 | } | ||
| 651 | module_init(adis16260_init); | ||
| 652 | |||
| 653 | static __exit void adis16260_exit(void) | ||
| 654 | { | ||
| 655 | spi_unregister_driver(&adis16260_driver); | ||
| 656 | } | ||
| 657 | module_exit(adis16260_exit); | ||
| 658 | |||
| 659 | MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>"); | ||
| 660 | MODULE_DESCRIPTION("Analog Devices ADIS16260/5 Digital Gyroscope Sensor"); | ||
| 661 | MODULE_LICENSE("GPL v2"); | ||
diff --git a/drivers/staging/iio/gyro/adis16260_ring.c b/drivers/staging/iio/gyro/adis16260_ring.c new file mode 100644 index 000000000000..4c4390ca6d73 --- /dev/null +++ b/drivers/staging/iio/gyro/adis16260_ring.c | |||
| @@ -0,0 +1,256 @@ | |||
| 1 | #include <linux/interrupt.h> | ||
| 2 | #include <linux/irq.h> | ||
| 3 | #include <linux/gpio.h> | ||
| 4 | #include <linux/workqueue.h> | ||
| 5 | #include <linux/mutex.h> | ||
| 6 | #include <linux/device.h> | ||
| 7 | #include <linux/kernel.h> | ||
| 8 | #include <linux/spi/spi.h> | ||
| 9 | #include <linux/sysfs.h> | ||
| 10 | #include <linux/list.h> | ||
| 11 | |||
| 12 | #include "../iio.h" | ||
| 13 | #include "../sysfs.h" | ||
| 14 | #include "../ring_sw.h" | ||
| 15 | #include "../accel/accel.h" | ||
| 16 | #include "../trigger.h" | ||
| 17 | #include "adis16260.h" | ||
| 18 | |||
| 19 | /** | ||
| 20 | * combine_8_to_16() utility function to munge to u8s into u16 | ||
| 21 | **/ | ||
| 22 | static inline u16 combine_8_to_16(u8 lower, u8 upper) | ||
| 23 | { | ||
| 24 | u16 _lower = lower; | ||
| 25 | u16 _upper = upper; | ||
| 26 | return _lower | (_upper << 8); | ||
| 27 | } | ||
| 28 | |||
| 29 | static IIO_SCAN_EL_C(supply, ADIS16260_SCAN_SUPPLY, IIO_UNSIGNED(12), | ||
| 30 | ADIS16260_SUPPLY_OUT, NULL); | ||
| 31 | static IIO_SCAN_EL_C(gyro, ADIS16260_SCAN_GYRO, IIO_SIGNED(14), | ||
| 32 | ADIS16260_GYRO_OUT, NULL); | ||
| 33 | static IIO_SCAN_EL_C(aux_adc, ADIS16260_SCAN_AUX_ADC, IIO_SIGNED(14), | ||
| 34 | ADIS16260_AUX_ADC, NULL); | ||
| 35 | static IIO_SCAN_EL_C(temp, ADIS16260_SCAN_TEMP, IIO_UNSIGNED(12), | ||
| 36 | ADIS16260_TEMP_OUT, NULL); | ||
| 37 | static IIO_SCAN_EL_C(angl, ADIS16260_SCAN_ANGL, IIO_UNSIGNED(12), | ||
| 38 | ADIS16260_ANGL_OUT, NULL); | ||
| 39 | |||
| 40 | static IIO_SCAN_EL_TIMESTAMP(5); | ||
| 41 | |||
| 42 | static struct attribute *adis16260_scan_el_attrs[] = { | ||
| 43 | &iio_scan_el_supply.dev_attr.attr, | ||
| 44 | &iio_scan_el_gyro.dev_attr.attr, | ||
| 45 | &iio_scan_el_aux_adc.dev_attr.attr, | ||
| 46 | &iio_scan_el_temp.dev_attr.attr, | ||
| 47 | &iio_scan_el_angl.dev_attr.attr, | ||
| 48 | &iio_scan_el_timestamp.dev_attr.attr, | ||
| 49 | NULL, | ||
| 50 | }; | ||
| 51 | |||
| 52 | static struct attribute_group adis16260_scan_el_group = { | ||
| 53 | .attrs = adis16260_scan_el_attrs, | ||
| 54 | .name = "scan_elements", | ||
| 55 | }; | ||
| 56 | |||
| 57 | /** | ||
| 58 | * adis16260_poll_func_th() top half interrupt handler called by trigger | ||
| 59 | * @private_data: iio_dev | ||
| 60 | **/ | ||
| 61 | static void adis16260_poll_func_th(struct iio_dev *indio_dev) | ||
| 62 | { | ||
| 63 | struct adis16260_state *st = iio_dev_get_devdata(indio_dev); | ||
| 64 | st->last_timestamp = indio_dev->trig->timestamp; | ||
| 65 | schedule_work(&st->work_trigger_to_ring); | ||
| 66 | } | ||
| 67 | |||
| 68 | /** | ||
| 69 | * adis16260_read_ring_data() read data registers which will be placed into ring | ||
| 70 | * @dev: device associated with child of actual device (iio_dev or iio_trig) | ||
| 71 | * @rx: somewhere to pass back the value read | ||
| 72 | **/ | ||
| 73 | static int adis16260_read_ring_data(struct device *dev, u8 *rx) | ||
| 74 | { | ||
| 75 | struct spi_message msg; | ||
| 76 | struct iio_dev *indio_dev = dev_get_drvdata(dev); | ||
| 77 | struct adis16260_state *st = iio_dev_get_devdata(indio_dev); | ||
| 78 | struct spi_transfer xfers[ADIS16260_OUTPUTS + 1]; | ||
| 79 | int ret; | ||
| 80 | int i; | ||
| 81 | |||
| 82 | mutex_lock(&st->buf_lock); | ||
| 83 | |||
| 84 | spi_message_init(&msg); | ||
| 85 | |||
| 86 | memset(xfers, 0, sizeof(xfers)); | ||
| 87 | for (i = 0; i <= ADIS16260_OUTPUTS; i++) { | ||
| 88 | xfers[i].bits_per_word = 8; | ||
| 89 | xfers[i].cs_change = 1; | ||
| 90 | xfers[i].len = 2; | ||
| 91 | xfers[i].delay_usecs = 30; | ||
| 92 | xfers[i].tx_buf = st->tx + 2 * i; | ||
| 93 | if (i < 2) /* SUPPLY_OUT:0x02 GYRO_OUT:0x04 */ | ||
| 94 | st->tx[2 * i] | ||
| 95 | = ADIS16260_READ_REG(ADIS16260_SUPPLY_OUT | ||
| 96 | + 2 * i); | ||
| 97 | else /* 0x06 to 0x09 is reserved */ | ||
| 98 | st->tx[2 * i] | ||
| 99 | = ADIS16260_READ_REG(ADIS16260_SUPPLY_OUT | ||
| 100 | + 2 * i + 4); | ||
| 101 | st->tx[2 * i + 1] = 0; | ||
| 102 | if (i >= 1) | ||
| 103 | xfers[i].rx_buf = rx + 2 * (i - 1); | ||
| 104 | spi_message_add_tail(&xfers[i], &msg); | ||
| 105 | } | ||
| 106 | |||
| 107 | ret = spi_sync(st->us, &msg); | ||
| 108 | if (ret) | ||
| 109 | dev_err(&st->us->dev, "problem when burst reading"); | ||
| 110 | |||
| 111 | mutex_unlock(&st->buf_lock); | ||
| 112 | |||
| 113 | return ret; | ||
| 114 | } | ||
| 115 | |||
| 116 | |||
| 117 | static void adis16260_trigger_bh_to_ring(struct work_struct *work_s) | ||
| 118 | { | ||
| 119 | struct adis16260_state *st | ||
| 120 | = container_of(work_s, struct adis16260_state, | ||
| 121 | work_trigger_to_ring); | ||
| 122 | |||
| 123 | int i = 0; | ||
| 124 | s16 *data; | ||
| 125 | size_t datasize = st->indio_dev | ||
| 126 | ->ring->access.get_bpd(st->indio_dev->ring); | ||
| 127 | |||
| 128 | data = kmalloc(datasize , GFP_KERNEL); | ||
| 129 | if (data == NULL) { | ||
| 130 | dev_err(&st->us->dev, "memory alloc failed in ring bh"); | ||
| 131 | return; | ||
| 132 | } | ||
| 133 | |||
| 134 | if (st->indio_dev->scan_count) | ||
| 135 | if (adis16260_read_ring_data(&st->indio_dev->dev, st->rx) >= 0) | ||
| 136 | for (; i < st->indio_dev->scan_count; i++) { | ||
| 137 | data[i] = combine_8_to_16(st->rx[i*2+1], | ||
| 138 | st->rx[i*2]); | ||
| 139 | } | ||
| 140 | |||
| 141 | /* Guaranteed to be aligned with 8 byte boundary */ | ||
| 142 | if (st->indio_dev->scan_timestamp) | ||
| 143 | *((s64 *)(data + ((i + 3)/4)*4)) = st->last_timestamp; | ||
| 144 | |||
| 145 | st->indio_dev->ring->access.store_to(st->indio_dev->ring, | ||
| 146 | (u8 *)data, | ||
| 147 | st->last_timestamp); | ||
| 148 | |||
| 149 | iio_trigger_notify_done(st->indio_dev->trig); | ||
| 150 | kfree(data); | ||
| 151 | |||
| 152 | return; | ||
| 153 | } | ||
| 154 | |||
| 155 | static int adis16260_data_rdy_ring_preenable(struct iio_dev *indio_dev) | ||
| 156 | { | ||
| 157 | size_t size; | ||
| 158 | dev_dbg(&indio_dev->dev, "%s\n", __func__); | ||
| 159 | /* Check if there are any scan elements enabled, if not fail*/ | ||
| 160 | if (!(indio_dev->scan_count || indio_dev->scan_timestamp)) | ||
| 161 | return -EINVAL; | ||
| 162 | |||
| 163 | if (indio_dev->ring->access.set_bpd) { | ||
| 164 | if (indio_dev->scan_timestamp) | ||
| 165 | if (indio_dev->scan_count) | ||
| 166 | /* Timestamp (aligned s64) and data */ | ||
| 167 | size = (((indio_dev->scan_count * sizeof(s16)) | ||
| 168 | + sizeof(s64) - 1) | ||
| 169 | & ~(sizeof(s64) - 1)) | ||
| 170 | + sizeof(s64); | ||
| 171 | else /* Timestamp only */ | ||
| 172 | size = sizeof(s64); | ||
| 173 | else /* Data only */ | ||
| 174 | size = indio_dev->scan_count*sizeof(s16); | ||
| 175 | indio_dev->ring->access.set_bpd(indio_dev->ring, size); | ||
| 176 | } | ||
| 177 | |||
| 178 | return 0; | ||
| 179 | } | ||
| 180 | |||
| 181 | static int adis16260_data_rdy_ring_postenable(struct iio_dev *indio_dev) | ||
| 182 | { | ||
| 183 | return indio_dev->trig | ||
| 184 | ? iio_trigger_attach_poll_func(indio_dev->trig, | ||
| 185 | indio_dev->pollfunc) | ||
| 186 | : 0; | ||
| 187 | } | ||
| 188 | |||
| 189 | static int adis16260_data_rdy_ring_predisable(struct iio_dev *indio_dev) | ||
| 190 | { | ||
| 191 | return indio_dev->trig | ||
| 192 | ? iio_trigger_dettach_poll_func(indio_dev->trig, | ||
| 193 | indio_dev->pollfunc) | ||
| 194 | : 0; | ||
| 195 | } | ||
| 196 | |||
| 197 | void adis16260_unconfigure_ring(struct iio_dev *indio_dev) | ||
| 198 | { | ||
| 199 | kfree(indio_dev->pollfunc); | ||
| 200 | iio_sw_rb_free(indio_dev->ring); | ||
| 201 | } | ||
| 202 | |||
| 203 | int adis16260_configure_ring(struct iio_dev *indio_dev) | ||
| 204 | { | ||
| 205 | int ret = 0; | ||
| 206 | struct adis16260_state *st = indio_dev->dev_data; | ||
| 207 | struct iio_ring_buffer *ring; | ||
| 208 | INIT_WORK(&st->work_trigger_to_ring, adis16260_trigger_bh_to_ring); | ||
| 209 | /* Set default scan mode */ | ||
| 210 | |||
| 211 | iio_scan_mask_set(indio_dev, iio_scan_el_supply.number); | ||
| 212 | iio_scan_mask_set(indio_dev, iio_scan_el_gyro.number); | ||
| 213 | iio_scan_mask_set(indio_dev, iio_scan_el_aux_adc.number); | ||
| 214 | iio_scan_mask_set(indio_dev, iio_scan_el_temp.number); | ||
| 215 | iio_scan_mask_set(indio_dev, iio_scan_el_angl.number); | ||
| 216 | indio_dev->scan_timestamp = true; | ||
| 217 | |||
| 218 | indio_dev->scan_el_attrs = &adis16260_scan_el_group; | ||
| 219 | |||
| 220 | ring = iio_sw_rb_allocate(indio_dev); | ||
| 221 | if (!ring) { | ||
| 222 | ret = -ENOMEM; | ||
| 223 | return ret; | ||
| 224 | } | ||
| 225 | indio_dev->ring = ring; | ||
| 226 | /* Effectively select the ring buffer implementation */ | ||
| 227 | iio_ring_sw_register_funcs(&ring->access); | ||
| 228 | ring->preenable = &adis16260_data_rdy_ring_preenable; | ||
| 229 | ring->postenable = &adis16260_data_rdy_ring_postenable; | ||
| 230 | ring->predisable = &adis16260_data_rdy_ring_predisable; | ||
| 231 | ring->owner = THIS_MODULE; | ||
| 232 | |||
| 233 | indio_dev->pollfunc = kzalloc(sizeof(*indio_dev->pollfunc), GFP_KERNEL); | ||
| 234 | if (indio_dev->pollfunc == NULL) { | ||
| 235 | ret = -ENOMEM; | ||
| 236 | goto error_iio_sw_rb_free;; | ||
| 237 | } | ||
| 238 | indio_dev->pollfunc->poll_func_main = &adis16260_poll_func_th; | ||
| 239 | indio_dev->pollfunc->private_data = indio_dev; | ||
| 240 | indio_dev->modes |= INDIO_RING_TRIGGERED; | ||
| 241 | return 0; | ||
| 242 | |||
| 243 | error_iio_sw_rb_free: | ||
| 244 | iio_sw_rb_free(indio_dev->ring); | ||
| 245 | return ret; | ||
| 246 | } | ||
| 247 | |||
| 248 | int adis16260_initialize_ring(struct iio_ring_buffer *ring) | ||
| 249 | { | ||
| 250 | return iio_ring_buffer_register(ring, 0); | ||
| 251 | } | ||
| 252 | |||
| 253 | void adis16260_uninitialize_ring(struct iio_ring_buffer *ring) | ||
| 254 | { | ||
| 255 | iio_ring_buffer_unregister(ring); | ||
| 256 | } | ||
diff --git a/drivers/staging/iio/gyro/adis16260_trigger.c b/drivers/staging/iio/gyro/adis16260_trigger.c new file mode 100644 index 000000000000..b3c565942b8d --- /dev/null +++ b/drivers/staging/iio/gyro/adis16260_trigger.c | |||
| @@ -0,0 +1,124 @@ | |||
| 1 | #include <linux/interrupt.h> | ||
| 2 | #include <linux/irq.h> | ||
| 3 | #include <linux/mutex.h> | ||
| 4 | #include <linux/device.h> | ||
| 5 | #include <linux/kernel.h> | ||
| 6 | #include <linux/sysfs.h> | ||
| 7 | #include <linux/list.h> | ||
| 8 | #include <linux/spi/spi.h> | ||
| 9 | |||
| 10 | #include "../iio.h" | ||
| 11 | #include "../sysfs.h" | ||
| 12 | #include "../trigger.h" | ||
| 13 | #include "adis16260.h" | ||
| 14 | |||
| 15 | /** | ||
| 16 | * adis16260_data_rdy_trig_poll() the event handler for the data rdy trig | ||
| 17 | **/ | ||
| 18 | static int adis16260_data_rdy_trig_poll(struct iio_dev *dev_info, | ||
| 19 | int index, | ||
| 20 | s64 timestamp, | ||
| 21 | int no_test) | ||
| 22 | { | ||
| 23 | struct adis16260_state *st = iio_dev_get_devdata(dev_info); | ||
| 24 | struct iio_trigger *trig = st->trig; | ||
| 25 | |||
| 26 | trig->timestamp = timestamp; | ||
| 27 | iio_trigger_poll(trig); | ||
| 28 | |||
| 29 | return IRQ_HANDLED; | ||
| 30 | } | ||
| 31 | |||
| 32 | IIO_EVENT_SH(data_rdy_trig, &adis16260_data_rdy_trig_poll); | ||
| 33 | |||
| 34 | static DEVICE_ATTR(name, S_IRUGO, iio_trigger_read_name, NULL); | ||
| 35 | |||
| 36 | static struct attribute *adis16260_trigger_attrs[] = { | ||
| 37 | &dev_attr_name.attr, | ||
| 38 | NULL, | ||
| 39 | }; | ||
| 40 | |||
| 41 | static const struct attribute_group adis16260_trigger_attr_group = { | ||
| 42 | .attrs = adis16260_trigger_attrs, | ||
| 43 | }; | ||
| 44 | |||
| 45 | /** | ||
| 46 | * adis16260_data_rdy_trigger_set_state() set datardy interrupt state | ||
| 47 | **/ | ||
| 48 | static int adis16260_data_rdy_trigger_set_state(struct iio_trigger *trig, | ||
| 49 | bool state) | ||
| 50 | { | ||
| 51 | struct adis16260_state *st = trig->private_data; | ||
| 52 | struct iio_dev *indio_dev = st->indio_dev; | ||
| 53 | int ret = 0; | ||
| 54 | |||
| 55 | dev_dbg(&indio_dev->dev, "%s (%d)\n", __func__, state); | ||
| 56 | ret = adis16260_set_irq(&st->indio_dev->dev, state); | ||
| 57 | if (state == false) { | ||
| 58 | iio_remove_event_from_list(&iio_event_data_rdy_trig, | ||
| 59 | &indio_dev->interrupts[0] | ||
| 60 | ->ev_list); | ||
| 61 | flush_scheduled_work(); | ||
| 62 | } else { | ||
| 63 | iio_add_event_to_list(&iio_event_data_rdy_trig, | ||
| 64 | &indio_dev->interrupts[0]->ev_list); | ||
| 65 | } | ||
| 66 | return ret; | ||
| 67 | } | ||
| 68 | |||
| 69 | /** | ||
| 70 | * adis16260_trig_try_reen() try renabling irq for data rdy trigger | ||
| 71 | * @trig: the datardy trigger | ||
| 72 | **/ | ||
| 73 | static int adis16260_trig_try_reen(struct iio_trigger *trig) | ||
| 74 | { | ||
| 75 | struct adis16260_state *st = trig->private_data; | ||
| 76 | enable_irq(st->us->irq); | ||
| 77 | return 0; | ||
| 78 | } | ||
| 79 | |||
| 80 | int adis16260_probe_trigger(struct iio_dev *indio_dev) | ||
| 81 | { | ||
| 82 | int ret; | ||
| 83 | struct adis16260_state *st = indio_dev->dev_data; | ||
| 84 | |||
| 85 | st->trig = iio_allocate_trigger(); | ||
| 86 | st->trig->name = kmalloc(IIO_TRIGGER_NAME_LENGTH, GFP_KERNEL); | ||
| 87 | if (!st->trig->name) { | ||
| 88 | ret = -ENOMEM; | ||
| 89 | goto error_free_trig; | ||
| 90 | } | ||
| 91 | snprintf((char *)st->trig->name, | ||
| 92 | IIO_TRIGGER_NAME_LENGTH, | ||
| 93 | "adis16260-dev%d", indio_dev->id); | ||
| 94 | st->trig->dev.parent = &st->us->dev; | ||
| 95 | st->trig->owner = THIS_MODULE; | ||
| 96 | st->trig->private_data = st; | ||
| 97 | st->trig->set_trigger_state = &adis16260_data_rdy_trigger_set_state; | ||
| 98 | st->trig->try_reenable = &adis16260_trig_try_reen; | ||
| 99 | st->trig->control_attrs = &adis16260_trigger_attr_group; | ||
| 100 | ret = iio_trigger_register(st->trig); | ||
| 101 | |||
| 102 | /* select default trigger */ | ||
| 103 | indio_dev->trig = st->trig; | ||
| 104 | if (ret) | ||
| 105 | goto error_free_trig_name; | ||
| 106 | |||
| 107 | return 0; | ||
| 108 | |||
| 109 | error_free_trig_name: | ||
| 110 | kfree(st->trig->name); | ||
| 111 | error_free_trig: | ||
| 112 | iio_free_trigger(st->trig); | ||
| 113 | |||
| 114 | return ret; | ||
| 115 | } | ||
| 116 | |||
| 117 | void adis16260_remove_trigger(struct iio_dev *indio_dev) | ||
| 118 | { | ||
| 119 | struct adis16260_state *state = indio_dev->dev_data; | ||
| 120 | |||
| 121 | iio_trigger_unregister(state->trig); | ||
| 122 | kfree(state->trig->name); | ||
| 123 | iio_free_trigger(state->trig); | ||
| 124 | } | ||
diff --git a/drivers/staging/iio/gyro/gyro.h b/drivers/staging/iio/gyro/gyro.h index 16f6ffa039bb..f68edab8f30d 100644 --- a/drivers/staging/iio/gyro/gyro.h +++ b/drivers/staging/iio/gyro/gyro.h | |||
| @@ -39,3 +39,5 @@ | |||
| 39 | #define IIO_DEV_ATTR_GYRO_Z(_show, _addr) \ | 39 | #define IIO_DEV_ATTR_GYRO_Z(_show, _addr) \ |
| 40 | IIO_DEVICE_ATTR(gyro_z_raw, S_IRUGO, _show, NULL, _addr) | 40 | IIO_DEVICE_ATTR(gyro_z_raw, S_IRUGO, _show, NULL, _addr) |
| 41 | 41 | ||
| 42 | #define IIO_DEV_ATTR_ANGL(_show, _addr) \ | ||
| 43 | IIO_DEVICE_ATTR(angl_raw, S_IRUGO, _show, NULL, _addr) | ||
