adis16204_core.c revision 4de66bbb3eb1099a76e1bbf87e376c7a31dc30d5
1bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song/* 2bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song * ADIS16204 Programmable High-g Digital Impact Sensor and Recorder 3bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song * 4bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song * Copyright 2010 Analog Devices Inc. 5bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song * 6bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song * Licensed under the GPL-2 or later. 7bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song */ 8bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song 9bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song#include <linux/interrupt.h> 10bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song#include <linux/irq.h> 11bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song#include <linux/gpio.h> 12bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song#include <linux/delay.h> 13bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song#include <linux/mutex.h> 14bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song#include <linux/device.h> 15bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song#include <linux/kernel.h> 16bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song#include <linux/spi/spi.h> 17bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song#include <linux/slab.h> 18bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song#include <linux/sysfs.h> 19bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song#include <linux/list.h> 20bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song 21bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song#include "../iio.h" 22bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song#include "../sysfs.h" 23ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron#include "../ring_generic.h" 24bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song#include "accel.h" 25bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song#include "../adc/adc.h" 26bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song 27bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song#include "adis16204.h" 28bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song 29bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song#define DRIVER_NAME "adis16204" 30bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song 31bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song/** 32bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song * adis16204_spi_write_reg_8() - write single byte to a register 33bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song * @dev: device associated with child of actual device (iio_dev or iio_trig) 34bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song * @reg_address: the address of the register to be written 35bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song * @val: the value to write 36bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song **/ 37ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameronstatic int adis16204_spi_write_reg_8(struct iio_dev *indio_dev, 38bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song u8 reg_address, 39bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song u8 val) 40bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song{ 41bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song int ret; 424de66bbb3eb1099a76e1bbf87e376c7a31dc30d5Jonathan Cameron struct adis16204_state *st = iio_priv(indio_dev); 43bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song 44bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song mutex_lock(&st->buf_lock); 45bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song st->tx[0] = ADIS16204_WRITE_REG(reg_address); 46bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song st->tx[1] = val; 47bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song 48bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song ret = spi_write(st->us, st->tx, 2); 49bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song mutex_unlock(&st->buf_lock); 50bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song 51bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song return ret; 52bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song} 53bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song 54bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song/** 55bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song * adis16204_spi_write_reg_16() - write 2 bytes to a pair of registers 56ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron * @indio_dev: iio device associated with child of actual device 57bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song * @reg_address: the address of the lower of the two registers. Second register 58bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song * is assumed to have address one greater. 59bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song * @val: value to be written 60bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song **/ 61ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameronstatic int adis16204_spi_write_reg_16(struct iio_dev *indio_dev, 62bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song u8 lower_reg_address, 63bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song u16 value) 64bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song{ 65bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song int ret; 66bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song struct spi_message msg; 674de66bbb3eb1099a76e1bbf87e376c7a31dc30d5Jonathan Cameron struct adis16204_state *st = iio_priv(indio_dev); 68bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song struct spi_transfer xfers[] = { 69bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song { 70bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song .tx_buf = st->tx, 71bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song .bits_per_word = 8, 72bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song .len = 2, 73bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song .cs_change = 1, 74bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song }, { 75bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song .tx_buf = st->tx + 2, 76bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song .bits_per_word = 8, 77bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song .len = 2, 78bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song .cs_change = 1, 79bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song }, 80bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song }; 81bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song 82bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song mutex_lock(&st->buf_lock); 83bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song st->tx[0] = ADIS16204_WRITE_REG(lower_reg_address); 84bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song st->tx[1] = value & 0xFF; 85bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song st->tx[2] = ADIS16204_WRITE_REG(lower_reg_address + 1); 86bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song st->tx[3] = (value >> 8) & 0xFF; 87bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song 88bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song spi_message_init(&msg); 89bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song spi_message_add_tail(&xfers[0], &msg); 90bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song spi_message_add_tail(&xfers[1], &msg); 91bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song ret = spi_sync(st->us, &msg); 92bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song mutex_unlock(&st->buf_lock); 93bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song 94bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song return ret; 95bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song} 96bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song 97bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song/** 98bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song * adis16204_spi_read_reg_16() - read 2 bytes from a 16-bit register 99ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron * @indio_dev: iio device associated with child of actual device 100bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song * @reg_address: the address of the lower of the two registers. Second register 101bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song * is assumed to have address one greater. 102bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song * @val: somewhere to pass back the value read 103bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song **/ 104ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameronstatic int adis16204_spi_read_reg_16(struct iio_dev *indio_dev, 105ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron u8 lower_reg_address, 106ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron u16 *val) 107bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song{ 108bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song struct spi_message msg; 1094de66bbb3eb1099a76e1bbf87e376c7a31dc30d5Jonathan Cameron struct adis16204_state *st = iio_priv(indio_dev); 110bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song int ret; 111bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song struct spi_transfer xfers[] = { 112bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song { 113bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song .tx_buf = st->tx, 114bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song .bits_per_word = 8, 115bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song .len = 2, 116bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song .cs_change = 1, 117bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song .delay_usecs = 20, 118bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song }, { 119bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song .rx_buf = st->rx, 120bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song .bits_per_word = 8, 121bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song .len = 2, 122bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song .delay_usecs = 20, 123bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song }, 124bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song }; 125bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song 126bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song mutex_lock(&st->buf_lock); 127bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song st->tx[0] = ADIS16204_READ_REG(lower_reg_address); 128bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song st->tx[1] = 0; 129bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song 130bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song spi_message_init(&msg); 131bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song spi_message_add_tail(&xfers[0], &msg); 132bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song spi_message_add_tail(&xfers[1], &msg); 133bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song ret = spi_sync(st->us, &msg); 134bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song if (ret) { 135bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song dev_err(&st->us->dev, "problem when reading 16 bit register 0x%02X", 136bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song lower_reg_address); 137bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song goto error_ret; 138bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song } 139bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song *val = (st->rx[0] << 8) | st->rx[1]; 140bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song 141bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Songerror_ret: 142bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song mutex_unlock(&st->buf_lock); 143bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song return ret; 144bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song} 145bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song 146ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameronstatic int adis16204_check_status(struct iio_dev *indio_dev) 147bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song{ 148ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron u16 status; 149bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song int ret; 150bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song 151ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron ret = adis16204_spi_read_reg_16(indio_dev, 152ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron ADIS16204_DIAG_STAT, &status); 153ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron if (ret < 0) { 154ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron dev_err(&indio_dev->dev, "Reading status failed\n"); 155bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song goto error_ret; 156bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song } 157ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron ret = status & 0x1F; 158bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song 159ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron if (status & ADIS16204_DIAG_STAT_SELFTEST_FAIL) 160ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron dev_err(&indio_dev->dev, "Self test failure\n"); 161ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron if (status & ADIS16204_DIAG_STAT_SPI_FAIL) 162ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron dev_err(&indio_dev->dev, "SPI failure\n"); 163ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron if (status & ADIS16204_DIAG_STAT_FLASH_UPT) 164ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron dev_err(&indio_dev->dev, "Flash update failed\n"); 165ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron if (status & ADIS16204_DIAG_STAT_POWER_HIGH) 166ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron dev_err(&indio_dev->dev, "Power supply above 3.625V\n"); 167ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron if (status & ADIS16204_DIAG_STAT_POWER_LOW) 168ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron dev_err(&indio_dev->dev, "Power supply below 2.975V\n"); 169bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song 170ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameronerror_ret: 171bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song return ret; 172bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song} 173bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song 174bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Songstatic ssize_t adis16204_read_14bit_signed(struct device *dev, 175bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song struct device_attribute *attr, 176bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song char *buf) 177bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song{ 178bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song struct iio_dev *indio_dev = dev_get_drvdata(dev); 179bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); 180bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song s16 val = 0; 181bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song ssize_t ret; 182bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song 183bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song mutex_lock(&indio_dev->mlock); 184bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song 185ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron ret = adis16204_spi_read_reg_16(indio_dev, 186ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron this_attr->address, (u16 *)&val); 187bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song if (!ret) { 188bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song if (val & ADIS16204_ERROR_ACTIVE) 189ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron adis16204_check_status(indio_dev); 190bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song 191bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song val = ((s16)(val << 2) >> 2); 192bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song ret = sprintf(buf, "%d\n", val); 193bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song } 194bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song 195bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song mutex_unlock(&indio_dev->mlock); 196bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song 197bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song return ret; 198bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song} 199bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song 200ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameronstatic int adis16204_reset(struct iio_dev *indio_dev) 201bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song{ 202bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song int ret; 203ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron ret = adis16204_spi_write_reg_8(indio_dev, 204bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song ADIS16204_GLOB_CMD, 205bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song ADIS16204_GLOB_CMD_SW_RESET); 206bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song if (ret) 207ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron dev_err(&indio_dev->dev, "problem resetting device"); 208bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song 209bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song return ret; 210bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song} 211bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song 212bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Songstatic ssize_t adis16204_write_reset(struct device *dev, 213bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song struct device_attribute *attr, 214bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song const char *buf, size_t len) 215bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song{ 216ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron struct iio_dev *indio_dev = dev_get_drvdata(dev); 217ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron 218bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song if (len < 1) 219bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song return -EINVAL; 220bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song switch (buf[0]) { 221bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song case '1': 222bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song case 'y': 223bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song case 'Y': 224ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron return adis16204_reset(indio_dev); 225bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song } 226bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song return -EINVAL; 227bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song} 228bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song 229ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameronint adis16204_set_irq(struct iio_dev *indio_dev, bool enable) 230bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song{ 231bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song int ret = 0; 232bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song u16 msc; 233bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song 234ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron ret = adis16204_spi_read_reg_16(indio_dev, ADIS16204_MSC_CTRL, &msc); 235bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song if (ret) 236bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song goto error_ret; 237bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song 238bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song msc |= ADIS16204_MSC_CTRL_ACTIVE_HIGH; 239bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song msc &= ~ADIS16204_MSC_CTRL_DATA_RDY_DIO2; 240bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song if (enable) 241bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song msc |= ADIS16204_MSC_CTRL_DATA_RDY_EN; 242bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song else 243bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song msc &= ~ADIS16204_MSC_CTRL_DATA_RDY_EN; 244bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song 245ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron ret = adis16204_spi_write_reg_16(indio_dev, ADIS16204_MSC_CTRL, msc); 246bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song 247bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Songerror_ret: 248bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song return ret; 249bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song} 250bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song 251ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameronstatic int adis16204_self_test(struct iio_dev *indio_dev) 252bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song{ 253bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song int ret; 254ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron ret = adis16204_spi_write_reg_16(indio_dev, 255bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song ADIS16204_MSC_CTRL, 256bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song ADIS16204_MSC_CTRL_SELF_TEST_EN); 257bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song if (ret) { 258ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron dev_err(&indio_dev->dev, "problem starting self test"); 259bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song goto err_ret; 260bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song } 261bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song 262ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron adis16204_check_status(indio_dev); 263bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song 264bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Songerr_ret: 265bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song return ret; 266bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song} 267bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song 268ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameronstatic int adis16204_initial_setup(struct iio_dev *indio_dev) 269bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song{ 270bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song int ret; 271bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song 272bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song /* Disable IRQ */ 273ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron ret = adis16204_set_irq(indio_dev, false); 274bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song if (ret) { 275ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron dev_err(&indio_dev->dev, "disable irq failed"); 276bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song goto err_ret; 277bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song } 278bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song 279bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song /* Do self test */ 280ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron ret = adis16204_self_test(indio_dev); 281bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song if (ret) { 282ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron dev_err(&indio_dev->dev, "self test failure"); 283bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song goto err_ret; 284bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song } 285bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song 286bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song /* Read status register to check the result */ 287ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron ret = adis16204_check_status(indio_dev); 288bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song if (ret) { 289ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron adis16204_reset(indio_dev); 290ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron dev_err(&indio_dev->dev, "device not playing ball -> reset"); 291bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song msleep(ADIS16204_STARTUP_DELAY); 292ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron ret = adis16204_check_status(indio_dev); 293bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song if (ret) { 294ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron dev_err(&indio_dev->dev, "giving up"); 295bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song goto err_ret; 296bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song } 297bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song } 298bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song 299bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Songerr_ret: 300bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song return ret; 301bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song} 302bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Songstatic IIO_DEV_ATTR_ACCEL_XY(adis16204_read_14bit_signed, 303bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song ADIS16204_XY_RSS_OUT); 304bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Songstatic IIO_DEV_ATTR_ACCEL_XPEAK(adis16204_read_14bit_signed, 305bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song ADIS16204_X_PEAK_OUT); 306bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Songstatic IIO_DEV_ATTR_ACCEL_YPEAK(adis16204_read_14bit_signed, 307bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song ADIS16204_Y_PEAK_OUT); 308bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Songstatic IIO_DEV_ATTR_ACCEL_XYPEAK(adis16204_read_14bit_signed, 309bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song ADIS16204_XY_PEAK_OUT); 310bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Songstatic IIO_CONST_ATTR(accel_xy_scale, "0.017125"); 311bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song 312bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Songstatic IIO_DEVICE_ATTR(reset, S_IWUSR, NULL, adis16204_write_reset, 0); 313bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song 314ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameronenum adis16204_channel { 315ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron in_supply, 316ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron in_aux, 317ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron temp, 318ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron accel_x, 319ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron accel_y, 320ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron}; 321ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron 322ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameronstatic u8 adis16204_addresses[5][2] = { 323ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron [in_supply] = { ADIS16204_SUPPLY_OUT }, 324ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron [in_aux] = { ADIS16204_AUX_ADC }, 325ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron [temp] = { ADIS16204_TEMP_OUT }, 326ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron [accel_x] = { ADIS16204_XACCL_OUT, ADIS16204_XACCL_NULL }, 327ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron [accel_y] = { ADIS16204_XACCL_OUT, ADIS16204_YACCL_NULL }, 328ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron}; 329ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameronstatic int adis16204_read_raw(struct iio_dev *indio_dev, 330ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron struct iio_chan_spec const *chan, 331ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron int *val, int *val2, 332ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron long mask) 333ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron{ 334ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron int ret; 335ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron int bits; 336ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron u8 addr; 337ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron s16 val16; 338ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron 339ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron switch (mask) { 340ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron case 0: 341ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron mutex_lock(&indio_dev->mlock); 342ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron addr = adis16204_addresses[chan->address][0]; 343ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron ret = adis16204_spi_read_reg_16(indio_dev, addr, &val16); 344ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron if (ret) 345ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron return ret; 346ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron 347ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron if (val16 & ADIS16204_ERROR_ACTIVE) { 348ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron ret = adis16204_check_status(indio_dev); 349ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron if (ret) 350ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron return ret; 351ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron } 352ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron val16 = val16 & ((1 << chan->scan_type.realbits) - 1); 353ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron if (chan->scan_type.sign == 's') 354ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron val16 = (s16)(val16 << 355ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron (16 - chan->scan_type.realbits)) >> 356ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron (16 - chan->scan_type.realbits); 357ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron *val = val16; 358ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron mutex_unlock(&indio_dev->mlock); 359ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron return IIO_VAL_INT; 360ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron case (1 << IIO_CHAN_INFO_SCALE_SEPARATE): 361ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron switch (chan->type) { 362ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron case IIO_IN: 363ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron *val = 0; 364ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron if (chan->channel == 0) 365ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron *val2 = 1220; 366ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron else 367ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron *val2 = 610; 368ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron return IIO_VAL_INT_PLUS_MICRO; 369ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron case IIO_TEMP: 370ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron *val = 0; 371ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron *val2 = -470000; 372ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron return IIO_VAL_INT_PLUS_MICRO; 373ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron case IIO_ACCEL: 374ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron *val = 0; 375ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron if (chan->channel == 'x') 376ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron *val2 = 17125; 377ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron else 378ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron *val2 = 8407; 379ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron return IIO_VAL_INT_PLUS_MICRO; 380ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron default: 381ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron return -EINVAL; 382ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron } 383ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron break; 384ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron case (1 << IIO_CHAN_INFO_OFFSET_SEPARATE): 385ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron *val = 25; 386ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron return IIO_VAL_INT; 387ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron case (1 << IIO_CHAN_INFO_CALIBBIAS_SEPARATE): 388ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron switch (chan->type) { 389ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron case IIO_ACCEL: 390ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron bits = 12; 391ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron break; 392ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron default: 393ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron return -EINVAL; 394ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron }; 395ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron mutex_lock(&indio_dev->mlock); 396ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron addr = adis16204_addresses[chan->address][1]; 397ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron ret = adis16204_spi_read_reg_16(indio_dev, addr, &val16); 398ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron if (ret) { 399ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron mutex_unlock(&indio_dev->mlock); 400ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron return ret; 401ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron } 402ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron val16 &= (1 << bits) - 1; 403ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron val16 = (s16)(val16 << (16 - bits)) >> (16 - bits); 404ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron *val = val16; 405ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron mutex_unlock(&indio_dev->mlock); 406ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron return IIO_VAL_INT; 407ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron } 408ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron return -EINVAL; 409ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron} 410ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron 411ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameronstatic int adis16204_write_raw(struct iio_dev *indio_dev, 412ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron struct iio_chan_spec const *chan, 413ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron int val, 414ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron int val2, 415ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron long mask) 416ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron{ 417ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron int bits; 418ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron s16 val16; 419ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron u8 addr; 420ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron switch (mask) { 421ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron case (1 << IIO_CHAN_INFO_CALIBBIAS_SEPARATE): 422ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron switch (chan->type) { 423ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron case IIO_ACCEL: 424ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron bits = 12; 425ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron break; 426ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron default: 427ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron return -EINVAL; 428ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron }; 429ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron val16 = val & ((1 << bits) - 1); 430ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron addr = adis16204_addresses[chan->address][1]; 431ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron return adis16204_spi_write_reg_16(indio_dev, addr, val16); 432ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron } 433ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron return -EINVAL; 434ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron} 435ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron 436ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameronstatic struct iio_chan_spec adis16204_channels[] = { 437ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron IIO_CHAN(IIO_IN, 0, 0, 0, "supply", 0, 0, 438ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron (1 << IIO_CHAN_INFO_SCALE_SEPARATE), 439ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron in_supply, ADIS16204_SCAN_SUPPLY, 440ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron IIO_ST('u', 12, 16, 0), 0), 441ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron IIO_CHAN(IIO_IN, 0, 1, 0, NULL, 1, 0, 442ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron (1 << IIO_CHAN_INFO_SCALE_SEPARATE), 443ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron in_aux, ADIS16204_SCAN_AUX_ADC, 444ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron IIO_ST('u', 12, 16, 0), 0), 445ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron IIO_CHAN(IIO_TEMP, 0, 1, 0, NULL, 0, 0, 446ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron (1 << IIO_CHAN_INFO_SCALE_SEPARATE) | 447ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron (1 << IIO_CHAN_INFO_OFFSET_SEPARATE), 448ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron temp, ADIS16204_SCAN_TEMP, 449ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron IIO_ST('u', 12, 16, 0), 0), 450ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron IIO_CHAN(IIO_ACCEL, 1, 0, 0, NULL, 0, IIO_MOD_X, 451ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron (1 << IIO_CHAN_INFO_SCALE_SEPARATE) | 452ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron (1 << IIO_CHAN_INFO_CALIBBIAS_SEPARATE), 453ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron accel_x, ADIS16204_SCAN_ACC_X, 454ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron IIO_ST('s', 14, 16, 0), 0), 455ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron IIO_CHAN(IIO_ACCEL, 1, 0, 0, NULL, 0, IIO_MOD_Y, 456ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron (1 << IIO_CHAN_INFO_SCALE_SEPARATE) | 457ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron (1 << IIO_CHAN_INFO_CALIBBIAS_SEPARATE), 458ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron accel_y, ADIS16204_SCAN_ACC_Y, 459ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron IIO_ST('s', 14, 16, 0), 0), 460ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron IIO_CHAN_SOFT_TIMESTAMP(5), 461ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron}; 462bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Songstatic struct attribute *adis16204_attributes[] = { 463bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song &iio_dev_attr_reset.dev_attr.attr, 464bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song &iio_dev_attr_accel_xy.dev_attr.attr, 465bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song &iio_dev_attr_accel_xpeak.dev_attr.attr, 466bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song &iio_dev_attr_accel_ypeak.dev_attr.attr, 467bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song &iio_dev_attr_accel_xypeak.dev_attr.attr, 468bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song &iio_const_attr_accel_xy_scale.dev_attr.attr, 469bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song NULL 470bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song}; 471bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song 472bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Songstatic const struct attribute_group adis16204_attribute_group = { 473bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song .attrs = adis16204_attributes, 474bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song}; 475bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song 4766fe8135fccd66aedcc55ded70824342587fd2499Jonathan Cameronstatic const struct iio_info adis16204_info = { 4776fe8135fccd66aedcc55ded70824342587fd2499Jonathan Cameron .attrs = &adis16204_attribute_group, 4786fe8135fccd66aedcc55ded70824342587fd2499Jonathan Cameron .read_raw = &adis16204_read_raw, 4796fe8135fccd66aedcc55ded70824342587fd2499Jonathan Cameron .write_raw = &adis16204_write_raw, 4806fe8135fccd66aedcc55ded70824342587fd2499Jonathan Cameron .driver_module = THIS_MODULE, 4816fe8135fccd66aedcc55ded70824342587fd2499Jonathan Cameron}; 4826fe8135fccd66aedcc55ded70824342587fd2499Jonathan Cameron 483bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Songstatic int __devinit adis16204_probe(struct spi_device *spi) 484bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song{ 485bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song int ret, regdone = 0; 4864de66bbb3eb1099a76e1bbf87e376c7a31dc30d5Jonathan Cameron struct adis16204_state *st; 4874de66bbb3eb1099a76e1bbf87e376c7a31dc30d5Jonathan Cameron struct iio_dev *indio_dev; 488bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song 4894de66bbb3eb1099a76e1bbf87e376c7a31dc30d5Jonathan Cameron /* setup the industrialio driver allocated elements */ 4904de66bbb3eb1099a76e1bbf87e376c7a31dc30d5Jonathan Cameron indio_dev = iio_allocate_device(sizeof(*st)); 4914de66bbb3eb1099a76e1bbf87e376c7a31dc30d5Jonathan Cameron if (indio_dev == NULL) { 492bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song ret = -ENOMEM; 4934de66bbb3eb1099a76e1bbf87e376c7a31dc30d5Jonathan Cameron goto error_ret; 494bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song } 4954de66bbb3eb1099a76e1bbf87e376c7a31dc30d5Jonathan Cameron st = iio_priv(indio_dev); 4964de66bbb3eb1099a76e1bbf87e376c7a31dc30d5Jonathan Cameron /* this is only used for removal purposes */ 4974de66bbb3eb1099a76e1bbf87e376c7a31dc30d5Jonathan Cameron spi_set_drvdata(spi, indio_dev); 498bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song st->us = spi; 499bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song mutex_init(&st->buf_lock); 500bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song 5014de66bbb3eb1099a76e1bbf87e376c7a31dc30d5Jonathan Cameron indio_dev->name = spi->dev.driver->name; 5024de66bbb3eb1099a76e1bbf87e376c7a31dc30d5Jonathan Cameron indio_dev->dev.parent = &spi->dev; 5034de66bbb3eb1099a76e1bbf87e376c7a31dc30d5Jonathan Cameron indio_dev->info = &adis16204_info; 5044de66bbb3eb1099a76e1bbf87e376c7a31dc30d5Jonathan Cameron indio_dev->channels = adis16204_channels; 5054de66bbb3eb1099a76e1bbf87e376c7a31dc30d5Jonathan Cameron indio_dev->num_channels = ARRAY_SIZE(adis16204_channels); 5064de66bbb3eb1099a76e1bbf87e376c7a31dc30d5Jonathan Cameron indio_dev->modes = INDIO_DIRECT_MODE; 507bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song 5084de66bbb3eb1099a76e1bbf87e376c7a31dc30d5Jonathan Cameron ret = adis16204_configure_ring(indio_dev); 509bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song if (ret) 510bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song goto error_free_dev; 511bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song 5124de66bbb3eb1099a76e1bbf87e376c7a31dc30d5Jonathan Cameron ret = iio_device_register(indio_dev); 513bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song if (ret) 514bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song goto error_unreg_ring_funcs; 515bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song regdone = 1; 516bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song 5174de66bbb3eb1099a76e1bbf87e376c7a31dc30d5Jonathan Cameron ret = iio_ring_buffer_register_ex(indio_dev->ring, 0, 518ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron adis16204_channels, 519ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan Cameron ARRAY_SIZE(adis16204_channels)); 520bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song if (ret) { 521bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song printk(KERN_ERR "failed to initialize the ring\n"); 522bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song goto error_unreg_ring_funcs; 523bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song } 524bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song 525bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song if (spi->irq) { 5264de66bbb3eb1099a76e1bbf87e376c7a31dc30d5Jonathan Cameron ret = adis16204_probe_trigger(indio_dev); 527bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song if (ret) 52837f9d2714a0a95cf7f45e0ed9422eea80490a77cJonathan Cameron goto error_uninitialize_ring; 529bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song } 530bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song 531bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song /* Get the device into a sane initial state */ 5324de66bbb3eb1099a76e1bbf87e376c7a31dc30d5Jonathan Cameron ret = adis16204_initial_setup(indio_dev); 533bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song if (ret) 534bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song goto error_remove_trigger; 535bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song return 0; 536bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song 537bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Songerror_remove_trigger: 5384de66bbb3eb1099a76e1bbf87e376c7a31dc30d5Jonathan Cameron adis16204_remove_trigger(indio_dev); 539bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Songerror_uninitialize_ring: 5404de66bbb3eb1099a76e1bbf87e376c7a31dc30d5Jonathan Cameron iio_ring_buffer_unregister(indio_dev->ring); 541bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Songerror_unreg_ring_funcs: 5424de66bbb3eb1099a76e1bbf87e376c7a31dc30d5Jonathan Cameron adis16204_unconfigure_ring(indio_dev); 543bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Songerror_free_dev: 544bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song if (regdone) 5454de66bbb3eb1099a76e1bbf87e376c7a31dc30d5Jonathan Cameron iio_device_unregister(indio_dev); 546bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song else 5474de66bbb3eb1099a76e1bbf87e376c7a31dc30d5Jonathan Cameron iio_free_device(indio_dev); 548bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Songerror_ret: 549bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song return ret; 550bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song} 551bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song 552bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Songstatic int adis16204_remove(struct spi_device *spi) 553bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song{ 5544de66bbb3eb1099a76e1bbf87e376c7a31dc30d5Jonathan Cameron struct iio_dev *indio_dev = spi_get_drvdata(spi); 555bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song 556bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song adis16204_remove_trigger(indio_dev); 5574de66bbb3eb1099a76e1bbf87e376c7a31dc30d5Jonathan Cameron iio_ring_buffer_unregister(indio_dev->ring); 558bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song iio_device_unregister(indio_dev); 559bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song adis16204_unconfigure_ring(indio_dev); 560bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song 561bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song return 0; 562bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song} 563bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song 564bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Songstatic struct spi_driver adis16204_driver = { 565bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song .driver = { 566bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song .name = "adis16204", 567bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song .owner = THIS_MODULE, 568bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song }, 569bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song .probe = adis16204_probe, 570bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song .remove = __devexit_p(adis16204_remove), 571bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song}; 572bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song 573bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Songstatic __init int adis16204_init(void) 574bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song{ 575bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song return spi_register_driver(&adis16204_driver); 576bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song} 577bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Songmodule_init(adis16204_init); 578bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song 579bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Songstatic __exit void adis16204_exit(void) 580bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song{ 581bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song spi_unregister_driver(&adis16204_driver); 582bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song} 583bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Songmodule_exit(adis16204_exit); 584bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry Song 585bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry SongMODULE_AUTHOR("Barry Song <21cnbao@gmail.com>"); 586ad3eb9ab1276cc69bb9cc72dffc074d1a317a6c0Jonathan CameronMODULE_DESCRIPTION("ADIS16204 High-g Digital Impact Sensor and Recorder"); 587bb6f19eafe3a1a5dd937ce66668e70aeaa1b0bf4Barry SongMODULE_LICENSE("GPL v2"); 588