diff options
author | Jiri Kosina <jkosina@suse.cz> | 2010-06-16 18:08:13 +0200 |
---|---|---|
committer | Jiri Kosina <jkosina@suse.cz> | 2010-06-16 18:08:13 +0200 |
commit | f1bbbb6912662b9f6070c5bfc4ca9eb1f06a9d5b (patch) | |
tree | c2c130a74be25b0b2dff992e1a195e2728bdaadd /drivers/staging/iio | |
parent | fd0961ff67727482bb20ca7e8ea97b83e9de2ddb (diff) | |
parent | 7e27d6e778cd87b6f2415515d7127eba53fe5d02 (diff) |
Merge branch 'master' into for-next
Diffstat (limited to 'drivers/staging/iio')
66 files changed, 10766 insertions, 1058 deletions
diff --git a/drivers/staging/iio/Documentation/iio_utils.h b/drivers/staging/iio/Documentation/iio_utils.h index 74d312473378..014f6684faba 100644 --- a/drivers/staging/iio/Documentation/iio_utils.h +++ b/drivers/staging/iio/Documentation/iio_utils.h @@ -7,111 +7,172 @@ * the Free Software Foundation. */ +/* Made up value to limit allocation sizes */ +#include <string.h> +#include <stdlib.h> + +#define IIO_MAX_NAME_LENGTH 30 + #define IIO_EVENT_CODE_RING_50_FULL 200 #define IIO_EVENT_CODE_RING_75_FULL 201 #define IIO_EVENT_CODE_RING_100_FULL 202 +const char *iio_dir = "/sys/bus/iio/devices/"; + struct iio_event_data { int id; __s64 timestamp; }; - -inline char *find_ring_subelement(const char *directory, const char *subelement) -{ - DIR *dp; - const struct dirent *ent; - int pos; - char temp[100]; - char *returnstring; - dp = opendir(directory); - if (dp == NULL) { - printf("could not directory: %s\n", directory); - return NULL; - } - while (ent = readdir(dp), ent != NULL) { - if (strcmp(ent->d_name, ".") != 0 && - strcmp(ent->d_name, "..") != 0) { - if (strncmp(ent->d_name, subelement, strlen(subelement)) == 0) { - int length = sprintf(temp, "%s%s%s", directory, ent->d_name, "/"); - returnstring = malloc(length+1); - strncpy(returnstring, temp, length+1); - return returnstring; - - } - } - } - return 0; -} - - -char *find_type_by_name(const char *name, const char *type) +/** + * find_type_by_name() - function to match top level types by name + * @name: top level type instance name + * @type: the type of top level instance being sort + * + * Typical types this is used for are device and trigger. + **/ +inline int find_type_by_name(const char *name, const char *type) { - const char *iio_dir = "/sys/class/iio/"; const struct dirent *ent; - int cnt, pos, pos2; + int number, numstrlen; FILE *nameFile; DIR *dp; - char thisname[100]; - char temp[100]; - - char *returnstring = NULL; + char thisname[IIO_MAX_NAME_LENGTH]; + char *filename; struct stat Stat; - pos = sprintf(temp, "%s", iio_dir); + dp = opendir(iio_dir); if (dp == NULL) { printf("No industrialio devices available"); - return NULL; + return -ENODEV; } + while (ent = readdir(dp), ent != NULL) { - cnt++; - /*reject . and .. */ if (strcmp(ent->d_name, ".") != 0 && - strcmp(ent->d_name, "..") != 0) { - /*make sure it isn't a trigger!*/ - if (strncmp(ent->d_name, type, strlen(type)) == 0) { - /* build full path to new file */ - pos2 = pos + sprintf(temp + pos, "%s/", ent->d_name); - sprintf(temp + pos2, "name"); - printf("search location %s\n", temp); - nameFile = fopen(temp, "r"); - if (!nameFile) { - sprintf(temp + pos2, "modalias", ent->d_name); - nameFile = fopen(temp, "r"); - if (!nameFile) { - printf("Failed to find a name for device\n"); - return NULL; - } - } + strcmp(ent->d_name, "..") != 0 && + strlen(ent->d_name) > strlen(type) && + strncmp(ent->d_name, type, strlen(type)) == 0) { + numstrlen = sscanf(ent->d_name + strlen(type), + "%d", + &number); + /* verify the next character is not a colon */ + if (strncmp(ent->d_name + strlen(type) + numstrlen, + ":", + 1) != 0) { + filename = malloc(strlen(iio_dir) + + strlen(type) + + numstrlen + + 6); + if (filename == NULL) + return -ENOMEM; + sprintf(filename, "%s%s%d/name", + iio_dir, + type, + number); + nameFile = fopen(filename, "r"); + if (!nameFile) + continue; + free(filename); fscanf(nameFile, "%s", thisname); - if (strcmp(name, thisname) == 0) { - returnstring = malloc(strlen(temp) + 1); - sprintf(temp + pos2, ""); - strcpy(returnstring, temp); - return returnstring; - } + if (strcmp(name, thisname) == 0) + return number; fclose(nameFile); - } } } + return -ENODEV; } -int write_sysfs_int(char *filename, char *basedir, int val) +inline int _write_sysfs_int(char *filename, char *basedir, int val, int verify) { int ret; - FILE *sysfsfp; - char temp[100]; - sprintf(temp, "%s%s", basedir, filename); + FILE *sysfsfp; + int test; + char *temp = malloc(strlen(basedir) + strlen(filename) + 2); + if (temp == NULL) + return -ENOMEM; + sprintf(temp, "%s/%s", basedir, filename); sysfsfp = fopen(temp, "w"); - if (sysfsfp == NULL) - return -1; + if (sysfsfp == NULL) { + printf("failed to open %s\n", temp); + ret = -errno; + goto error_free; + } fprintf(sysfsfp, "%d", val); fclose(sysfsfp); - return 0; + if (verify) { + sysfsfp = fopen(temp, "r"); + if (sysfsfp == NULL) { + printf("failed to open %s\n", temp); + ret = -errno; + goto error_free; + } + fscanf(sysfsfp, "%d", &test); + if (test != val) { + printf("Possible failure in int write %d to %s%s\n", + val, + basedir, + filename); + ret = -1; + } + } +error_free: + free(temp); + return ret; } +int write_sysfs_int(char *filename, char *basedir, int val) +{ + return _write_sysfs_int(filename, basedir, val, 0); +} + +int write_sysfs_int_and_verify(char *filename, char *basedir, int val) +{ + return _write_sysfs_int(filename, basedir, val, 1); +} + +int _write_sysfs_string(char *filename, char *basedir, char *val, int verify) +{ + int ret; + FILE *sysfsfp; + char *temp = malloc(strlen(basedir) + strlen(filename) + 2); + if (temp == NULL) { + printf("Memory allocation failed\n"); + return -ENOMEM; + } + sprintf(temp, "%s/%s", basedir, filename); + sysfsfp = fopen(temp, "w"); + if (sysfsfp == NULL) { + printf("Could not open %s\n", temp); + ret = -errno; + goto error_free; + } + fprintf(sysfsfp, "%s", val); + fclose(sysfsfp); + if (verify) { + sysfsfp = fopen(temp, "r"); + if (sysfsfp == NULL) { + ret = -errno; + goto error_free; + } + fscanf(sysfsfp, "%s", temp); + if (strcmp(temp, val) != 0) { + printf("Possible failure in string write of %s " + "Should be %s " + "writen to %s\%s\n", + temp, + val, + basedir, + filename); + ret = -1; + } + } +error_free: + free(temp); + + return ret; +} /** * write_sysfs_string_and_verify() - string write, readback and verify * @filename: name of file to write to @@ -120,40 +181,54 @@ int write_sysfs_int(char *filename, char *basedir, int val) **/ int write_sysfs_string_and_verify(char *filename, char *basedir, char *val) { - int ret; - FILE *sysfsfp; - char temp[100]; - sprintf(temp, "%s%s", basedir, filename); - sysfsfp = fopen(temp, "w"); - if (sysfsfp == NULL) - return -1; - fprintf(sysfsfp, "%s", val); - fclose(sysfsfp); + return _write_sysfs_string(filename, basedir, val, 1); +} - sysfsfp = fopen(temp, "r"); - if (sysfsfp == NULL) - return -1; - fscanf(sysfsfp, "%s", temp); - if (strcmp(temp, val) != 0) { - printf("Possible failure in string write %s to %s%s \n", - val, - basedir, - filename); - return -1; - } - return 0; +int write_sysfs_string(char *filename, char *basedir, char *val) +{ + return _write_sysfs_string(filename, basedir, val, 0); } int read_sysfs_posint(char *filename, char *basedir) { int ret; FILE *sysfsfp; - char temp[100]; - sprintf(temp, "%s%s", basedir, filename); + char *temp = malloc(strlen(basedir) + strlen(filename) + 2); + if (temp == NULL) { + printf("Memory allocation failed"); + return -ENOMEM; + } + sprintf(temp, "%s/%s", basedir, filename); sysfsfp = fopen(temp, "r"); - if (sysfsfp == NULL) - return -1; + if (sysfsfp == NULL) { + ret = -errno; + goto error_free; + } fscanf(sysfsfp, "%d\n", &ret); fclose(sysfsfp); +error_free: + free(temp); + return ret; +} + +int read_sysfs_float(char *filename, char *basedir, float *val) +{ + float ret = 0; + FILE *sysfsfp; + char *temp = malloc(strlen(basedir) + strlen(filename) + 2); + if (temp == NULL) { + printf("Memory allocation failed"); + return -ENOMEM; + } + sprintf(temp, "%s/%s", basedir, filename); + sysfsfp = fopen(temp, "r"); + if (sysfsfp == NULL) { + ret = -errno; + goto error_free; + } + fscanf(sysfsfp, "%f\n", val); + fclose(sysfsfp); +error_free: + free(temp); return ret; } diff --git a/drivers/staging/iio/Documentation/lis3l02dqbuffersimple.c b/drivers/staging/iio/Documentation/lis3l02dqbuffersimple.c index 2b5cfc58d78d..3a580284020c 100644 --- a/drivers/staging/iio/Documentation/lis3l02dqbuffersimple.c +++ b/drivers/staging/iio/Documentation/lis3l02dqbuffersimple.c @@ -1,4 +1,4 @@ -/* Industrialio test ring buffer with a lis3l02dq acceleromter +/* Industrialio ring buffer with a lis3l02dq accelerometer * * Copyright (c) 2008 Jonathan Cameron * @@ -6,126 +6,181 @@ * under the terms of the GNU General Public License version 2 as published by * the Free Software Foundation. * - * Assumes suitable udev rules are used to create the dev nodes as named here. + * This program is primarily intended as an example application. */ #include <dirent.h> #include <fcntl.h> #include <stdio.h> #include <errno.h> -#include <stdint.h> -#include <sys/types.h> #include <sys/stat.h> #include <sys/dir.h> - #include <linux/types.h> -#include <dirent.h> -#include "iio_util.h" +#include "iio_utils.h" -static const char *ring_access = "/dev/iio/lis3l02dq_ring_access"; -static const char *ring_event = "/dev/iio/lis3l02dq_ring_event"; -static const char *device_name = "lis3l02dq"; -static const char *trigger_name = "lis3l02dq-dev0"; -static int NumVals = 3; -static int scan_ts = 1; -static int RingLength = 128; +const char *device_name = "lis3l02dq"; +const char *trigger_name_base = "lis3l02dq-dev"; +const int num_vals = 3; +const int scan_ts = 1; +const int buf_len = 128; +const int num_loops = 10; /* * Could get this from ring bps, but only after starting the ring - * which is a bit late for it to be useful + * which is a bit late for it to be useful. + * + * Todo: replace with much more generic version based on scan_elements + * directory. */ -int size_from_scanmode(int numVals, int timestamp) +int size_from_scanmode(int num_vals, int timestamp) { - if (numVals && timestamp) + if (num_vals && timestamp) return 16; else if (timestamp) return 8; else - return numVals*2; + return num_vals*2; } int main(int argc, char **argv) { + int ret; int i, j, k, toread; FILE *fp_ev; int fp; + + char *trigger_name, *dev_dir_name, *buf_dir_name; char *data; size_t read_size; struct iio_event_data dat; + int dev_num, trig_num; - char *BaseDirectoryName, - *TriggerDirectoryName, - *RingBufferDirectoryName; + char *buffer_access, *buffer_event; + const char *iio_dir = "/sys/bus/iio/devices/"; + int scan_size; + float gain = 1; - BaseDirectoryName = find_type_by_name(device_name, "device"); - if (BaseDirectoryName == NULL) { - printf("Failed to find the %s \n", device_name); - return -1; + + /* Find out which iio device is the accelerometer. */ + dev_num = find_type_by_name(device_name, "device"); + if (dev_num < 0) { + printf("Failed to find the %s\n", device_name); + ret = -ENODEV; + goto error_ret; + } + printf("iio device number being used is %d\n", dev_num); + asprintf(&dev_dir_name, "%sdevice%d", iio_dir, dev_num); + + /* + * Build the trigger name. + * In this case we want the lis3l02dq's data ready trigger + * for this lis3l02dq. The naming is lis3l02dq_dev[n], where + * n matches the device number found above. + */ + ret = asprintf(&trigger_name, "%s%d", trigger_name_base, dev_num); + if (ret < 0) { + ret = -ENOMEM; + goto error_free_dev_dir_name; } - TriggerDirectoryName = find_type_by_name(trigger_name, "trigger"); - if (TriggerDirectoryName == NULL) { + + /* + * Find the trigger by name. + * This is techically unecessary here as we only need to + * refer to the trigger by name and that name is already + * known. + */ + trig_num = find_type_by_name(trigger_name, "trigger"); + if (trig_num < 0) { printf("Failed to find the %s\n", trigger_name); - return -1; + ret = -ENODEV; + goto error_free_triggername; } - RingBufferDirectoryName = find_ring_subelement(BaseDirectoryName, - "ring_buffer"); - if (RingBufferDirectoryName == NULL) { - printf("Failed to find ring buffer\n"); - return -1; + printf("iio trigger number being used is %d\n", trig_num); + + /* + * Read in the scale value - in a more generic case, first + * check for accel_scale, then the indivual channel scales + */ + ret = read_sysfs_float("accel_scale", dev_dir_name, &gain); + if (ret) + goto error_free_triggername;; + + /* + * Construct the directory name for the associated buffer. + * As we know that the lis3l02dq has only one buffer this may + * be built rather than found. + */ + ret = asprintf(&buf_dir_name, "%sdevice%d:buffer0", iio_dir, dev_num); + if (ret < 0) { + ret = -ENOMEM; + goto error_free_triggername; } - - if (write_sysfs_string_and_verify("trigger/current_trigger", - BaseDirectoryName, - (char *)trigger_name) < 0) { - printf("Failed to write current_trigger file \n"); - return -1; + /* Set the device trigger to be the data rdy trigger found above */ + ret = write_sysfs_string_and_verify("trigger/current_trigger", + dev_dir_name, + trigger_name); + if (ret < 0) { + printf("Failed to write current_trigger file\n"); + goto error_free_buf_dir_name; } /* Setup ring buffer parameters */ - if (write_sysfs_int("length", RingBufferDirectoryName, - RingLength) < 0) { - printf("Failed to open the ring buffer length file \n"); - return -1; - } + ret = write_sysfs_int("length", buf_dir_name, buf_len); + if (ret < 0) + goto error_free_buf_dir_name; - /* Enable the ring buffer */ - if (write_sysfs_int("ring_enable", RingBufferDirectoryName, 1) < 0) { - printf("Failed to open the ring buffer control file \n"); - return -1; - }; + /* Enable the buffer */ + ret = write_sysfs_int("ring_enable", buf_dir_name, 1); + if (ret < 0) + goto error_free_buf_dir_name; - data = malloc(size_from_scanmode(NumVals, scan_ts)*RingLength); + data = malloc(size_from_scanmode(num_vals, scan_ts)*buf_len); if (!data) { - printf("Could not allocate space for usespace data store\n"); - return -1; + ret = -ENOMEM; + goto error_free_buf_dir_name; + } + + ret = asprintf(&buffer_access, + "/dev/device%d:buffer0:access0", + dev_num); + if (ret < 0) { + ret = -ENOMEM; + goto error_free_data; } + ret = asprintf(&buffer_event, "/dev/device%d:buffer0:event0", dev_num); + if (ret < 0) { + ret = -ENOMEM; + goto error_free_data; + } /* Attempt to open non blocking the access dev */ - fp = open(ring_access, O_RDONLY | O_NONBLOCK); + fp = open(buffer_access, O_RDONLY | O_NONBLOCK); if (fp == -1) { /*If it isn't there make the node */ - printf("Failed to open %s\n", ring_access); - return -1; + printf("Failed to open %s\n", buffer_access); + ret = -errno; + goto error_free_buffer_event; } /* Attempt to open the event access dev (blocking this time) */ - fp_ev = fopen(ring_event, "rb"); + fp_ev = fopen(buffer_event, "rb"); if (fp_ev == NULL) { - printf("Failed to open %s\n", ring_event); - return -1; + printf("Failed to open %s\n", buffer_event); + ret = -errno; + goto error_close_buffer_access; } /* Wait for events 10 times */ - for (j = 0; j < 10; j++) { + for (j = 0; j < num_loops; j++) { read_size = fread(&dat, 1, sizeof(struct iio_event_data), fp_ev); switch (dat.id) { case IIO_EVENT_CODE_RING_100_FULL: - toread = RingLength; + toread = buf_len; break; case IIO_EVENT_CODE_RING_75_FULL: - toread = RingLength*3/4; + toread = buf_len*3/4; break; case IIO_EVENT_CODE_RING_50_FULL: - toread = RingLength/2; + toread = buf_len/2; break; default: printf("Unexpecteded event code\n"); @@ -133,39 +188,51 @@ int main(int argc, char **argv) } read_size = read(fp, data, - toread*size_from_scanmode(NumVals, scan_ts)); + toread*size_from_scanmode(num_vals, scan_ts)); if (read_size == -EAGAIN) { - printf("nothing available \n"); + printf("nothing available\n"); continue; } - - for (i = 0; - i < read_size/size_from_scanmode(NumVals, scan_ts); - i++) { - for (k = 0; k < NumVals; k++) { - __s16 val = *(__s16 *)(&data[i*size_from_scanmode(NumVals, scan_ts) + scan_size = size_from_scanmode(num_vals, scan_ts); + for (i = 0; i < read_size/scan_size; i++) { + for (k = 0; k < num_vals; k++) { + __s16 val = *(__s16 *)(&data[i*scan_size + (k)*2]); - printf("%05d ", val); + printf("%05f ", (float)val*gain); } printf(" %lld\n", - *(__s64 *)(&data[(i+1)*size_from_scanmode(NumVals, scan_ts) + *(__s64 *)(&data[(i + 1) + *size_from_scanmode(num_vals, + scan_ts) - sizeof(__s64)])); } } /* Stop the ring buffer */ - if (write_sysfs_int("ring_enable", RingBufferDirectoryName, 0) < 0) { - printf("Failed to open the ring buffer control file \n"); - return -1; - }; - - /* Disconnect from the trigger - writing something that doesn't exist.*/ - write_sysfs_string_and_verify("trigger/current_trigger", - BaseDirectoryName, "NULL"); - free(BaseDirectoryName); - free(TriggerDirectoryName); - free(RingBufferDirectoryName); + ret = write_sysfs_int("ring_enable", buf_dir_name, 0); + if (ret < 0) + goto error_close_buffer_event; + + /* Disconnect from the trigger - just write a dummy name.*/ + write_sysfs_string("trigger/current_trigger", + dev_dir_name, "NULL"); + +error_close_buffer_event: + fclose(fp_ev); +error_close_buffer_access: + close(fp); +error_free_data: free(data); - - return 0; +error_free_buffer_access: + free(buffer_access); +error_free_buffer_event: + free(buffer_event); +error_free_buf_dir_name: + free(buf_dir_name); +error_free_triggername: + free(trigger_name); +error_free_dev_dir_name: + free(dev_dir_name); +error_ret: + return ret; } diff --git a/drivers/staging/iio/Documentation/sysfs-class-iio b/drivers/staging/iio/Documentation/sysfs-class-iio new file mode 100644 index 000000000000..714b4c57c82a --- /dev/null +++ b/drivers/staging/iio/Documentation/sysfs-class-iio @@ -0,0 +1,294 @@ + +What: /sys/bus/iio/devices/device[n] +KernelVersion: 2.6.35 +Contact: linux-iio@vger.kernel.org +Description: + Hardware chip or device accessed by on communication port. + Corresponds to a grouping of sensor channels. + +What: /sys/bus/iio/devices/trigger[n] +KernelVersion: 2.6.35 +Contact: linux-iio@vger.kernel.org +Description: + An event driven driver of data capture to an in kernel buffer. + May be provided by a device driver that also has an IIO device + based on hardware generated events (e.g. data ready) or + provided by a separate driver for other hardware (e.g. + periodic timer, gpio or high resolution timer). + Contains trigger type specific elements. These do not + generalize well and hence are not documented in this file. + +What: /sys/bus/iio/devices/device[n]:buffer +KernelVersion: 2.6.35 +Contact: linux-iio@vger.kernel.org +Description: + Link to /sys/class/iio/device[n]/device[n]:buffer. n indicates the + device with which this buffer buffer is associated. + +What: /sys/.../device[n]/name +KernelVersion: 2.6.35 +Contact: linux-iio@vger.kernel.org +Description: + Description of the physical chip / device. Typically a part + number. + +What: /sys/.../device[n]/sampling_frequency +KernelVersion: 2.6.35 +Contact: linux-iio@vger.kernel.org +Description: + Some devices have internal clocks. This parameter sets the + resulting sampling frequency. In many devices this + parameter has an effect on input filters etc rather than + simply controlling when the input is sampled. As this + effects datardy triggers, hardware buffers and the sysfs + direct access interfaces, it may be found in any of the + relevant directories. If it effects all of the above + then it is to be found in the base device directory as here. + +What: /sys/.../device[n]/sampling_frequency_available +KernelVersion: 2.6.35 +Contact: linux-iio@vger.kernel.org +Description: + When the internal sampling clock can only take a small + discrete set of values, this file lists those availale. + +What: /sys/.../device[n]/in[_name][m]_raw +KernelVersion: 2.6.35 +Contact: linux-iio@vger.kernel.org +Description: + Raw (unscaled no bias removal etc) voltage measurement from + channel m. name is used in special cases where this does + not correspond to externally available input (e.g. supply + voltage monitoring in which case the file is in_supply_raw). + +What: /sys/.../device[n]/in[_name][m]_offset +KernelVersion: 2.6.35 +Contact: linux-iio@vger.kernel.org +Description: + If known for a device, offset to be added to in[m]_raw prior + to scaling by in[_name][m]_scale in order to obtain voltage in + millivolts. Not present if the offset is always 0 or unknown. + If m is not present, then voltage offset applies to all in + channels. May be writable if a variable offset is controlled + by the device. Note that this is different to calibbias which + is for devices that apply offsets to compensate for variation + between different instances of the part, typically adjusted by + using some hardware supported calibration procedure. + +What: /sys/.../device[n]/in[_name][m]_offset_available +KernelVersion: 2.6.35 +Contact: linux-iio@vger.kernel.org +Description: + If a small number of discrete offset values are available, this + will be a space separated list. If these are independant (but + options the same) for individual offsets then m should not be + present. + +What: /sys/.../device[n]/in[_name][m]_offset_[min|max] +KernelVersion: 2.6.35 +Contact: linux-iio@vger.kernel.org +Description: + If a more or less continuous range of voltage offsets are supported + then these specify the minimum and maximum. If shared by all + in channels then m is not present. + +What: /sys/.../device[n]/in[_name][m]_calibbias +KernelVersion: 2.6.35 +Contact: linux-iio@vger.kernel.org +Description: + Hardware applied calibration offset. (assumed to fix production + inaccuracies) + +What /sys/.../device[n]/in[_name][m]_calibscale +KernelVersion: 2.6.35 +Contact: linux-iio@vger.kernel.org +Description: + Hardware applied calibration scale factor. (assumed to fix production + inaccuracies) + +What: /sys/.../device[n]/in[_name][m]_scale +KernelVersion: 2.6.35 +Contact: linux-iio@vger.kernel.org +Description: + If known for a device, scale to be applied to volt[m]_raw post + addition of in[_name][m]_offset in order to obtain the measured + voltage in millivolts. If shared across all in channels then m is not present. + +What: /sys/.../device[n]/in[m]-in[o]_raw +KernelVersion: 2.6.35 +Contact: linux-iio@vger.kernel.org +Description: + Raw (unscaled) differential voltage measurement equivalent to + channel m - channel o where these channel numbers apply to the physically + equivalent inputs when non differential readings are separately available. + In differential only parts, then all that is required is a consistent + labelling. + +What: /sys/.../device[n]/accel[_x|_y|_z][m]_raw +KernelVersion: 2.6.35 +Contact: linux-iio@vger.kernel.org +Description: + Acceleration in direction x, y or z (may be arbitrarily assigned + but should match other such assignments on device) + channel m (not present if only one accelerometer channel at + this orientation). Has all of the equivalent parameters as per in[m]. + Units after application of scale and offset are m/s^2. + +What: /sys/.../device[n]/gyro[_x|_y|_z][m]_raw +KernelVersion: 2.6.35 +Contact: linux-iio@vger.kernel.org +Description: + Angular velocity about axis x, y or z (may be arbitrarily assigned) + channel m (not present if only one gyroscope at this orientation). + Data converted by application of offset then scale to + radians per second. Has all the equivalent parameters as per in[m]. + +What: /sys/.../device[n]/incli[_x|_y|_z][m]_raw +KernelVersion: 2.6.35 +Contact: linux-iio@vger.kernel.org +Description: + Inclination raw reading about axis x, y or z (may be arbitarily + assigned) channel m (not present if only one inclinometer at + this orientation). Data converted by application of offset + and scale to Degrees. + +What: /sys/.../device[n]/magn[_x|_y|_z][m]_raw +KernelVersion: 2.6.35 +Contact: linux-iio@vger.kernel.org +Description: + Magnetic field along axis x, y or z (may be arbitrarily assigned) + channel m (not present if only one magnetometer at this orientation). + Data converted by application of offset then scale to Gauss + Has all the equivalent modifiers as per in[m]. + +What: /sys/.../device[n]/device[n]:event[m] +KernelVersion: 2.6.35 +Contact: linux-iio@vger.kernel.org +Description: + Configuration of which hardware generated events are passed up to + userspace. Some of these are a bit complex to generalize so this + section is a work in progress. + +What: /sys/.../device[n]:event[m]/dev +KernelVersion: 2.6.35 +Contact: linux-iio@vger.kernel.org +Description: + major:minor character device numbers for the event line. + +Taking accel_x0 as an example + +What: /sys/.../device[n]:event[m]/accel_x0_thresh[_high|_low]_en +KernelVersion: 2.6.35 +Contact: linux-iio@vger.kernel.org +Description: + Event generated when accel_x0 passes a threshold in correction direction + (or stays beyond one). If direction isn't specified, either triggers it. + Note driver will assume last p events requested are enabled where p is + however many it supports. So if you want to be sure you have + set what you think you have, check the contents of these. Drivers + may have to buffer any parameters so that they are consistent when a + given event type is enabled a future point (and not those for whatever + alarm was previously enabled). + +What: /sys/.../device[n]:event[m]/accel_x0_roc[_high|_low]_en +KernelVersion: 2.6.35 +Contact: linux-iio@vger.kernel.org +Description: + Same as above but based on the first differential of the value. + + +What: /sys/.../device[n]:event[m]/accel_x0[_thresh|_roc][_high|_low]_period +KernelVersion: 2.6.35 +Contact: linux-iio@vger.kernel.org +Description: + A period of time (microsecs) for which the condition must be broken + before an interrupt is triggered. Applies to all alarms if type is not + specified. + +What: /sys/.../device[n]:event[m]/accel_x0[_thresh|_roc][_high|_low]_value +KernelVersion: 2.6.35 +Contact: linux-iio@vger.kernel.org +Description: + The actual value of the threshold in raw device units obtained by + reverse application of scale and offfset to the acceleration in m/s^2. + +What: /sys/.../device[n]/scan_elements +KernelVersion: 2.6.35 +Contact: linux-iio@vger.kernel.org +Description: + Directory containing interfaces for elements that will be captured + for a single triggered sample set in the buffer. + +What: /sys/.../device[n]/scan_elements/[m]_accel_x0_en +KernelVersion: 2.6.35 +Contact: linux-iio@vger.kernel.org +Description: + Scan element control for triggered data capture. m implies the + ordering within the buffer. Next the type is specified with + modifier and channel number as per the sysfs single channel + access above. + +What: /sys/.../device[n]/scan_elements/accel[_x0]_precision +KernelVersion: 2.6.35 +Contact: linux-iio@vger.kernel.org +Description: + Scan element precision within the buffer. Note that the + data alignment must restrictions must be read from within + buffer to work out full data alignment for data read + via buffer_access chrdev. _x0 dropped if shared across all + acceleration channels. + +What: /sys/.../device[n]/scan_elements/accel[_x0]_shift +KernelVersion: 2.6.35 +Contact: linux-iio@vger.kernel.org +Description: + A bit shift (to right) that must be applied prior to + extracting the bits specified by accel[_x0]_precision. + +What: /sys/.../device[n]/device[n]:buffer:event/dev +KernelVersion: 2.6.35 +Contact: linux-iio@vger.kernel.org +Description: + Buffer for device n event character device major:minor numbers. + +What: /sys/.../device[n]/device[n]:buffer:access/dev +KernelVersion: 2.6.35 +Contact: linux-iio@vger.kernel.org +Description: + Buffer for device n access character device o major:minor numbers. + +What: /sys/.../device[n]:buffer/trigger +KernelVersion: 2.6.35 +Contact: linux-iio@vger.kernel.org +Description: + The name of the trigger source being used, as per string given + in /sys/class/iio/trigger[n]/name. + +What: /sys/.../device[n]:buffer/length +KernelVersion: 2.6.35 +Contact: linux-iio@vger.kernel.org +Description: + Number of scans contained by the buffer. + +What: /sys/.../device[n]:buffer/bps +KernelVersion: 2.6.35 +Contact: linux-iio@vger.kernel.org +Description: + Bytes per scan. Due to alignment fun, the scan may be larger + than implied directly by the scan_element parameters. + +What: /sys/.../device[n]:buffer/enable +KernelVersion: 2.6.35 +Contact: linux-iio@vger.kernel.org +Description: + Actually start the buffer capture up. Will start trigger + if first device and appropriate. + +What: /sys/.../device[n]:buffer/alignment +KernelVersion: 2.6.35 +Contact: linux-iio@vger.kernel.org +Description: + Minimum data alignment. Scan elements larger than this are aligned + to the nearest power of 2 times this. (may not be true in weird + hardware buffers that pack data well) + diff --git a/drivers/staging/iio/Documentation/userspace.txt b/drivers/staging/iio/Documentation/userspace.txt index 661015a0b866..4838818f65ef 100644 --- a/drivers/staging/iio/Documentation/userspace.txt +++ b/drivers/staging/iio/Documentation/userspace.txt @@ -56,5 +56,5 @@ KERNEL="ring_event_line*", ID="spi1.0", DRIVER="lis3l02dq", NAME="iio/lis3l02dq_ KERNEL="event_line*", ID="spi1.0", DRIVER="lis3l02dq", NAME="iio/lis3l02dq_event" KERNEL="ring_access*", ID="spi1.0", DRIVER="lis3l02dq", NAME="iio/lis3l02dq_ring_access" -The files, lis3l02dqbuffersimple.c and iio_util.h in this directory provide an example +The files, lis3l02dqbuffersimple.c and iio_utils.h in this directory provide an example of how to use the ring buffer and event interfaces. diff --git a/drivers/staging/iio/Kconfig b/drivers/staging/iio/Kconfig index ace99f6d1166..b0e62449c621 100644 --- a/drivers/staging/iio/Kconfig +++ b/drivers/staging/iio/Kconfig @@ -41,6 +41,8 @@ config IIO_TRIGGER source "drivers/staging/iio/accel/Kconfig" source "drivers/staging/iio/adc/Kconfig" +source "drivers/staging/iio/gyro/Kconfig" +source "drivers/staging/iio/imu/Kconfig" source "drivers/staging/iio/light/Kconfig" source "drivers/staging/iio/trigger/Kconfig" diff --git a/drivers/staging/iio/Makefile b/drivers/staging/iio/Makefile index 7ec021810a77..3502b39f0847 100644 --- a/drivers/staging/iio/Makefile +++ b/drivers/staging/iio/Makefile @@ -11,6 +11,8 @@ obj-$(CONFIG_IIO_SW_RING) += ring_sw.o obj-y += accel/ obj-y += adc/ +obj-y += gyro/ +obj-y += imu/ obj-y += light/ obj-y += trigger/
\ No newline at end of file diff --git a/drivers/staging/iio/accel/Kconfig b/drivers/staging/iio/accel/Kconfig index 3d3c3339dbc7..b4e57d1bc87d 100644 --- a/drivers/staging/iio/accel/Kconfig +++ b/drivers/staging/iio/accel/Kconfig @@ -3,6 +3,31 @@ # comment "Accelerometers" +config ADIS16209 + tristate "Analog Devices ADIS16209 Dual-Axis Digital Inclinometer and Accelerometer" + depends on SPI + select IIO_TRIGGER if IIO_RING_BUFFER + select IIO_SW_RING if IIO_RING_BUFFER + help + Say yes here to build support for Analog Devices adis16209 dual-axis digital inclinometer + and accelerometer. + +config ADIS16220 + tristate "Analog Devices ADIS16220 Programmable Digital Vibration Sensor driver" + depends on SPI + help + Say yes here to build support for Analog Devices adis16220 programmable + digital vibration sensor. + +config ADIS16240 + tristate "Analog Devices ADIS16240 Programmable Impact Sensor and Recorder" + depends on SPI + select IIO_TRIGGER if IIO_RING_BUFFER + select IIO_SW_RING if IIO_RING_BUFFER + help + Say yes here to build support for Analog Devices adis16240 programmable + impact Sensor and recorder. + config KXSD9 tristate "Kionix KXSD9 Accelerometer Driver" depends on SPI diff --git a/drivers/staging/iio/accel/Makefile b/drivers/staging/iio/accel/Makefile index d5335f9094ad..c34b13634c2d 100644 --- a/drivers/staging/iio/accel/Makefile +++ b/drivers/staging/iio/accel/Makefile @@ -1,6 +1,17 @@ # # Makefile for industrial I/O accelerometer drivers # +adis16209-y := adis16209_core.o +adis16209-$(CONFIG_IIO_RING_BUFFER) += adis16209_ring.o adis16209_trigger.o +obj-$(CONFIG_ADIS16209) += adis16209.o + +adis16220-y := adis16220_core.o +obj-$(CONFIG_ADIS16220) += adis16220.o + +adis16240-y := adis16240_core.o +adis16240-$(CONFIG_IIO_RING_BUFFER) += adis16240_ring.o adis16240_trigger.o +obj-$(CONFIG_ADIS16240) += adis16240.o + obj-$(CONFIG_KXSD9) += kxsd9.o lis3l02dq-y := lis3l02dq_core.o diff --git a/drivers/staging/iio/accel/accel.h b/drivers/staging/iio/accel/accel.h index d7fc7f98348e..1b6e37f76200 100644 --- a/drivers/staging/iio/accel/accel.h +++ b/drivers/staging/iio/accel/accel.h @@ -2,6 +2,8 @@ #include "../sysfs.h" /* Accelerometer types of attribute */ +#define IIO_DEV_ATTR_ACCEL_OFFSET(_mode, _show, _store, _addr) \ + IIO_DEVICE_ATTR(accel_offset, _mode, _show, _store, _addr) #define IIO_DEV_ATTR_ACCEL_X_OFFSET(_mode, _show, _store, _addr) \ IIO_DEVICE_ATTR(accel_x_offset, _mode, _show, _store, _addr) @@ -21,14 +23,17 @@ #define IIO_DEV_ATTR_ACCEL_Z_GAIN(_mode, _show, _store, _addr) \ IIO_DEVICE_ATTR(accel_z_gain, _mode, _show, _store, _addr) +#define IIO_DEV_ATTR_ACCEL(_show, _addr) \ + IIO_DEVICE_ATTR(accel_raw, S_IRUGO, _show, NULL, _addr) + #define IIO_DEV_ATTR_ACCEL_X(_show, _addr) \ - IIO_DEVICE_ATTR(accel_x, S_IRUGO, _show, NULL, _addr) + IIO_DEVICE_ATTR(accel_x_raw, S_IRUGO, _show, NULL, _addr) #define IIO_DEV_ATTR_ACCEL_Y(_show, _addr) \ - IIO_DEVICE_ATTR(accel_y, S_IRUGO, _show, NULL, _addr) + IIO_DEVICE_ATTR(accel_y_raw, S_IRUGO, _show, NULL, _addr) #define IIO_DEV_ATTR_ACCEL_Z(_show, _addr) \ - IIO_DEVICE_ATTR(accel_z, S_IRUGO, _show, NULL, _addr) + IIO_DEVICE_ATTR(accel_z_raw, S_IRUGO, _show, NULL, _addr) /* Thresholds are somewhat chip dependent - may need quite a few defs here */ /* For unified thresholds (shared across all directions */ @@ -61,7 +66,6 @@ #define IIO_DEV_ATTR_ACCEL_THRESH_Z(_mode, _show, _store, _addr) \ IIO_DEVICE_ATTR(thresh_accel_z, _mode, _show, _store, _addr) - /** * IIO_EVENT_ATTR_ACCEL_X_HIGH: threshold event, x acceleration * @_show: read x acceleration high threshold diff --git a/drivers/staging/iio/accel/adis16209.h b/drivers/staging/iio/accel/adis16209.h new file mode 100644 index 000000000000..877fd2a48380 --- /dev/null +++ b/drivers/staging/iio/accel/adis16209.h @@ -0,0 +1,193 @@ +#ifndef SPI_ADIS16209_H_ +#define SPI_ADIS16209_H_ + +#define ADIS16209_STARTUP_DELAY 220 /* ms */ + +#define ADIS16209_READ_REG(a) a +#define ADIS16209_WRITE_REG(a) ((a) | 0x80) + +/* Flash memory write count */ +#define ADIS16209_FLASH_CNT 0x00 +/* Output, power supply */ +#define ADIS16209_SUPPLY_OUT 0x02 +/* Output, x-axis accelerometer */ +#define ADIS16209_XACCL_OUT 0x04 +/* Output, y-axis accelerometer */ +#define ADIS16209_YACCL_OUT 0x06 +/* Output, auxiliary ADC input */ +#define ADIS16209_AUX_ADC 0x08 +/* Output, temperature */ +#define ADIS16209_TEMP_OUT 0x0A +/* Output, x-axis inclination */ +#define ADIS16209_XINCL_OUT 0x0C +/* Output, y-axis inclination */ +#define ADIS16209_YINCL_OUT 0x0E +/* Output, +/-180 vertical rotational position */ +#define ADIS16209_ROT_OUT 0x10 +/* Calibration, x-axis acceleration offset null */ +#define ADIS16209_XACCL_NULL 0x12 +/* Calibration, y-axis acceleration offset null */ +#define ADIS16209_YACCL_NULL 0x14 +/* Calibration, x-axis inclination offset null */ +#define ADIS16209_XINCL_NULL 0x16 +/* Calibration, y-axis inclination offset null */ +#define ADIS16209_YINCL_NULL 0x18 +/* Calibration, vertical rotation offset null */ +#define ADIS16209_ROT_NULL 0x1A +/* Alarm 1 amplitude threshold */ +#define ADIS16209_ALM_MAG1 0x20 +/* Alarm 2 amplitude threshold */ +#define ADIS16209_ALM_MAG2 0x22 +/* Alarm 1, sample period */ +#define ADIS16209_ALM_SMPL1 0x24 +/* Alarm 2, sample period */ +#define ADIS16209_ALM_SMPL2 0x26 +/* Alarm control */ +#define ADIS16209_ALM_CTRL 0x28 +/* Auxiliary DAC data */ +#define ADIS16209_AUX_DAC 0x30 +/* General-purpose digital input/output control */ +#define ADIS16209_GPIO_CTRL 0x32 +/* Miscellaneous control */ +#define ADIS16209_MSC_CTRL 0x34 +/* Internal sample period (rate) control */ +#define ADIS16209_SMPL_PRD 0x36 +/* Operation, filter configuration */ +#define ADIS16209_AVG_CNT 0x38 +/* Operation, sleep mode control */ +#define ADIS16209_SLP_CNT 0x3A +/* Diagnostics, system status register */ +#define ADIS16209_DIAG_STAT 0x3C +/* Operation, system command register */ +#define ADIS16209_GLOB_CMD 0x3E + +#define ADIS16209_OUTPUTS 8 + +/* MSC_CTRL */ +/* Self-test at power-on: 1 = disabled, 0 = enabled */ +#define ADIS16209_MSC_CTRL_PWRUP_SELF_TEST (1 << 10) +/* Self-test enable */ +#define ADIS16209_MSC_CTRL_SELF_TEST_EN (1 << 8) +/* Data-ready enable: 1 = enabled, 0 = disabled */ +#define ADIS16209_MSC_CTRL_DATA_RDY_EN (1 << 2) +/* Data-ready polarity: 1 = active high, 0 = active low */ +#define ADIS16209_MSC_CTRL_ACTIVE_HIGH (1 << 1) +/* Data-ready line selection: 1 = DIO2, 0 = DIO1 */ +#define ADIS16209_MSC_CTRL_DATA_RDY_DIO2 (1 << 0) + +/* DIAG_STAT */ +/* Alarm 2 status: 1 = alarm active, 0 = alarm inactive */ +#define ADIS16209_DIAG_STAT_ALARM2 (1<<9) +/* Alarm 1 status: 1 = alarm active, 0 = alarm inactive */ +#define ADIS16209_DIAG_STAT_ALARM1 (1<<8) +/* Self-test diagnostic error flag: 1 = error condition, 0 = normal operation */ +#define ADIS16209_DIAG_STAT_SELFTEST_FAIL (1<<5) +/* SPI communications failure */ +#define ADIS16209_DIAG_STAT_SPI_FAIL (1<<3) +/* Flash update failure */ +#define ADIS16209_DIAG_STAT_FLASH_UPT (1<<2) +/* Power supply above 3.625 V */ +#define ADIS16209_DIAG_STAT_POWER_HIGH (1<<1) +/* Power supply below 3.15 V */ +#define ADIS16209_DIAG_STAT_POWER_LOW (1<<0) + +/* GLOB_CMD */ +#define ADIS16209_GLOB_CMD_SW_RESET (1<<7) +#define ADIS16209_GLOB_CMD_CLEAR_STAT (1<<4) +#define ADIS16209_GLOB_CMD_FACTORY_CAL (1<<1) + +#define ADIS16209_MAX_TX 24 +#define ADIS16209_MAX_RX 24 + +#define ADIS16209_ERROR_ACTIVE (1<<14) + +/** + * struct adis16209_state - device instance specific data + * @us: actual spi_device + * @work_trigger_to_ring: bh for triggered event handling + * @work_cont_thresh: CLEAN + * @inter: used to check if new interrupt has been triggered + * @last_timestamp: passing timestamp from th to bh of interrupt handler + * @indio_dev: industrial I/O device structure + * @trig: data ready trigger registered with iio + * @tx: transmit buffer + * @rx: recieve buffer + * @buf_lock: mutex to protect tx and rx + **/ +struct adis16209_state { + struct spi_device *us; + struct work_struct work_trigger_to_ring; + struct iio_work_cont work_cont_thresh; + s64 last_timestamp; + struct iio_dev *indio_dev; + struct iio_trigger *trig; + u8 *tx; + u8 *rx; + struct mutex buf_lock; +}; + +int adis16209_set_irq(struct device *dev, bool enable); + +#ifdef CONFIG_IIO_RING_BUFFER +enum adis16209_scan { + ADIS16209_SCAN_SUPPLY, + ADIS16209_SCAN_ACC_X, + ADIS16209_SCAN_ACC_Y, + ADIS16209_SCAN_AUX_ADC, + ADIS16209_SCAN_TEMP, + ADIS16209_SCAN_INCLI_X, + ADIS16209_SCAN_INCLI_Y, + ADIS16209_SCAN_ROT, +}; + +void adis16209_remove_trigger(struct iio_dev *indio_dev); +int adis16209_probe_trigger(struct iio_dev *indio_dev); + +ssize_t adis16209_read_data_from_ring(struct device *dev, + struct device_attribute *attr, + char *buf); + +int adis16209_configure_ring(struct iio_dev *indio_dev); +void adis16209_unconfigure_ring(struct iio_dev *indio_dev); + +int adis16209_initialize_ring(struct iio_ring_buffer *ring); +void adis16209_uninitialize_ring(struct iio_ring_buffer *ring); +#else /* CONFIG_IIO_RING_BUFFER */ + +static inline void adis16209_remove_trigger(struct iio_dev *indio_dev) +{ +} + +static inline int adis16209_probe_trigger(struct iio_dev *indio_dev) +{ + return 0; +} + +static inline ssize_t +adis16209_read_data_from_ring(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return 0; +} + +static int adis16209_configure_ring(struct iio_dev *indio_dev) +{ + return 0; +} + +static inline void adis16209_unconfigure_ring(struct iio_dev *indio_dev) +{ +} + +static inline int adis16209_initialize_ring(struct iio_ring_buffer *ring) +{ + return 0; +} + +static inline void adis16209_uninitialize_ring(struct iio_ring_buffer *ring) +{ +} + +#endif /* CONFIG_IIO_RING_BUFFER */ +#endif /* SPI_ADIS16209_H_ */ diff --git a/drivers/staging/iio/accel/adis16209_core.c b/drivers/staging/iio/accel/adis16209_core.c new file mode 100644 index 000000000000..ac375c50f56f --- /dev/null +++ b/drivers/staging/iio/accel/adis16209_core.c @@ -0,0 +1,615 @@ +/* + * ADIS16209 Programmable Digital Vibration Sensor driver + * + * Copyright 2010 Analog Devices Inc. + * + * Licensed under the GPL-2 or later. + */ + +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/gpio.h> +#include <linux/delay.h> +#include <linux/mutex.h> +#include <linux/device.h> +#include <linux/kernel.h> +#include <linux/spi/spi.h> + +#include <linux/sysfs.h> +#include <linux/list.h> + +#include "../iio.h" +#include "../sysfs.h" +#include "accel.h" +#include "inclinometer.h" +#include "../gyro/gyro.h" +#include "../adc/adc.h" + +#include "adis16209.h" + +#define DRIVER_NAME "adis16209" + +static int adis16209_check_status(struct device *dev); + +/** + * adis16209_spi_write_reg_8() - write single byte to a register + * @dev: device associated with child of actual device (iio_dev or iio_trig) + * @reg_address: the address of the register to be written + * @val: the value to write + **/ +static int adis16209_spi_write_reg_8(struct device *dev, + u8 reg_address, + u8 val) +{ + int ret; + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct adis16209_state *st = iio_dev_get_devdata(indio_dev); + + mutex_lock(&st->buf_lock); + st->tx[0] = ADIS16209_WRITE_REG(reg_address); + st->tx[1] = val; + + ret = spi_write(st->us, st->tx, 2); + mutex_unlock(&st->buf_lock); + + return ret; +} + +/** + * adis16209_spi_write_reg_16() - write 2 bytes to a pair of registers + * @dev: device associated with child of actual device (iio_dev or iio_trig) + * @reg_address: the address of the lower of the two registers. Second register + * is assumed to have address one greater. + * @val: value to be written + **/ +static int adis16209_spi_write_reg_16(struct device *dev, + u8 lower_reg_address, + u16 value) +{ + int ret; + struct spi_message msg; + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct adis16209_state *st = iio_dev_get_devdata(indio_dev); + struct spi_transfer xfers[] = { + { + .tx_buf = st->tx, + .bits_per_word = 8, + .len = 2, + .cs_change = 1, + }, { + .tx_buf = st->tx + 2, + .bits_per_word = 8, + .len = 2, + .cs_change = 1, + }, + }; + + mutex_lock(&st->buf_lock); + st->tx[0] = ADIS16209_WRITE_REG(lower_reg_address); + st->tx[1] = value & 0xFF; + st->tx[2] = ADIS16209_WRITE_REG(lower_reg_address + 1); + st->tx[3] = (value >> 8) & 0xFF; + + spi_message_init(&msg); + spi_message_add_tail(&xfers[0], &msg); + spi_message_add_tail(&xfers[1], &msg); + ret = spi_sync(st->us, &msg); + mutex_unlock(&st->buf_lock); + + return ret; +} + +/** + * adis16209_spi_read_reg_16() - read 2 bytes from a 16-bit register + * @dev: device associated with child of actual device (iio_dev or iio_trig) + * @reg_address: the address of the lower of the two registers. Second register + * is assumed to have address one greater. + * @val: somewhere to pass back the value read + **/ +static int adis16209_spi_read_reg_16(struct device *dev, + u8 lower_reg_address, + u16 *val) +{ + struct spi_message msg; + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct adis16209_state *st = iio_dev_get_devdata(indio_dev); + int ret; + struct spi_transfer xfers[] = { + { + .tx_buf = st->tx, + .bits_per_word = 8, + .len = 2, + .cs_change = 1, + .delay_usecs = 20, + }, { + .rx_buf = st->rx, + .bits_per_word = 8, + .len = 2, + .cs_change = 1, + .delay_usecs = 20, + }, + }; + + mutex_lock(&st->buf_lock); + st->tx[0] = ADIS16209_READ_REG(lower_reg_address); + st->tx[1] = 0; + + spi_message_init(&msg); + spi_message_add_tail(&xfers[0], &msg); + spi_message_add_tail(&xfers[1], &msg); + ret = spi_sync(st->us, &msg); + if (ret) { + dev_err(&st->us->dev, + "problem when reading 16 bit register 0x%02X", + lower_reg_address); + goto error_ret; + } + *val = (st->rx[0] << 8) | st->rx[1]; + +error_ret: + mutex_unlock(&st->buf_lock); + return ret; +} + +static ssize_t adis16209_read_12bit_unsigned(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int ret; + u16 val = 0; + struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); + + ret = adis16209_spi_read_reg_16(dev, this_attr->address, &val); + if (ret) + return ret; + + if (val & ADIS16209_ERROR_ACTIVE) + adis16209_check_status(dev); + + return sprintf(buf, "%u\n", val & 0x0FFF); +} + +static ssize_t adis16209_read_14bit_unsigned(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int ret; + u16 val = 0; + struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); + + ret = adis16209_spi_read_reg_16(dev, this_attr->address, &val); + if (ret) + return ret; + + if (val & ADIS16209_ERROR_ACTIVE) + adis16209_check_status(dev); + + return sprintf(buf, "%u\n", val & 0x3FFF); +} + +static ssize_t adis16209_read_temp(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + ssize_t ret; + u16 val; + + /* Take the iio_dev status lock */ + mutex_lock(&indio_dev->mlock); + + ret = adis16209_spi_read_reg_16(dev, ADIS16209_TEMP_OUT, (u16 *)&val); + if (ret) + goto error_ret; + + if (val & ADIS16209_ERROR_ACTIVE) + adis16209_check_status(dev); + + val &= 0xFFF; + ret = sprintf(buf, "%d\n", val); + +error_ret: + mutex_unlock(&indio_dev->mlock); + return ret; +} + +static ssize_t adis16209_read_14bit_signed(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); + s16 val = 0; + ssize_t ret; + + mutex_lock(&indio_dev->mlock); + + ret = adis16209_spi_read_reg_16(dev, this_attr->address, (u16 *)&val); + if (!ret) { + if (val & ADIS16209_ERROR_ACTIVE) + adis16209_check_status(dev); + + val = ((s16)(val << 2) >> 2); + ret = sprintf(buf, "%d\n", val); + } + + mutex_unlock(&indio_dev->mlock); + + return ret; +} + +static ssize_t adis16209_write_16bit(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t len) +{ + struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); + int ret; + long val; + + ret = strict_strtol(buf, 10, &val); + if (ret) + goto error_ret; + ret = adis16209_spi_write_reg_16(dev, this_attr->address, val); + +error_ret: + return ret ? ret : len; +} + +static int adis16209_reset(struct device *dev) +{ + int ret; + ret = adis16209_spi_write_reg_8(dev, + ADIS16209_GLOB_CMD, + ADIS16209_GLOB_CMD_SW_RESET); + if (ret) + dev_err(dev, "problem resetting device"); + + return ret; +} + +static ssize_t adis16209_write_reset(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + if (len < 1) + return -EINVAL; + switch (buf[0]) { + case '1': + case 'y': + case 'Y': + return adis16209_reset(dev); + } + return -EINVAL; +} + +int adis16209_set_irq(struct device *dev, bool enable) +{ + int ret = 0; + u16 msc; + + ret = adis16209_spi_read_reg_16(dev, ADIS16209_MSC_CTRL, &msc); + if (ret) + goto error_ret; + + msc |= ADIS16209_MSC_CTRL_ACTIVE_HIGH; + msc &= ~ADIS16209_MSC_CTRL_DATA_RDY_DIO2; + if (enable) + msc |= ADIS16209_MSC_CTRL_DATA_RDY_EN; + else + msc &= ~ADIS16209_MSC_CTRL_DATA_RDY_EN; + + ret = adis16209_spi_write_reg_16(dev, ADIS16209_MSC_CTRL, msc); + +error_ret: + return ret; +} + +static int adis16209_check_status(struct device *dev) +{ + u16 status; + int ret; + + ret = adis16209_spi_read_reg_16(dev, ADIS16209_DIAG_STAT, &status); + if (ret < 0) { + dev_err(dev, "Reading status failed\n"); + goto error_ret; + } + ret = status & 0x1F; + + if (status & ADIS16209_DIAG_STAT_SELFTEST_FAIL) + dev_err(dev, "Self test failure\n"); + if (status & ADIS16209_DIAG_STAT_SPI_FAIL) + dev_err(dev, "SPI failure\n"); + if (status & ADIS16209_DIAG_STAT_FLASH_UPT) + dev_err(dev, "Flash update failed\n"); + if (status & ADIS16209_DIAG_STAT_POWER_HIGH) + dev_err(dev, "Power supply above 3.625V\n"); + if (status & ADIS16209_DIAG_STAT_POWER_LOW) + dev_err(dev, "Power supply below 3.15V\n"); + +error_ret: + return ret; +} + +static int adis16209_self_test(struct device *dev) +{ + int ret; + ret = adis16209_spi_write_reg_16(dev, + ADIS16209_MSC_CTRL, + ADIS16209_MSC_CTRL_SELF_TEST_EN); + if (ret) { + dev_err(dev, "problem starting self test"); + goto err_ret; + } + + adis16209_check_status(dev); + +err_ret: + return ret; +} + +static int adis16209_initial_setup(struct adis16209_state *st) +{ + int ret; + struct device *dev = &st->indio_dev->dev; + + /* Disable IRQ */ + ret = adis16209_set_irq(dev, false); + if (ret) { + dev_err(dev, "disable irq failed"); + goto err_ret; + } + + /* Do self test */ + ret = adis16209_self_test(dev); + if (ret) { + dev_err(dev, "self test failure"); + goto err_ret; + } + + /* Read status register to check the result */ + ret = adis16209_check_status(dev); + if (ret) { + adis16209_reset(dev); + dev_err(dev, "device not playing ball -> reset"); + msleep(ADIS16209_STARTUP_DELAY); + ret = adis16209_check_status(dev); + if (ret) { + dev_err(dev, "giving up"); + goto err_ret; + } + } + + printk(KERN_INFO DRIVER_NAME ": at CS%d (irq %d)\n", + st->us->chip_select, st->us->irq); + +err_ret: + return ret; +} + +static IIO_DEV_ATTR_IN_NAMED_RAW(supply, adis16209_read_14bit_unsigned, + ADIS16209_SUPPLY_OUT); +static IIO_CONST_ATTR(in_supply_scale, "0.30518"); +static IIO_DEV_ATTR_IN_RAW(0, adis16209_read_12bit_unsigned, + ADIS16209_AUX_ADC); +static IIO_CONST_ATTR(in0_scale, "0.6105"); + +static IIO_DEV_ATTR_ACCEL_X(adis16209_read_14bit_signed, + ADIS16209_XACCL_OUT); +static IIO_DEV_ATTR_ACCEL_Y(adis16209_read_14bit_signed, + ADIS16209_YACCL_OUT); +static IIO_DEV_ATTR_ACCEL_X_OFFSET(S_IWUSR | S_IRUGO, + adis16209_read_14bit_signed, + adis16209_write_16bit, + ADIS16209_XACCL_NULL); +static IIO_DEV_ATTR_ACCEL_Y_OFFSET(S_IWUSR | S_IRUGO, + adis16209_read_14bit_signed, + adis16209_write_16bit, + ADIS16209_YACCL_NULL); +static IIO_CONST_ATTR(accel_scale, "0.24414"); + +static IIO_DEV_ATTR_INCLI_X(adis16209_read_14bit_signed, + ADIS16209_XINCL_OUT); +static IIO_DEV_ATTR_INCLI_Y(adis16209_read_14bit_signed, + ADIS16209_YINCL_OUT); +static IIO_DEV_ATTR_INCLI_X_OFFSET(S_IWUSR | S_IRUGO, + adis16209_read_14bit_signed, + adis16209_write_16bit, + ADIS16209_XACCL_NULL); +static IIO_DEV_ATTR_INCLI_Y_OFFSET(S_IWUSR | S_IRUGO, + adis16209_read_14bit_signed, + adis16209_write_16bit, + ADIS16209_YACCL_NULL); +static IIO_CONST_ATTR(incli_scale, "0.025"); + +static IIO_DEVICE_ATTR(rot_raw, S_IRUGO, adis16209_read_14bit_signed, + NULL, ADIS16209_ROT_OUT); + +static IIO_DEV_ATTR_TEMP(adis16209_read_temp); +static IIO_CONST_ATTR(temp_offset, "25"); +static IIO_CONST_ATTR(temp_scale, "-0.47"); + +static IIO_DEVICE_ATTR(reset, S_IWUSR, NULL, adis16209_write_reset, 0); + +static IIO_CONST_ATTR(name, "adis16209"); + +static struct attribute *adis16209_event_attributes[] = { + NULL +}; + +static struct attribute_group adis16209_event_attribute_group = { + .attrs = adis16209_event_attributes, +}; + +static struct attribute *adis16209_attributes[] = { + &iio_dev_attr_in_supply_raw.dev_attr.attr, + &iio_const_attr_in_supply_scale.dev_attr.attr, + &iio_dev_attr_temp.dev_attr.attr, + &iio_const_attr_temp_offset.dev_attr.attr, + &iio_const_attr_temp_scale.dev_attr.attr, + &iio_dev_attr_reset.dev_attr.attr, + &iio_const_attr_name.dev_attr.attr, + &iio_dev_attr_in0_raw.dev_attr.attr, + &iio_const_attr_in0_scale.dev_attr.attr, + &iio_dev_attr_accel_x_raw.dev_attr.attr, + &iio_dev_attr_accel_y_raw.dev_attr.attr, + &iio_dev_attr_accel_x_offset.dev_attr.attr, + &iio_dev_attr_accel_y_offset.dev_attr.attr, + &iio_const_attr_accel_scale.dev_attr.attr, + &iio_dev_attr_incli_x_raw.dev_attr.attr, + &iio_dev_attr_incli_y_raw.dev_attr.attr, + &iio_dev_attr_incli_x_offset.dev_attr.attr, + &iio_dev_attr_incli_y_offset.dev_attr.attr, + &iio_const_attr_incli_scale.dev_attr.attr, + &iio_dev_attr_rot_raw.dev_attr.attr, + NULL +}; + +static const struct attribute_group adis16209_attribute_group = { + .attrs = adis16209_attributes, +}; + +static int __devinit adis16209_probe(struct spi_device *spi) +{ + int ret, regdone = 0; + struct adis16209_state *st = kzalloc(sizeof *st, GFP_KERNEL); + if (!st) { + ret = -ENOMEM; + goto error_ret; + } + /* this is only used for removal purposes */ + spi_set_drvdata(spi, st); + + /* Allocate the comms buffers */ + st->rx = kzalloc(sizeof(*st->rx)*ADIS16209_MAX_RX, GFP_KERNEL); + if (st->rx == NULL) { + ret = -ENOMEM; + goto error_free_st; + } + st->tx = kzalloc(sizeof(*st->tx)*ADIS16209_MAX_TX, GFP_KERNEL); + if (st->tx == NULL) { + ret = -ENOMEM; + goto error_free_rx; + } + st->us = spi; + mutex_init(&st->buf_lock); + /* setup the industrialio driver allocated elements */ + st->indio_dev = iio_allocate_device(); + if (st->indio_dev == NULL) { + ret = -ENOMEM; + goto error_free_tx; + } + + st->indio_dev->dev.parent = &spi->dev; + st->indio_dev->num_interrupt_lines = 1; + st->indio_dev->event_attrs = &adis16209_event_attribute_group; + st->indio_dev->attrs = &adis16209_attribute_group; + st->indio_dev->dev_data = (void *)(st); + st->indio_dev->driver_module = THIS_MODULE; + st->indio_dev->modes = INDIO_DIRECT_MODE; + + ret = adis16209_configure_ring(st->indio_dev); + if (ret) + goto error_free_dev; + + ret = iio_device_register(st->indio_dev); + if (ret) + goto error_unreg_ring_funcs; + regdone = 1; + + ret = adis16209_initialize_ring(st->indio_dev->ring); + if (ret) { + printk(KERN_ERR "failed to initialize the ring\n"); + goto error_unreg_ring_funcs; + } + + if (spi->irq) { + ret = iio_register_interrupt_line(spi->irq, + st->indio_dev, + 0, + IRQF_TRIGGER_RISING, + "adis16209"); + if (ret) + goto error_uninitialize_ring; + + ret = adis16209_probe_trigger(st->indio_dev); + if (ret) + goto error_unregister_line; + } + + /* Get the device into a sane initial state */ + ret = adis16209_initial_setup(st); + if (ret) + goto error_remove_trigger; + return 0; + +error_remove_trigger: + adis16209_remove_trigger(st->indio_dev); +error_unregister_line: + if (spi->irq) + iio_unregister_interrupt_line(st->indio_dev, 0); +error_uninitialize_ring: + adis16209_uninitialize_ring(st->indio_dev->ring); +error_unreg_ring_funcs: + adis16209_unconfigure_ring(st->indio_dev); +error_free_dev: + if (regdone) + iio_device_unregister(st->indio_dev); + else + iio_free_device(st->indio_dev); +error_free_tx: + kfree(st->tx); +error_free_rx: + kfree(st->rx); +error_free_st: + kfree(st); +error_ret: + return ret; +} + +static int adis16209_remove(struct spi_device *spi) +{ + struct adis16209_state *st = spi_get_drvdata(spi); + struct iio_dev *indio_dev = st->indio_dev; + + flush_scheduled_work(); + + adis16209_remove_trigger(indio_dev); + if (spi->irq) + iio_unregister_interrupt_line(indio_dev, 0); + + adis16209_uninitialize_ring(indio_dev->ring); + iio_device_unregister(indio_dev); + adis16209_unconfigure_ring(indio_dev); + kfree(st->tx); + kfree(st->rx); + kfree(st); + + return 0; +} + +static struct spi_driver adis16209_driver = { + .driver = { + .name = "adis16209", + .owner = THIS_MODULE, + }, + .probe = adis16209_probe, + .remove = __devexit_p(adis16209_remove), +}; + +static __init int adis16209_init(void) +{ + return spi_register_driver(&adis16209_driver); +} +module_init(adis16209_init); + +static __exit void adis16209_exit(void) +{ + spi_unregister_driver(&adis16209_driver); +} +module_exit(adis16209_exit); + +MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>"); +MODULE_DESCRIPTION("Analog Devices ADIS16209 Digital Vibration Sensor driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/iio/accel/adis16209_ring.c b/drivers/staging/iio/accel/adis16209_ring.c new file mode 100644 index 000000000000..533e28574910 --- /dev/null +++ b/drivers/staging/iio/accel/adis16209_ring.c @@ -0,0 +1,266 @@ +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/gpio.h> +#include <linux/workqueue.h> +#include <linux/mutex.h> +#include <linux/device.h> +#include <linux/kernel.h> +#include <linux/spi/spi.h> +#include <linux/sysfs.h> +#include <linux/list.h> + +#include "../iio.h" +#include "../sysfs.h" +#include "../ring_sw.h" +#include "accel.h" +#include "../trigger.h" +#include "adis16209.h" + +/** + * combine_8_to_16() utility function to munge to u8s into u16 + **/ +static inline u16 combine_8_to_16(u8 lower, u8 upper) +{ + u16 _lower = lower; + u16 _upper = upper; + return _lower | (_upper << 8); +} + +static IIO_SCAN_EL_C(supply, ADIS16209_SCAN_SUPPLY, IIO_UNSIGNED(14), + ADIS16209_SUPPLY_OUT, NULL); +static IIO_SCAN_EL_C(accel_x, ADIS16209_SCAN_ACC_X, IIO_SIGNED(14), + ADIS16209_XACCL_OUT, NULL); +static IIO_SCAN_EL_C(accel_y, ADIS16209_SCAN_ACC_Y, IIO_SIGNED(14), + ADIS16209_YACCL_OUT, NULL); +static IIO_SCAN_EL_C(aux_adc, ADIS16209_SCAN_AUX_ADC, IIO_UNSIGNED(12), + ADIS16209_AUX_ADC, NULL); +static IIO_SCAN_EL_C(temp, ADIS16209_SCAN_TEMP, IIO_UNSIGNED(12), + ADIS16209_TEMP_OUT, NULL); +static IIO_SCAN_EL_C(incli_x, ADIS16209_SCAN_INCLI_X, IIO_SIGNED(14), + ADIS16209_XINCL_OUT, NULL); +static IIO_SCAN_EL_C(incli_y, ADIS16209_SCAN_INCLI_Y, IIO_SIGNED(14), + ADIS16209_YINCL_OUT, NULL); +static IIO_SCAN_EL_C(rot, ADIS16209_SCAN_ROT, IIO_SIGNED(14), + ADIS16209_ROT_OUT, NULL); + +static IIO_SCAN_EL_TIMESTAMP(8); + +static struct attribute *adis16209_scan_el_attrs[] = { + &iio_scan_el_supply.dev_attr.attr, + &iio_scan_el_accel_x.dev_attr.attr, + &iio_scan_el_accel_y.dev_attr.attr, + &iio_scan_el_aux_adc.dev_attr.attr, + &iio_scan_el_temp.dev_attr.attr, + &iio_scan_el_incli_x.dev_attr.attr, + &iio_scan_el_incli_y.dev_attr.attr, + &iio_scan_el_rot.dev_attr.attr, + &iio_scan_el_timestamp.dev_attr.attr, + NULL, +}; + +static struct attribute_group adis16209_scan_el_group = { + .attrs = adis16209_scan_el_attrs, + .name = "scan_elements", +}; + +/** + * adis16209_poll_func_th() top half interrupt handler called by trigger + * @private_data: iio_dev + **/ +static void adis16209_poll_func_th(struct iio_dev *indio_dev) +{ + struct adis16209_state *st = iio_dev_get_devdata(indio_dev); + st->last_timestamp = indio_dev->trig->timestamp; + schedule_work(&st->work_trigger_to_ring); +} + +/** + * adis16209_read_ring_data() read data registers which will be placed into ring + * @dev: device associated with child of actual device (iio_dev or iio_trig) + * @rx: somewhere to pass back the value read + **/ +static int adis16209_read_ring_data(struct device *dev, u8 *rx) +{ + struct spi_message msg; + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct adis16209_state *st = iio_dev_get_devdata(indio_dev); + struct spi_transfer xfers[ADIS16209_OUTPUTS + 1]; + int ret; + int i; + + mutex_lock(&st->buf_lock); + + spi_message_init(&msg); + + memset(xfers, 0, sizeof(xfers)); + for (i = 0; i <= ADIS16209_OUTPUTS; i++) { + xfers[i].bits_per_word = 8; + xfers[i].cs_change = 1; + xfers[i].len = 2; + xfers[i].delay_usecs = 20; + xfers[i].tx_buf = st->tx + 2 * i; + st->tx[2 * i] + = ADIS16209_READ_REG(ADIS16209_SUPPLY_OUT + 2 * i); + st->tx[2 * i + 1] = 0; + if (i >= 1) + xfers[i].rx_buf = rx + 2 * (i - 1); + spi_message_add_tail(&xfers[i], &msg); + } + + ret = spi_sync(st->us, &msg); + if (ret) + dev_err(&st->us->dev, "problem when burst reading"); + + mutex_unlock(&st->buf_lock); + + return ret; +} + +/* Whilst this makes a lot of calls to iio_sw_ring functions - it is to device + * specific to be rolled into the core. + */ +static void adis16209_trigger_bh_to_ring(struct work_struct *work_s) +{ + struct adis16209_state *st + = container_of(work_s, struct adis16209_state, + work_trigger_to_ring); + + int i = 0; + s16 *data; + size_t datasize = st->indio_dev + ->ring->access.get_bpd(st->indio_dev->ring); + + data = kmalloc(datasize , GFP_KERNEL); + if (data == NULL) { + dev_err(&st->us->dev, "memory alloc failed in ring bh"); + return; + } + + if (st->indio_dev->scan_count) + if (adis16209_read_ring_data(&st->indio_dev->dev, st->rx) >= 0) + for (; i < st->indio_dev->scan_count; i++) { + data[i] = combine_8_to_16(st->rx[i*2+1], + st->rx[i*2]); + } + + /* Guaranteed to be aligned with 8 byte boundary */ + if (st->indio_dev->scan_timestamp) + *((s64 *)(data + ((i + 3)/4)*4)) = st->last_timestamp; + + st->indio_dev->ring->access.store_to(st->indio_dev->ring, + (u8 *)data, + st->last_timestamp); + + iio_trigger_notify_done(st->indio_dev->trig); + kfree(data); + + return; +} + +/* in these circumstances is it better to go with unaligned packing and + * deal with the cost?*/ +static int adis16209_data_rdy_ring_preenable(struct iio_dev *indio_dev) +{ + size_t size; + dev_dbg(&indio_dev->dev, "%s\n", __func__); + /* Check if there are any scan elements enabled, if not fail*/ + if (!(indio_dev->scan_count || indio_dev->scan_timestamp)) + return -EINVAL; + + if (indio_dev->ring->access.set_bpd) { + if (indio_dev->scan_timestamp) + if (indio_dev->scan_count) + /* Timestamp (aligned to s64) and data */ + size = (((indio_dev->scan_count * sizeof(s16)) + + sizeof(s64) - 1) + & ~(sizeof(s64) - 1)) + + sizeof(s64); + else /* Timestamp only */ + size = sizeof(s64); + else /* Data only */ + size = indio_dev->scan_count*sizeof(s16); + indio_dev->ring->access.set_bpd(indio_dev->ring, size); + } + + return 0; +} + +static int adis16209_data_rdy_ring_postenable(struct iio_dev *indio_dev) +{ + return indio_dev->trig + ? iio_trigger_attach_poll_func(indio_dev->trig, + indio_dev->pollfunc) + : 0; +} + +static int adis16209_data_rdy_ring_predisable(struct iio_dev *indio_dev) +{ + return indio_dev->trig + ? iio_trigger_dettach_poll_func(indio_dev->trig, + indio_dev->pollfunc) + : 0; +} + +void adis16209_unconfigure_ring(struct iio_dev *indio_dev) +{ + kfree(indio_dev->pollfunc); + iio_sw_rb_free(indio_dev->ring); +} + +int adis16209_configure_ring(struct iio_dev *indio_dev) +{ + int ret = 0; + struct adis16209_state *st = indio_dev->dev_data; + struct iio_ring_buffer *ring; + INIT_WORK(&st->work_trigger_to_ring, adis16209_trigger_bh_to_ring); + /* Set default scan mode */ + + iio_scan_mask_set(indio_dev, iio_scan_el_supply.number); + iio_scan_mask_set(indio_dev, iio_scan_el_rot.number); + iio_scan_mask_set(indio_dev, iio_scan_el_accel_x.number); + iio_scan_mask_set(indio_dev, iio_scan_el_accel_y.number); + iio_scan_mask_set(indio_dev, iio_scan_el_temp.number); + iio_scan_mask_set(indio_dev, iio_scan_el_aux_adc.number); + iio_scan_mask_set(indio_dev, iio_scan_el_incli_x.number); + iio_scan_mask_set(indio_dev, iio_scan_el_incli_y.number); + indio_dev->scan_timestamp = true; + + indio_dev->scan_el_attrs = &adis16209_scan_el_group; + + ring = iio_sw_rb_allocate(indio_dev); + if (!ring) { + ret = -ENOMEM; + return ret; + } + indio_dev->ring = ring; + /* Effectively select the ring buffer implementation */ + iio_ring_sw_register_funcs(&ring->access); + ring->preenable = &adis16209_data_rdy_ring_preenable; + ring->postenable = &adis16209_data_rdy_ring_postenable; + ring->predisable = &adis16209_data_rdy_ring_predisable; + ring->owner = THIS_MODULE; + + indio_dev->pollfunc = kzalloc(sizeof(*indio_dev->pollfunc), GFP_KERNEL); + if (indio_dev->pollfunc == NULL) { + ret = -ENOMEM; + goto error_iio_sw_rb_free;; + } + indio_dev->pollfunc->poll_func_main = &adis16209_poll_func_th; + indio_dev->pollfunc->private_data = indio_dev; + indio_dev->modes |= INDIO_RING_TRIGGERED; + return 0; + +error_iio_sw_rb_free: + iio_sw_rb_free(indio_dev->ring); + return ret; +} + +int adis16209_initialize_ring(struct iio_ring_buffer *ring) +{ + return iio_ring_buffer_register(ring, 0); +} + +void adis16209_uninitialize_ring(struct iio_ring_buffer *ring) +{ + iio_ring_buffer_unregister(ring); +} diff --git a/drivers/staging/iio/accel/adis16209_trigger.c b/drivers/staging/iio/accel/adis16209_trigger.c new file mode 100644 index 000000000000..4a0507c9a13b --- /dev/null +++ b/drivers/staging/iio/accel/adis16209_trigger.c @@ -0,0 +1,124 @@ +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/mutex.h> +#include <linux/device.h> +#include <linux/kernel.h> +#include <linux/sysfs.h> +#include <linux/list.h> +#include <linux/spi/spi.h> + +#include "../iio.h" +#include "../sysfs.h" +#include "../trigger.h" +#include "adis16209.h" + +/** + * adis16209_data_rdy_trig_poll() the event handler for the data rdy trig + **/ +static int adis16209_data_rdy_trig_poll(struct iio_dev *dev_info, + int index, + s64 timestamp, + int no_test) +{ + struct adis16209_state *st = iio_dev_get_devdata(dev_info); + struct iio_trigger *trig = st->trig; + + trig->timestamp = timestamp; + iio_trigger_poll(trig); + + return IRQ_HANDLED; +} + +IIO_EVENT_SH(data_rdy_trig, &adis16209_data_rdy_trig_poll); + +static DEVICE_ATTR(name, S_IRUGO, iio_trigger_read_name, NULL); + +static struct attribute *adis16209_trigger_attrs[] = { + &dev_attr_name.attr, + NULL, +}; + +static const struct attribute_group adis16209_trigger_attr_group = { + .attrs = adis16209_trigger_attrs, +}; + +/** + * adis16209_data_rdy_trigger_set_state() set datardy interrupt state + **/ +static int adis16209_data_rdy_trigger_set_state(struct iio_trigger *trig, + bool state) +{ + struct adis16209_state *st = trig->private_data; + struct iio_dev *indio_dev = st->indio_dev; + int ret = 0; + + dev_dbg(&indio_dev->dev, "%s (%d)\n", __func__, state); + ret = adis16209_set_irq(&st->indio_dev->dev, state); + if (state == false) { + iio_remove_event_from_list(&iio_event_data_rdy_trig, + &indio_dev->interrupts[0] + ->ev_list); + flush_scheduled_work(); + } else { + iio_add_event_to_list(&iio_event_data_rdy_trig, + &indio_dev->interrupts[0]->ev_list); + } + return ret; +} + +/** + * adis16209_trig_try_reen() try renabling irq for data rdy trigger + * @trig: the datardy trigger + **/ +static int adis16209_trig_try_reen(struct iio_trigger *trig) +{ + struct adis16209_state *st = trig->private_data; + enable_irq(st->us->irq); + return 0; +} + +int adis16209_probe_trigger(struct iio_dev *indio_dev) +{ + int ret; + struct adis16209_state *st = indio_dev->dev_data; + + st->trig = iio_allocate_trigger(); + st->trig->name = kmalloc(IIO_TRIGGER_NAME_LENGTH, GFP_KERNEL); + if (!st->trig->name) { + ret = -ENOMEM; + goto error_free_trig; + } + snprintf((char *)st->trig->name, + IIO_TRIGGER_NAME_LENGTH, + "adis16209-dev%d", indio_dev->id); + st->trig->dev.parent = &st->us->dev; + st->trig->owner = THIS_MODULE; + st->trig->private_data = st; + st->trig->set_trigger_state = &adis16209_data_rdy_trigger_set_state; + st->trig->try_reenable = &adis16209_trig_try_reen; + st->trig->control_attrs = &adis16209_trigger_attr_group; + ret = iio_trigger_register(st->trig); + + /* select default trigger */ + indio_dev->trig = st->trig; + if (ret) + goto error_free_trig_name; + + return 0; + +error_free_trig_name: + kfree(st->trig->name); +error_free_trig: + iio_free_trigger(st->trig); + + return ret; +} + +void adis16209_remove_trigger(struct iio_dev *indio_dev) +{ + struct adis16209_state *state = indio_dev->dev_data; + + iio_trigger_unregister(state->trig); + kfree(state->trig->name); + iio_free_trigger(state->trig); +} diff --git a/drivers/staging/iio/accel/adis16220.h b/drivers/staging/iio/accel/adis16220.h new file mode 100644 index 000000000000..2abf4850b373 --- /dev/null +++ b/drivers/staging/iio/accel/adis16220.h @@ -0,0 +1,147 @@ +#ifndef SPI_ADIS16220_H_ +#define SPI_ADIS16220_H_ + +#define ADIS16220_STARTUP_DELAY 220 /* ms */ + +#define ADIS16220_READ_REG(a) a +#define ADIS16220_WRITE_REG(a) ((a) | 0x80) + +/* Flash memory write count */ +#define ADIS16220_FLASH_CNT 0x00 +/* Control, acceleration offset adjustment control */ +#define ADIS16220_ACCL_NULL 0x02 +/* Control, AIN1 offset adjustment control */ +#define ADIS16220_AIN1_NULL 0x04 +/* Control, AIN2 offset adjustment control */ +#define ADIS16220_AIN2_NULL 0x06 +/* Output, power supply during capture */ +#define ADIS16220_CAPT_SUPPLY 0x0A +/* Output, temperature during capture */ +#define ADIS16220_CAPT_TEMP 0x0C +/* Output, peak acceleration during capture */ +#define ADIS16220_CAPT_PEAKA 0x0E +/* Output, peak AIN1 level during capture */ +#define ADIS16220_CAPT_PEAK1 0x10 +/* Output, peak AIN2 level during capture */ +#define ADIS16220_CAPT_PEAK2 0x12 +/* Output, capture buffer for acceleration */ +#define ADIS16220_CAPT_BUFA 0x14 +/* Output, capture buffer for AIN1 */ +#define ADIS16220_CAPT_BUF1 0x16 +/* Output, capture buffer for AIN2 */ +#define ADIS16220_CAPT_BUF2 0x18 +/* Control, capture buffer address pointer */ +#define ADIS16220_CAPT_PNTR 0x1A +/* Control, capture control register */ +#define ADIS16220_CAPT_CTRL 0x1C +/* Control, capture period (automatic mode) */ +#define ADIS16220_CAPT_PRD 0x1E +/* Control, Alarm A, acceleration peak threshold */ +#define ADIS16220_ALM_MAGA 0x20 +/* Control, Alarm 1, AIN1 peak threshold */ +#define ADIS16220_ALM_MAG1 0x22 +/* Control, Alarm 2, AIN2 peak threshold */ +#define ADIS16220_ALM_MAG2 0x24 +/* Control, Alarm S, peak threshold */ +#define ADIS16220_ALM_MAGS 0x26 +/* Control, alarm configuration register */ +#define ADIS16220_ALM_CTRL 0x28 +/* Control, general I/O configuration */ +#define ADIS16220_GPIO_CTRL 0x32 +/* Control, self-test control, AIN configuration */ +#define ADIS16220_MSC_CTRL 0x34 +/* Control, digital I/O configuration */ +#define ADIS16220_DIO_CTRL 0x36 +/* Control, filter configuration */ +#define ADIS16220_AVG_CNT 0x38 +/* Status, system status */ +#define ADIS16220_DIAG_STAT 0x3C +/* Control, system commands */ +#define ADIS16220_GLOB_CMD 0x3E +/* Status, self-test response */ +#define ADIS16220_ST_DELTA 0x40 +/* Lot Identification Code 1 */ +#define ADIS16220_LOT_ID1 0x52 +/* Lot Identification Code 2 */ +#define ADIS16220_LOT_ID2 0x54 +/* Product identifier; convert to decimal = 16220 */ +#define ADIS16220_PROD_ID 0x56 +/* Serial number */ +#define ADIS16220_SERIAL_NUM 0x58 + +#define ADIS16220_CAPTURE_SIZE 2048 + +/* MSC_CTRL */ +#define ADIS16220_MSC_CTRL_SELF_TEST_EN (1 << 8) +#define ADIS16220_MSC_CTRL_POWER_SUP_COM_AIN1 (1 << 1) +#define ADIS16220_MSC_CTRL_POWER_SUP_COM_AIN2 (1 << 0) + +/* DIO_CTRL */ +#define ADIS16220_MSC_CTRL_DIO2_BUSY_IND (3<<4) +#define ADIS16220_MSC_CTRL_DIO1_BUSY_IND (3<<2) +#define ADIS16220_MSC_CTRL_DIO2_ACT_HIGH (1<<1) +#define ADIS16220_MSC_CTRL_DIO1_ACT_HIGH (1<<0) + +/* DIAG_STAT */ +/* AIN2 sample > ALM_MAG2 */ +#define ADIS16220_DIAG_STAT_ALM_MAG2 (1<<14) +/* AIN1 sample > ALM_MAG1 */ +#define ADIS16220_DIAG_STAT_ALM_MAG1 (1<<13) +/* Acceleration sample > ALM_MAGA */ +#define ADIS16220_DIAG_STAT_ALM_MAGA (1<<12) +/* Error condition programmed into ALM_MAGS[11:0] and ALM_CTRL[5:4] is true */ +#define ADIS16220_DIAG_STAT_ALM_MAGS (1<<11) +/* |Peak value in AIN2 data capture| > ALM_MAG2 */ +#define ADIS16220_DIAG_STAT_PEAK_AIN2 (1<<10) +/* |Peak value in AIN1 data capture| > ALM_MAG1 */ +#define ADIS16220_DIAG_STAT_PEAK_AIN1 (1<<9) +/* |Peak value in acceleration data capture| > ALM_MAGA */ +#define ADIS16220_DIAG_STAT_PEAK_ACCEL (1<<8) +/* Data ready, capture complete */ +#define ADIS16220_DIAG_STAT_DATA_RDY (1<<7) +#define ADIS16220_DIAG_STAT_FLASH_CHK (1<<6) +#define ADIS16220_DIAG_STAT_SELF_TEST (1<<5) +/* Capture period violation/interruption */ +#define ADIS16220_DIAG_STAT_VIOLATION (1<<4) +/* SPI communications failure */ +#define ADIS16220_DIAG_STAT_SPI_FAIL (1<<3) +/* Flash update failure */ +#define ADIS16220_DIAG_STAT_FLASH_UPT (1<<2) +/* Power supply above 3.625 V */ +#define ADIS16220_DIAG_STAT_POWER_HIGH (1<<1) +/* Power supply below 3.15 V */ +#define ADIS16220_DIAG_STAT_POWER_LOW (1<<0) + +/* GLOB_CMD */ +#define ADIS16220_GLOB_CMD_SW_RESET (1<<7) +#define ADIS16220_GLOB_CMD_SELF_TEST (1<<2) +#define ADIS16220_GLOB_CMD_PWR_DOWN (1<<1) + +#define ADIS16220_MAX_TX 2048 +#define ADIS16220_MAX_RX 2048 + +#define ADIS16220_SPI_BURST (u32)(1000 * 1000) +#define ADIS16220_SPI_FAST (u32)(2000 * 1000) + +/** + * struct adis16220_state - device instance specific data + * @us: actual spi_device + * @work_trigger_to_ring: bh for triggered event handling + * @work_cont_thresh: CLEAN + * @inter: used to check if new interrupt has been triggered + * @last_timestamp: passing timestamp from th to bh of interrupt handler + * @indio_dev: industrial I/O device structure + * @trig: data ready trigger registered with iio + * @tx: transmit buffer + * @rx: recieve buffer + * @buf_lock: mutex to protect tx and rx + **/ +struct adis16220_state { + struct spi_device *us; + struct iio_dev *indio_dev; + u8 *tx; + u8 *rx; + struct mutex buf_lock; +}; + +#endif /* SPI_ADIS16220_H_ */ diff --git a/drivers/staging/iio/accel/adis16220_core.c b/drivers/staging/iio/accel/adis16220_core.c new file mode 100644 index 000000000000..6de439fd1675 --- /dev/null +++ b/drivers/staging/iio/accel/adis16220_core.c @@ -0,0 +1,670 @@ +/* + * ADIS16220 Programmable Digital Vibration Sensor driver + * + * Copyright 2010 Analog Devices Inc. + * + * Licensed under the GPL-2 or later. + */ + +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/gpio.h> +#include <linux/delay.h> +#include <linux/mutex.h> +#include <linux/device.h> +#include <linux/kernel.h> +#include <linux/spi/spi.h> + +#include <linux/sysfs.h> +#include <linux/list.h> + +#include "../iio.h" +#include "../sysfs.h" +#include "accel.h" +#include "../adc/adc.h" + +#include "adis16220.h" + +#define DRIVER_NAME "adis16220" + +/** + * adis16220_spi_write_reg_8() - write single byte to a register + * @dev: device associated with child of actual device (iio_dev or iio_trig) + * @reg_address: the address of the register to be written + * @val: the value to write + **/ +static int adis16220_spi_write_reg_8(struct device *dev, + u8 reg_address, + u8 val) +{ + int ret; + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct adis16220_state *st = iio_dev_get_devdata(indio_dev); + + mutex_lock(&st->buf_lock); + st->tx[0] = ADIS16220_WRITE_REG(reg_address); + st->tx[1] = val; + + ret = spi_write(st->us, st->tx, 2); + mutex_unlock(&st->buf_lock); + + return ret; +} + +/** + * adis16220_spi_write_reg_16() - write 2 bytes to a pair of registers + * @dev: device associated with child of actual device (iio_dev or iio_trig) + * @reg_address: the address of the lower of the two registers. Second register + * is assumed to have address one greater. + * @val: value to be written + **/ +static int adis16220_spi_write_reg_16(struct device *dev, + u8 lower_reg_address, + u16 value) +{ + int ret; + struct spi_message msg; + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct adis16220_state *st = iio_dev_get_devdata(indio_dev); + struct spi_transfer xfers[] = { + { + .tx_buf = st->tx, + .bits_per_word = 8, + .len = 2, + .cs_change = 1, + .delay_usecs = 25, + }, { + .tx_buf = st->tx + 2, + .bits_per_word = 8, + .len = 2, + .cs_change = 1, + .delay_usecs = 25, + }, + }; + + mutex_lock(&st->buf_lock); + st->tx[0] = ADIS16220_WRITE_REG(lower_reg_address); + st->tx[1] = value & 0xFF; + st->tx[2] = ADIS16220_WRITE_REG(lower_reg_address + 1); + st->tx[3] = (value >> 8) & 0xFF; + + spi_message_init(&msg); + spi_message_add_tail(&xfers[0], &msg); + spi_message_add_tail(&xfers[1], &msg); + ret = spi_sync(st->us, &msg); + mutex_unlock(&st->buf_lock); + + return ret; +} + +/** + * adis16220_spi_read_reg_16() - read 2 bytes from a 16-bit register + * @dev: device associated with child of actual device (iio_dev or iio_trig) + * @reg_address: the address of the lower of the two registers. Second register + * is assumed to have address one greater. + * @val: somewhere to pass back the value read + **/ +static int adis16220_spi_read_reg_16(struct device *dev, + u8 lower_reg_address, + u16 *val) +{ + struct spi_message msg; + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct adis16220_state *st = iio_dev_get_devdata(indio_dev); + int ret; + struct spi_transfer xfers[] = { + { + .tx_buf = st->tx, + .bits_per_word = 8, + .len = 2, + .cs_change = 1, + .delay_usecs = 25, + }, { + .rx_buf = st->rx, + .bits_per_word = 8, + .len = 2, + .cs_change = 1, + .delay_usecs = 25, + }, + }; + + mutex_lock(&st->buf_lock); + st->tx[0] = ADIS16220_READ_REG(lower_reg_address); + st->tx[1] = 0; + + spi_message_init(&msg); + spi_message_add_tail(&xfers[0], &msg); + spi_message_add_tail(&xfers[1], &msg); + ret = spi_sync(st->us, &msg); + if (ret) { + dev_err(&st->us->dev, + "problem when reading 16 bit register 0x%02X", + lower_reg_address); + goto error_ret; + } + *val = (st->rx[0] << 8) | st->rx[1]; + +error_ret: + mutex_unlock(&st->buf_lock); + return ret; +} + +static ssize_t adis16220_spi_read_signed(struct device *dev, + struct device_attribute *attr, + char *buf, + unsigned bits) +{ + int ret; + s16 val = 0; + unsigned shift = 16 - bits; + struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); + + ret = adis16220_spi_read_reg_16(dev, this_attr->address, (u16 *)&val); + if (ret) + return ret; + + val = ((s16)(val << shift) >> shift); + return sprintf(buf, "%d\n", val); +} + +static ssize_t adis16220_read_12bit_unsigned(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int ret; + u16 val = 0; + struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); + + ret = adis16220_spi_read_reg_16(dev, this_attr->address, &val); + if (ret) + return ret; + + return sprintf(buf, "%u\n", val & 0x0FFF); +} + +static ssize_t adis16220_read_16bit(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + ssize_t ret; + + /* Take the iio_dev status lock */ + mutex_lock(&indio_dev->mlock); + ret = adis16220_spi_read_signed(dev, attr, buf, 16); + mutex_unlock(&indio_dev->mlock); + + return ret; +} + +static ssize_t adis16220_write_16bit(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t len) +{ + struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); + int ret; + long val; + + ret = strict_strtol(buf, 10, &val); + if (ret) + goto error_ret; + ret = adis16220_spi_write_reg_16(dev, this_attr->address, val); + +error_ret: + return ret ? ret : len; +} + +static int adis16220_capture(struct device *dev) +{ + int ret; + ret = adis16220_spi_write_reg_16(dev, + ADIS16220_GLOB_CMD, + 0xBF08); /* initiates a manual data capture */ + if (ret) + dev_err(dev, "problem beginning capture"); + + msleep(10); /* delay for capture to finish */ + + return ret; +} + +static int adis16220_reset(struct device *dev) +{ + int ret; + ret = adis16220_spi_write_reg_8(dev, + ADIS16220_GLOB_CMD, + ADIS16220_GLOB_CMD_SW_RESET); + if (ret) + dev_err(dev, "problem resetting device"); + + return ret; +} + +static ssize_t adis16220_write_reset(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + if (len < 1) + return -1; + switch (buf[0]) { + case '1': + case 'y': + case 'Y': + return adis16220_reset(dev) == 0 ? len : -EIO; + } + return -1; +} + +static ssize_t adis16220_write_capture(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + if (len < 1) + return -1; + switch (buf[0]) { + case '1': + case 'y': + case 'Y': + return adis16220_capture(dev) == 0 ? len : -EIO; + } + return -1; +} + +static int adis16220_check_status(struct device *dev) +{ + u16 status; + int ret; + + ret = adis16220_spi_read_reg_16(dev, ADIS16220_DIAG_STAT, &status); + + if (ret < 0) { + dev_err(dev, "Reading status failed\n"); + goto error_ret; + } + ret = status & 0x7F; + + if (status & ADIS16220_DIAG_STAT_VIOLATION) + dev_err(dev, "Capture period violation/interruption\n"); + if (status & ADIS16220_DIAG_STAT_SPI_FAIL) + dev_err(dev, "SPI failure\n"); + if (status & ADIS16220_DIAG_STAT_FLASH_UPT) + dev_err(dev, "Flash update failed\n"); + if (status & ADIS16220_DIAG_STAT_POWER_HIGH) + dev_err(dev, "Power supply above 5.25V\n"); + if (status & ADIS16220_DIAG_STAT_POWER_LOW) + dev_err(dev, "Power supply below 4.75V\n"); + +error_ret: + return ret; +} + +static int adis16220_self_test(struct device *dev) +{ + int ret; + ret = adis16220_spi_write_reg_16(dev, + ADIS16220_MSC_CTRL, + ADIS16220_MSC_CTRL_SELF_TEST_EN); + if (ret) { + dev_err(dev, "problem starting self test"); + goto err_ret; + } + + adis16220_check_status(dev); + +err_ret: + return ret; +} + +static int adis16220_initial_setup(struct adis16220_state *st) +{ + int ret; + struct device *dev = &st->indio_dev->dev; + + /* Do self test */ + ret = adis16220_self_test(dev); + if (ret) { + dev_err(dev, "self test failure"); + goto err_ret; + } + + /* Read status register to check the result */ + ret = adis16220_check_status(dev); + if (ret) { + adis16220_reset(dev); + dev_err(dev, "device not playing ball -> reset"); + msleep(ADIS16220_STARTUP_DELAY); + ret = adis16220_check_status(dev); + if (ret) { + dev_err(dev, "giving up"); + goto err_ret; + } + } + + printk(KERN_INFO DRIVER_NAME ": at CS%d (irq %d)\n", + st->us->chip_select, st->us->irq); + +err_ret: + return ret; +} + +static ssize_t adis16220_capture_buffer_read(struct adis16220_state *st, + char *buf, + loff_t off, + size_t count, + int addr) +{ + struct spi_message msg; + struct spi_transfer xfers[] = { + { + .tx_buf = st->tx, + .bits_per_word = 8, + .len = 2, + .cs_change = 1, + .delay_usecs = 25, + }, { + .tx_buf = st->tx, + .rx_buf = st->rx, + .bits_per_word = 8, + .cs_change = 1, + .delay_usecs = 25, + }, + }; + int ret; + int i; + + if (unlikely(!count)) + return count; + + if ((off >= ADIS16220_CAPTURE_SIZE) || (count & 1) || (off & 1)) + return -EINVAL; + + if (off + count > ADIS16220_CAPTURE_SIZE) + count = ADIS16220_CAPTURE_SIZE - off; + + /* write the begin position of capture buffer */ + ret = adis16220_spi_write_reg_16(&st->indio_dev->dev, + ADIS16220_CAPT_PNTR, + off > 1); + if (ret) + return -EIO; + + /* read count/2 values from capture buffer */ + mutex_lock(&st->buf_lock); + + for (i = 0; i < count; i += 2) { + st->tx[i] = ADIS16220_READ_REG(addr); + st->tx[i + 1] = 0; + } + xfers[1].len = count; + + spi_message_init(&msg); + spi_message_add_tail(&xfers[0], &msg); + spi_message_add_tail(&xfers[1], &msg); + ret = spi_sync(st->us, &msg); + if (ret) { + + mutex_unlock(&st->buf_lock); + return -EIO; + } + + memcpy(buf, st->rx, count); + + mutex_unlock(&st->buf_lock); + return count; +} + +static ssize_t adis16220_accel_bin_read(struct kobject *kobj, + struct bin_attribute *attr, + char *buf, + loff_t off, + size_t count) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct adis16220_state *st = iio_dev_get_devdata(indio_dev); + + return adis16220_capture_buffer_read(st, buf, + off, count, + ADIS16220_CAPT_BUFA); +} + +static struct bin_attribute accel_bin = { + .attr = { + .name = "accel_bin", + .mode = S_IRUGO, + }, + .read = adis16220_accel_bin_read, + .size = ADIS16220_CAPTURE_SIZE, +}; + +static ssize_t adis16220_adc1_bin_read(struct kobject *kobj, + struct bin_attribute *attr, + char *buf, loff_t off, + size_t count) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct adis16220_state *st = iio_dev_get_devdata(indio_dev); + + return adis16220_capture_buffer_read(st, buf, + off, count, + ADIS16220_CAPT_BUF1); +} + +static struct bin_attribute adc1_bin = { + .attr = { + .name = "in0_bin", + .mode = S_IRUGO, + }, + .read = adis16220_adc1_bin_read, + .size = ADIS16220_CAPTURE_SIZE, +}; + +static ssize_t adis16220_adc2_bin_read(struct kobject *kobj, + struct bin_attribute *attr, + char *buf, loff_t off, + size_t count) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct adis16220_state *st = iio_dev_get_devdata(indio_dev); + + return adis16220_capture_buffer_read(st, buf, + off, count, + ADIS16220_CAPT_BUF2); +} + + +static struct bin_attribute adc2_bin = { + .attr = { + .name = "in1_bin", + .mode = S_IRUGO, + }, + .read = adis16220_adc2_bin_read, + .size = ADIS16220_CAPTURE_SIZE, +}; + +static IIO_DEV_ATTR_IN_NAMED_RAW(supply, adis16220_read_12bit_unsigned, + ADIS16220_CAPT_SUPPLY); +static IIO_CONST_ATTR(in_supply_scale, "0.0012207"); +static IIO_DEV_ATTR_ACCEL(adis16220_read_16bit, ADIS16220_CAPT_BUFA); +static IIO_DEVICE_ATTR(accel_peak_raw, S_IRUGO, adis16220_read_16bit, + NULL, ADIS16220_CAPT_PEAKA); +static IIO_DEV_ATTR_ACCEL_OFFSET(S_IWUSR | S_IRUGO, + adis16220_read_16bit, + adis16220_write_16bit, + ADIS16220_ACCL_NULL); +static IIO_DEV_ATTR_TEMP_RAW(adis16220_read_12bit_unsigned); +static IIO_CONST_ATTR(temp_offset, "25"); +static IIO_CONST_ATTR(temp_scale, "-0.47"); + +static IIO_DEV_ATTR_IN_RAW(0, adis16220_read_16bit, ADIS16220_CAPT_BUF1); +static IIO_DEV_ATTR_IN_RAW(1, adis16220_read_16bit, ADIS16220_CAPT_BUF2); + +static IIO_DEVICE_ATTR(reset, S_IWUSR, NULL, + adis16220_write_reset, 0); + +#define IIO_DEV_ATTR_CAPTURE(_store) \ + IIO_DEVICE_ATTR(capture, S_IWUGO, NULL, _store, 0) + +static IIO_DEV_ATTR_CAPTURE(adis16220_write_capture); + +#define IIO_DEV_ATTR_CAPTURE_COUNT(_mode, _show, _store, _addr) \ + IIO_DEVICE_ATTR(capture_count, _mode, _show, _store, _addr) + +static IIO_DEV_ATTR_CAPTURE_COUNT(S_IWUSR | S_IRUGO, + adis16220_read_16bit, + adis16220_write_16bit, + ADIS16220_CAPT_PNTR); + +static IIO_CONST_ATTR_AVAIL_SAMP_FREQ("100200"); + +static IIO_CONST_ATTR(name, "adis16220"); + +static struct attribute *adis16220_attributes[] = { + &iio_dev_attr_in_supply_raw.dev_attr.attr, + &iio_const_attr_in_supply_scale.dev_attr.attr, + &iio_dev_attr_accel_raw.dev_attr.attr, + &iio_dev_attr_accel_offset.dev_attr.attr, + &iio_dev_attr_accel_peak_raw.dev_attr.attr, + &iio_dev_attr_temp_raw.dev_attr.attr, + &iio_dev_attr_in0_raw.dev_attr.attr, + &iio_dev_attr_in1_raw.dev_attr.attr, + &iio_const_attr_temp_offset.dev_attr.attr, + &iio_const_attr_temp_scale.dev_attr.attr, + &iio_const_attr_available_sampling_frequency.dev_attr.attr, + &iio_dev_attr_reset.dev_attr.attr, + &iio_dev_attr_capture.dev_attr.attr, + &iio_dev_attr_capture_count.dev_attr.attr, + &iio_const_attr_name.dev_attr.attr, + NULL +}; + +static const struct attribute_group adis16220_attribute_group = { + .attrs = adis16220_attributes, +}; + +static int __devinit adis16220_probe(struct spi_device *spi) +{ + int ret, regdone = 0; + struct adis16220_state *st = kzalloc(sizeof *st, GFP_KERNEL); + if (!st) { + ret = -ENOMEM; + goto error_ret; + } + /* this is only used for removal purposes */ + spi_set_drvdata(spi, st); + + /* Allocate the comms buffers */ + st->rx = kzalloc(sizeof(*st->rx)*ADIS16220_MAX_RX, GFP_KERNEL); + if (st->rx == NULL) { + ret = -ENOMEM; + goto error_free_st; + } + st->tx = kzalloc(sizeof(*st->tx)*ADIS16220_MAX_TX, GFP_KERNEL); + if (st->tx == NULL) { + ret = -ENOMEM; + goto error_free_rx; + } + st->us = spi; + mutex_init(&st->buf_lock); + /* setup the industrialio driver allocated elements */ + st->indio_dev = iio_allocate_device(); + if (st->indio_dev == NULL) { + ret = -ENOMEM; + goto error_free_tx; + } + + st->indio_dev->dev.parent = &spi->dev; + st->indio_dev->attrs = &adis16220_attribute_group; + st->indio_dev->dev_data = (void *)(st); + st->indio_dev->driver_module = THIS_MODULE; + st->indio_dev->modes = INDIO_DIRECT_MODE; + + ret = iio_device_register(st->indio_dev); + if (ret) + goto error_free_dev; + regdone = 1; + + ret = sysfs_create_bin_file(&st->indio_dev->dev.kobj, &accel_bin); + if (ret) + goto error_free_dev; + + ret = sysfs_create_bin_file(&st->indio_dev->dev.kobj, &adc1_bin); + if (ret) + goto error_rm_accel_bin; + + ret = sysfs_create_bin_file(&st->indio_dev->dev.kobj, &adc2_bin); + if (ret) + goto error_rm_adc1_bin; + + /* Get the device into a sane initial state */ + ret = adis16220_initial_setup(st); + if (ret) + goto error_rm_adc2_bin; + return 0; + +error_rm_adc2_bin: + sysfs_remove_bin_file(&st->indio_dev->dev.kobj, &adc2_bin); +error_rm_adc1_bin: + sysfs_remove_bin_file(&st->indio_dev->dev.kobj, &adc1_bin); +error_rm_accel_bin: + sysfs_remove_bin_file(&st->indio_dev->dev.kobj, &accel_bin); +error_free_dev: + if (regdone) + iio_device_unregister(st->indio_dev); + else + iio_free_device(st->indio_dev); +error_free_tx: + kfree(st->tx); +error_free_rx: + kfree(st->rx); +error_free_st: + kfree(st); +error_ret: + return ret; +} + +static int adis16220_remove(struct spi_device *spi) +{ + struct adis16220_state *st = spi_get_drvdata(spi); + struct iio_dev *indio_dev = st->indio_dev; + + flush_scheduled_work(); + + sysfs_remove_bin_file(&st->indio_dev->dev.kobj, &adc2_bin); + sysfs_remove_bin_file(&st->indio_dev->dev.kobj, &adc1_bin); + sysfs_remove_bin_file(&st->indio_dev->dev.kobj, &accel_bin); + iio_device_unregister(indio_dev); + kfree(st->tx); + kfree(st->rx); + kfree(st); + + return 0; +} + +static struct spi_driver adis16220_driver = { + .driver = { + .name = "adis16220", + .owner = THIS_MODULE, + }, + .probe = adis16220_probe, + .remove = __devexit_p(adis16220_remove), +}; + +static __init int adis16220_init(void) +{ + return spi_register_driver(&adis16220_driver); +} +module_init(adis16220_init); + +static __exit void adis16220_exit(void) +{ + spi_unregister_driver(&adis16220_driver); +} +module_exit(adis16220_exit); + +MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>"); +MODULE_DESCRIPTION("Analog Devices ADIS16220 Digital Vibration Sensor"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/iio/accel/adis16240.h b/drivers/staging/iio/accel/adis16240.h new file mode 100644 index 000000000000..dcff43c75235 --- /dev/null +++ b/drivers/staging/iio/accel/adis16240.h @@ -0,0 +1,218 @@ +#ifndef SPI_ADIS16240_H_ +#define SPI_ADIS16240_H_ + +#define ADIS16240_STARTUP_DELAY 220 /* ms */ + +#define ADIS16240_READ_REG(a) a +#define ADIS16240_WRITE_REG(a) ((a) | 0x80) + +/* Flash memory write count */ +#define ADIS16240_FLASH_CNT 0x00 +/* Output, power supply */ +#define ADIS16240_SUPPLY_OUT 0x02 +/* Output, x-axis accelerometer */ +#define ADIS16240_XACCL_OUT 0x04 +/* Output, y-axis accelerometer */ +#define ADIS16240_YACCL_OUT 0x06 +/* Output, z-axis accelerometer */ +#define ADIS16240_ZACCL_OUT 0x08 +/* Output, auxiliary ADC input */ +#define ADIS16240_AUX_ADC 0x0A +/* Output, temperature */ +#define ADIS16240_TEMP_OUT 0x0C +/* Output, x-axis acceleration peak */ +#define ADIS16240_XPEAK_OUT 0x0E +/* Output, y-axis acceleration peak */ +#define ADIS16240_YPEAK_OUT 0x10 +/* Output, z-axis acceleration peak */ +#define ADIS16240_ZPEAK_OUT 0x12 +/* Output, sum-of-squares acceleration peak */ +#define ADIS16240_XYZPEAK_OUT 0x14 +/* Output, Capture Buffer 1, X and Y acceleration */ +#define ADIS16240_CAPT_BUF1 0x16 +/* Output, Capture Buffer 2, Z acceleration */ +#define ADIS16240_CAPT_BUF2 0x18 +/* Diagnostic, error flags */ +#define ADIS16240_DIAG_STAT 0x1A +/* Diagnostic, event counter */ +#define ADIS16240_EVNT_CNTR 0x1C +/* Diagnostic, check sum value from firmware test */ +#define ADIS16240_CHK_SUM 0x1E +/* Calibration, x-axis acceleration offset adjustment */ +#define ADIS16240_XACCL_OFF 0x20 +/* Calibration, y-axis acceleration offset adjustment */ +#define ADIS16240_YACCL_OFF 0x22 +/* Calibration, z-axis acceleration offset adjustment */ +#define ADIS16240_ZACCL_OFF 0x24 +/* Clock, hour and minute */ +#define ADIS16240_CLK_TIME 0x2E +/* Clock, month and day */ +#define ADIS16240_CLK_DATE 0x30 +/* Clock, year */ +#define ADIS16240_CLK_YEAR 0x32 +/* Wake-up setting, hour and minute */ +#define ADIS16240_WAKE_TIME 0x34 +/* Wake-up setting, month and day */ +#define ADIS16240_WAKE_DATE 0x36 +/* Alarm 1 amplitude threshold */ +#define ADIS16240_ALM_MAG1 0x38 +/* Alarm 2 amplitude threshold */ +#define ADIS16240_ALM_MAG2 0x3A +/* Alarm control */ +#define ADIS16240_ALM_CTRL 0x3C +/* Capture, external trigger control */ +#define ADIS16240_XTRIG_CTRL 0x3E +/* Capture, address pointer */ +#define ADIS16240_CAPT_PNTR 0x40 +/* Capture, configuration and control */ +#define ADIS16240_CAPT_CTRL 0x42 +/* General-purpose digital input/output control */ +#define ADIS16240_GPIO_CTRL 0x44 +/* Miscellaneous control */ +#define ADIS16240_MSC_CTRL 0x46 +/* Internal sample period (rate) control */ +#define ADIS16240_SMPL_PRD 0x48 +/* System command */ +#define ADIS16240_GLOB_CMD 0x4A + +#define ADIS16240_OUTPUTS 6 + +/* MSC_CTRL */ +/* Enables sum-of-squares output (XYZPEAK_OUT) */ +#define ADIS16240_MSC_CTRL_XYZPEAK_OUT_EN (1 << 15) +/* Enables peak tracking output (XPEAK_OUT, YPEAK_OUT, and ZPEAK_OUT) */ +#define ADIS16240_MSC_CTRL_X_Y_ZPEAK_OUT_EN (1 << 14) +/* Self-test enable: 1 = apply electrostatic force, 0 = disabled */ +#define ADIS16240_MSC_CTRL_SELF_TEST_EN (1 << 8) +/* Data-ready enable: 1 = enabled, 0 = disabled */ +#define ADIS16240_MSC_CTRL_DATA_RDY_EN (1 << 2) +/* Data-ready polarity: 1 = active high, 0 = active low */ +#define ADIS16240_MSC_CTRL_ACTIVE_HIGH (1 << 1) +/* Data-ready line selection: 1 = DIO2, 0 = DIO1 */ +#define ADIS16240_MSC_CTRL_DATA_RDY_DIO2 (1 << 0) + +/* DIAG_STAT */ +/* Alarm 2 status: 1 = alarm active, 0 = alarm inactive */ +#define ADIS16240_DIAG_STAT_ALARM2 (1<<9) +/* Alarm 1 status: 1 = alarm active, 0 = alarm inactive */ +#define ADIS16240_DIAG_STAT_ALARM1 (1<<8) +/* Capture buffer full: 1 = capture buffer is full */ +#define ADIS16240_DIAG_STAT_CPT_BUF_FUL (1<<7) +/* Flash test, checksum flag: 1 = mismatch, 0 = match */ +#define ADIS16240_DIAG_STAT_CHKSUM (1<<6) +/* Power-on, self-test flag: 1 = failure, 0 = pass */ +#define ADIS16240_DIAG_STAT_PWRON_FAIL (1<<5) +/* Power-on self-test: 1 = in-progress, 0 = complete */ +#define ADIS16240_DIAG_STAT_PWRON_BUSY (1<<4) +/* SPI communications failure */ +#define ADIS16240_DIAG_STAT_SPI_FAIL (1<<3) +/* Flash update failure */ +#define ADIS16240_DIAG_STAT_FLASH_UPT (1<<2) +/* Power supply above 3.625 V */ +#define ADIS16240_DIAG_STAT_POWER_HIGH (1<<1) + /* Power supply below 3.15 V */ +#define ADIS16240_DIAG_STAT_POWER_LOW (1<<0) + +/* GLOB_CMD */ +#define ADIS16240_GLOB_CMD_RESUME (1<<8) +#define ADIS16240_GLOB_CMD_SW_RESET (1<<7) +#define ADIS16240_GLOB_CMD_STANDBY (1<<2) + +#define ADIS16240_ERROR_ACTIVE (1<<14) + +#define ADIS16240_MAX_TX 24 +#define ADIS16240_MAX_RX 24 + +/** + * struct adis16240_state - device instance specific data + * @us: actual spi_device + * @work_trigger_to_ring: bh for triggered event handling + * @work_cont_thresh: CLEAN + * @inter: used to check if new interrupt has been triggered + * @last_timestamp: passing timestamp from th to bh of interrupt handler + * @indio_dev: industrial I/O device structure + * @trig: data ready trigger registered with iio + * @tx: transmit buffer + * @rx: recieve buffer + * @buf_lock: mutex to protect tx and rx + **/ +struct adis16240_state { + struct spi_device *us; + struct work_struct work_trigger_to_ring; + struct iio_work_cont work_cont_thresh; + s64 last_timestamp; + struct iio_dev *indio_dev; + struct iio_trigger *trig; + u8 *tx; + u8 *rx; + struct mutex buf_lock; +}; + +int adis16240_set_irq(struct device *dev, bool enable); + +#ifdef CONFIG_IIO_RING_BUFFER +/* At the moment triggers are only used for ring buffer + * filling. This may change! + */ + +enum adis16240_scan { + ADIS16240_SCAN_SUPPLY, + ADIS16240_SCAN_ACC_X, + ADIS16240_SCAN_ACC_Y, + ADIS16240_SCAN_ACC_Z, + ADIS16240_SCAN_AUX_ADC, + ADIS16240_SCAN_TEMP, +}; + +void adis16240_remove_trigger(struct iio_dev *indio_dev); +int adis16240_probe_trigger(struct iio_dev *indio_dev); + +ssize_t adis16240_read_data_from_ring(struct device *dev, + struct device_attribute *attr, + char *buf); + + +int adis16240_configure_ring(struct iio_dev *indio_dev); +void adis16240_unconfigure_ring(struct iio_dev *indio_dev); + +int adis16240_initialize_ring(struct iio_ring_buffer *ring); +void adis16240_uninitialize_ring(struct iio_ring_buffer *ring); +#else /* CONFIG_IIO_RING_BUFFER */ + +static inline void adis16240_remove_trigger(struct iio_dev *indio_dev) +{ +} + +static inline int adis16240_probe_trigger(struct iio_dev *indio_dev) +{ + return 0; +} + +static inline ssize_t +adis16240_read_data_from_ring(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return 0; +} + +static int adis16240_configure_ring(struct iio_dev *indio_dev) +{ + return 0; +} + +static inline void adis16240_unconfigure_ring(struct iio_dev *indio_dev) +{ +} + +static inline int adis16240_initialize_ring(struct iio_ring_buffer *ring) +{ + return 0; +} + +static inline void adis16240_uninitialize_ring(struct iio_ring_buffer *ring) +{ +} + +#endif /* CONFIG_IIO_RING_BUFFER */ +#endif /* SPI_ADIS16240_H_ */ diff --git a/drivers/staging/iio/accel/adis16240_core.c b/drivers/staging/iio/accel/adis16240_core.c new file mode 100644 index 000000000000..54fd6d77412f --- /dev/null +++ b/drivers/staging/iio/accel/adis16240_core.c @@ -0,0 +1,599 @@ +/* + * ADIS16240 Programmable Impact Sensor and Recorder driver + * + * Copyright 2010 Analog Devices Inc. + * + * Licensed under the GPL-2 or later. + */ + +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/gpio.h> +#include <linux/delay.h> +#include <linux/mutex.h> +#include <linux/device.h> +#include <linux/kernel.h> +#include <linux/spi/spi.h> + +#include <linux/sysfs.h> +#include <linux/list.h> + +#include "../iio.h" +#include "../sysfs.h" +#include "accel.h" +#include "../adc/adc.h" + +#include "adis16240.h" + +#define DRIVER_NAME "adis16240" + +static int adis16240_check_status(struct device *dev); + +/** + * adis16240_spi_write_reg_8() - write single byte to a register + * @dev: device associated with child of actual device (iio_dev or iio_trig) + * @reg_address: the address of the register to be written + * @val: the value to write + **/ +static int adis16240_spi_write_reg_8(struct device *dev, + u8 reg_address, + u8 val) +{ + int ret; + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct adis16240_state *st = iio_dev_get_devdata(indio_dev); + + mutex_lock(&st->buf_lock); + st->tx[0] = ADIS16240_WRITE_REG(reg_address); + st->tx[1] = val; + + ret = spi_write(st->us, st->tx, 2); + mutex_unlock(&st->buf_lock); + + return ret; +} + +/** + * adis16240_spi_write_reg_16() - write 2 bytes to a pair of registers + * @dev: device associated with child of actual device (iio_dev or iio_trig) + * @reg_address: the address of the lower of the two registers. Second register + * is assumed to have address one greater. + * @val: value to be written + **/ +static int adis16240_spi_write_reg_16(struct device *dev, + u8 lower_reg_address, + u16 value) +{ + int ret; + struct spi_message msg; + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct adis16240_state *st = iio_dev_get_devdata(indio_dev); + struct spi_transfer xfers[] = { + { + .tx_buf = st->tx, + .bits_per_word = 8, + .len = 2, + .cs_change = 1, + .delay_usecs = 25, + }, { + .tx_buf = st->tx + 2, + .bits_per_word = 8, + .len = 2, + .cs_change = 1, + .delay_usecs = 25, + }, + }; + + mutex_lock(&st->buf_lock); + st->tx[0] = ADIS16240_WRITE_REG(lower_reg_address); + st->tx[1] = value & 0xFF; + st->tx[2] = ADIS16240_WRITE_REG(lower_reg_address + 1); + st->tx[3] = (value >> 8) & 0xFF; + + spi_message_init(&msg); + spi_message_add_tail(&xfers[0], &msg); + spi_message_add_tail(&xfers[1], &msg); + ret = spi_sync(st->us, &msg); + mutex_unlock(&st->buf_lock); + + return ret; +} + +/** + * adis16240_spi_read_reg_16() - read 2 bytes from a 16-bit register + * @dev: device associated with child of actual device (iio_dev or iio_trig) + * @reg_address: the address of the lower of the two registers. Second register + * is assumed to have address one greater. + * @val: somewhere to pass back the value read + **/ +static int adis16240_spi_read_reg_16(struct device *dev, + u8 lower_reg_address, + u16 *val) +{ + struct spi_message msg; + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct adis16240_state *st = iio_dev_get_devdata(indio_dev); + int ret; + struct spi_transfer xfers[] = { + { + .tx_buf = st->tx, + .bits_per_word = 8, + .len = 2, + .cs_change = 1, + .delay_usecs = 25, + }, { + .rx_buf = st->rx, + .bits_per_word = 8, + .len = 2, + .cs_change = 1, + .delay_usecs = 25, + }, + }; + + mutex_lock(&st->buf_lock); + st->tx[0] = ADIS16240_READ_REG(lower_reg_address); + st->tx[1] = 0; + st->tx[2] = 0; + st->tx[3] = 0; + + spi_message_init(&msg); + spi_message_add_tail(&xfers[0], &msg); + spi_message_add_tail(&xfers[1], &msg); + ret = spi_sync(st->us, &msg); + if (ret) { + dev_err(&st->us->dev, + "problem when reading 16 bit register 0x%02X", + lower_reg_address); + goto error_ret; + } + *val = (st->rx[0] << 8) | st->rx[1]; + +error_ret: + mutex_unlock(&st->buf_lock); + return ret; +} + +static ssize_t adis16240_spi_read_signed(struct device *dev, + struct device_attribute *attr, + char *buf, + unsigned bits) +{ + int ret; + s16 val = 0; + unsigned shift = 16 - bits; + struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); + + ret = adis16240_spi_read_reg_16(dev, this_attr->address, (u16 *)&val); + if (ret) + return ret; + + if (val & ADIS16240_ERROR_ACTIVE) + adis16240_check_status(dev); + + val = ((s16)(val << shift) >> shift); + return sprintf(buf, "%d\n", val); +} + +static ssize_t adis16240_read_10bit_unsigned(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int ret; + u16 val = 0; + struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); + + ret = adis16240_spi_read_reg_16(dev, this_attr->address, &val); + if (ret) + return ret; + + if (val & ADIS16240_ERROR_ACTIVE) + adis16240_check_status(dev); + + return sprintf(buf, "%u\n", val & 0x03FF); +} + +static ssize_t adis16240_read_10bit_signed(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + ssize_t ret; + + /* Take the iio_dev status lock */ + mutex_lock(&indio_dev->mlock); + ret = adis16240_spi_read_signed(dev, attr, buf, 10); + mutex_unlock(&indio_dev->mlock); + + return ret; +} + +static ssize_t adis16240_read_12bit_signed(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + ssize_t ret; + + /* Take the iio_dev status lock */ + mutex_lock(&indio_dev->mlock); + ret = adis16240_spi_read_signed(dev, attr, buf, 12); + mutex_unlock(&indio_dev->mlock); + + return ret; +} + +static ssize_t adis16240_write_16bit(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t len) +{ + struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); + int ret; + long val; + + ret = strict_strtol(buf, 10, &val); + if (ret) + goto error_ret; + ret = adis16240_spi_write_reg_16(dev, this_attr->address, val); + +error_ret: + return ret ? ret : len; +} + +static int adis16240_reset(struct device *dev) +{ + int ret; + ret = adis16240_spi_write_reg_8(dev, + ADIS16240_GLOB_CMD, + ADIS16240_GLOB_CMD_SW_RESET); + if (ret) + dev_err(dev, "problem resetting device"); + + return ret; +} + +static ssize_t adis16240_write_reset(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + if (len < 1) + return -EINVAL; + switch (buf[0]) { + case '1': + case 'y': + case 'Y': + return adis16240_reset(dev); + } + return -EINVAL; +} + +int adis16240_set_irq(struct device *dev, bool enable) +{ + int ret = 0; + u16 msc; + + ret = adis16240_spi_read_reg_16(dev, ADIS16240_MSC_CTRL, &msc); + if (ret) + goto error_ret; + + msc |= ADIS16240_MSC_CTRL_ACTIVE_HIGH; + msc &= ~ADIS16240_MSC_CTRL_DATA_RDY_DIO2; + if (enable) + msc |= ADIS16240_MSC_CTRL_DATA_RDY_EN; + else + msc &= ~ADIS16240_MSC_CTRL_DATA_RDY_EN; + + ret = adis16240_spi_write_reg_16(dev, ADIS16240_MSC_CTRL, msc); + +error_ret: + return ret; +} + +static int adis16240_self_test(struct device *dev) +{ + int ret; + ret = adis16240_spi_write_reg_16(dev, + ADIS16240_MSC_CTRL, + ADIS16240_MSC_CTRL_SELF_TEST_EN); + if (ret) { + dev_err(dev, "problem starting self test"); + goto err_ret; + } + + msleep(ADIS16240_STARTUP_DELAY); + + adis16240_check_status(dev); + +err_ret: + return ret; +} + +static int adis16240_check_status(struct device *dev) +{ + u16 status; + int ret; + + ret = adis16240_spi_read_reg_16(dev, ADIS16240_DIAG_STAT, &status); + + if (ret < 0) { + dev_err(dev, "Reading status failed\n"); + goto error_ret; + } + + ret = status & 0x2F; + if (status & ADIS16240_DIAG_STAT_PWRON_FAIL) + dev_err(dev, "Power-on, self-test fail\n"); + if (status & ADIS16240_DIAG_STAT_SPI_FAIL) + dev_err(dev, "SPI failure\n"); + if (status & ADIS16240_DIAG_STAT_FLASH_UPT) + dev_err(dev, "Flash update failed\n"); + if (status & ADIS16240_DIAG_STAT_POWER_HIGH) + dev_err(dev, "Power supply above 3.625V\n"); + if (status & ADIS16240_DIAG_STAT_POWER_LOW) + dev_err(dev, "Power supply below 2.225V\n"); + +error_ret: + return ret; +} + +static int adis16240_initial_setup(struct adis16240_state *st) +{ + int ret; + struct device *dev = &st->indio_dev->dev; + + /* Disable IRQ */ + ret = adis16240_set_irq(dev, false); + if (ret) { + dev_err(dev, "disable irq failed"); + goto err_ret; + } + + /* Do self test */ + ret = adis16240_self_test(dev); + if (ret) { + dev_err(dev, "self test failure"); + goto err_ret; + } + + /* Read status register to check the result */ + ret = adis16240_check_status(dev); + if (ret) { + adis16240_reset(dev); + dev_err(dev, "device not playing ball -> reset"); + msleep(ADIS16240_STARTUP_DELAY); + ret = adis16240_check_status(dev); + if (ret) { + dev_err(dev, "giving up"); + goto err_ret; + } + } + + printk(KERN_INFO DRIVER_NAME ": at CS%d (irq %d)\n", + st->us->chip_select, st->us->irq); + +err_ret: + return ret; +} + +static IIO_DEV_ATTR_IN_NAMED_RAW(supply, adis16240_read_10bit_unsigned, + ADIS16240_SUPPLY_OUT); +static IIO_DEV_ATTR_IN_RAW(0, adis16240_read_10bit_signed, + ADIS16240_AUX_ADC); +static IIO_CONST_ATTR(in_supply_scale, "0.00488"); +static IIO_DEV_ATTR_ACCEL_X(adis16240_read_10bit_signed, + ADIS16240_XACCL_OUT); +static IIO_DEVICE_ATTR(accel_x_peak_raw, S_IRUGO, + adis16240_read_10bit_signed, NULL, + ADIS16240_XPEAK_OUT); +static IIO_DEV_ATTR_ACCEL_Y(adis16240_read_10bit_signed, + ADIS16240_YACCL_OUT); +static IIO_DEVICE_ATTR(accel_y_peak_raw, S_IRUGO, + adis16240_read_10bit_signed, NULL, + ADIS16240_YPEAK_OUT); +static IIO_DEV_ATTR_ACCEL_Z(adis16240_read_10bit_signed, + ADIS16240_ZACCL_OUT); +static IIO_DEVICE_ATTR(accel_z_peak_raw, S_IRUGO, + adis16240_read_10bit_signed, NULL, + ADIS16240_ZPEAK_OUT); + +static IIO_DEVICE_ATTR(accel_xyz_squared_peak_raw, S_IRUGO, + adis16240_read_12bit_signed, NULL, + ADIS16240_XYZPEAK_OUT); +static IIO_DEV_ATTR_ACCEL_X_OFFSET(S_IWUSR | S_IRUGO, + adis16240_read_10bit_signed, + adis16240_write_16bit, + ADIS16240_XACCL_OFF); +static IIO_DEV_ATTR_ACCEL_Y_OFFSET(S_IWUSR | S_IRUGO, + adis16240_read_10bit_signed, + adis16240_write_16bit, + ADIS16240_YACCL_OFF); +static IIO_DEV_ATTR_ACCEL_Z_OFFSET(S_IWUSR | S_IRUGO, + adis16240_read_10bit_signed, + adis16240_write_16bit, + ADIS16240_ZACCL_OFF); +static IIO_DEV_ATTR_TEMP_RAW(adis16240_read_10bit_unsigned); +static IIO_CONST_ATTR(temp_scale, "0.244"); + +static IIO_DEVICE_ATTR(reset, S_IWUSR, NULL, adis16240_write_reset, 0); + +static IIO_CONST_ATTR_AVAIL_SAMP_FREQ("4096"); + +static IIO_CONST_ATTR(name, "adis16240"); + +static struct attribute *adis16240_event_attributes[] = { + NULL +}; + +static struct attribute_group adis16240_event_attribute_group = { + .attrs = adis16240_event_attributes, +}; + +static struct attribute *adis16240_attributes[] = { + &iio_dev_attr_in_supply_raw.dev_attr.attr, + &iio_const_attr_in_supply_scale.dev_attr.attr, + &iio_dev_attr_in0_raw.dev_attr.attr, + &iio_dev_attr_accel_x_raw.dev_attr.attr, + &iio_dev_attr_accel_x_offset.dev_attr.attr, + &iio_dev_attr_accel_x_peak_raw.dev_attr.attr, + &iio_dev_attr_accel_y_raw.dev_attr.attr, + &iio_dev_attr_accel_y_offset.dev_attr.attr, + &iio_dev_attr_accel_y_peak_raw.dev_attr.attr, + &iio_dev_attr_accel_z_raw.dev_attr.attr, + &iio_dev_attr_accel_z_offset.dev_attr.attr, + &iio_dev_attr_accel_z_peak_raw.dev_attr.attr, + &iio_dev_attr_accel_xyz_squared_peak_raw.dev_attr.attr, + &iio_dev_attr_temp_raw.dev_attr.attr, + &iio_const_attr_temp_scale.dev_attr.attr, + &iio_const_attr_available_sampling_frequency.dev_attr.attr, + &iio_dev_attr_reset.dev_attr.attr, + &iio_const_attr_name.dev_attr.attr, + NULL +}; + +static const struct attribute_group adis16240_attribute_group = { + .attrs = adis16240_attributes, +}; + +static int __devinit adis16240_probe(struct spi_device *spi) +{ + int ret, regdone = 0; + struct adis16240_state *st = kzalloc(sizeof *st, GFP_KERNEL); + if (!st) { + ret = -ENOMEM; + goto error_ret; + } + /* this is only used for removal purposes */ + spi_set_drvdata(spi, st); + + /* Allocate the comms buffers */ + st->rx = kzalloc(sizeof(*st->rx)*ADIS16240_MAX_RX, GFP_KERNEL); + if (st->rx == NULL) { + ret = -ENOMEM; + goto error_free_st; + } + st->tx = kzalloc(sizeof(*st->tx)*ADIS16240_MAX_TX, GFP_KERNEL); + if (st->tx == NULL) { + ret = -ENOMEM; + goto error_free_rx; + } + st->us = spi; + mutex_init(&st->buf_lock); + /* setup the industrialio driver allocated elements */ + st->indio_dev = iio_allocate_device(); + if (st->indio_dev == NULL) { + ret = -ENOMEM; + goto error_free_tx; + } + + st->indio_dev->dev.parent = &spi->dev; + st->indio_dev->num_interrupt_lines = 1; + st->indio_dev->event_attrs = &adis16240_event_attribute_group; + st->indio_dev->attrs = &adis16240_attribute_group; + st->indio_dev->dev_data = (void *)(st); + st->indio_dev->driver_module = THIS_MODULE; + st->indio_dev->modes = INDIO_DIRECT_MODE; + + ret = adis16240_configure_ring(st->indio_dev); + if (ret) + goto error_free_dev; + + ret = iio_device_register(st->indio_dev); + if (ret) + goto error_unreg_ring_funcs; + regdone = 1; + + ret = adis16240_initialize_ring(st->indio_dev->ring); + if (ret) { + printk(KERN_ERR "failed to initialize the ring\n"); + goto error_unreg_ring_funcs; + } + + if (spi->irq) { + ret = iio_register_interrupt_line(spi->irq, + st->indio_dev, + 0, + IRQF_TRIGGER_RISING, + "adis16240"); + if (ret) + goto error_uninitialize_ring; + + ret = adis16240_probe_trigger(st->indio_dev); + if (ret) + goto error_unregister_line; + } + + /* Get the device into a sane initial state */ + ret = adis16240_initial_setup(st); + if (ret) + goto error_remove_trigger; + return 0; + +error_remove_trigger: + adis16240_remove_trigger(st->indio_dev); +error_unregister_line: + if (spi->irq) + iio_unregister_interrupt_line(st->indio_dev, 0); +error_uninitialize_ring: + adis16240_uninitialize_ring(st->indio_dev->ring); +error_unreg_ring_funcs: + adis16240_unconfigure_ring(st->indio_dev); +error_free_dev: + if (regdone) + iio_device_unregister(st->indio_dev); + else + iio_free_device(st->indio_dev); +error_free_tx: + kfree(st->tx); +error_free_rx: + kfree(st->rx); +error_free_st: + kfree(st); +error_ret: + return ret; +} + +static int adis16240_remove(struct spi_device *spi) +{ + struct adis16240_state *st = spi_get_drvdata(spi); + struct iio_dev *indio_dev = st->indio_dev; + + flush_scheduled_work(); + + adis16240_remove_trigger(indio_dev); + if (spi->irq) + iio_unregister_interrupt_line(indio_dev, 0); + + adis16240_uninitialize_ring(indio_dev->ring); + iio_device_unregister(indio_dev); + adis16240_unconfigure_ring(indio_dev); + kfree(st->tx); + kfree(st->rx); + kfree(st); + + return 0; +} + +static struct spi_driver adis16240_driver = { + .driver = { + .name = "adis16240", + .owner = THIS_MODULE, + }, + .probe = adis16240_probe, + .remove = __devexit_p(adis16240_remove), +}; + +static __init int adis16240_init(void) +{ + return spi_register_driver(&adis16240_driver); +} +module_init(adis16240_init); + +static __exit void adis16240_exit(void) +{ + spi_unregister_driver(&adis16240_driver); +} +module_exit(adis16240_exit); + +MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>"); +MODULE_DESCRIPTION("Analog Devices Programmable Impact Sensor and Recorder"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/iio/accel/adis16240_ring.c b/drivers/staging/iio/accel/adis16240_ring.c new file mode 100644 index 000000000000..26b677bd84c0 --- /dev/null +++ b/drivers/staging/iio/accel/adis16240_ring.c @@ -0,0 +1,254 @@ +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/gpio.h> +#include <linux/workqueue.h> +#include <linux/mutex.h> +#include <linux/device.h> +#include <linux/kernel.h> +#include <linux/spi/spi.h> +#include <linux/sysfs.h> +#include <linux/list.h> + +#include "../iio.h" +#include "../sysfs.h" +#include "../ring_sw.h" +#include "accel.h" +#include "../trigger.h" +#include "adis16240.h" + +/** + * combine_8_to_16() utility function to munge to u8s into u16 + **/ +static inline u16 combine_8_to_16(u8 lower, u8 upper) +{ + u16 _lower = lower; + u16 _upper = upper; + return _lower | (_upper << 8); +} + +static IIO_SCAN_EL_C(supply, ADIS16240_SCAN_SUPPLY, IIO_UNSIGNED(10), + ADIS16240_SUPPLY_OUT, NULL); +static IIO_SCAN_EL_C(accel_x, ADIS16240_SCAN_ACC_X, IIO_SIGNED(10), + ADIS16240_XACCL_OUT, NULL); +static IIO_SCAN_EL_C(accel_y, ADIS16240_SCAN_ACC_Y, IIO_SIGNED(10), + ADIS16240_YACCL_OUT, NULL); +static IIO_SCAN_EL_C(accel_z, ADIS16240_SCAN_ACC_Z, IIO_SIGNED(10), + ADIS16240_ZACCL_OUT, NULL); +static IIO_SCAN_EL_C(aux_adc, ADIS16240_SCAN_AUX_ADC, IIO_UNSIGNED(10), + ADIS16240_AUX_ADC, NULL); +static IIO_SCAN_EL_C(temp, ADIS16240_SCAN_TEMP, IIO_UNSIGNED(10), + ADIS16240_TEMP_OUT, NULL); + +static IIO_SCAN_EL_TIMESTAMP(6); + +static struct attribute *adis16240_scan_el_attrs[] = { + &iio_scan_el_supply.dev_attr.attr, + &iio_scan_el_accel_x.dev_attr.attr, + &iio_scan_el_accel_y.dev_attr.attr, + &iio_scan_el_accel_z.dev_attr.attr, + &iio_scan_el_aux_adc.dev_attr.attr, + &iio_scan_el_temp.dev_attr.attr, + &iio_scan_el_timestamp.dev_attr.attr, + NULL, +}; + +static struct attribute_group adis16240_scan_el_group = { + .attrs = adis16240_scan_el_attrs, + .name = "scan_elements", +}; + +/** + * adis16240_poll_func_th() top half interrupt handler called by trigger + * @private_data: iio_dev + **/ +static void adis16240_poll_func_th(struct iio_dev *indio_dev) +{ + struct adis16240_state *st = iio_dev_get_devdata(indio_dev); + st->last_timestamp = indio_dev->trig->timestamp; + schedule_work(&st->work_trigger_to_ring); +} + +/** + * adis16240_read_ring_data() read data registers which will be placed into ring + * @dev: device associated with child of actual device (iio_dev or iio_trig) + * @rx: somewhere to pass back the value read + **/ +static int adis16240_read_ring_data(struct device *dev, u8 *rx) +{ + struct spi_message msg; + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct adis16240_state *st = iio_dev_get_devdata(indio_dev); + struct spi_transfer xfers[ADIS16240_OUTPUTS + 1]; + int ret; + int i; + + mutex_lock(&st->buf_lock); + + spi_message_init(&msg); + + memset(xfers, 0, sizeof(xfers)); + for (i = 0; i <= ADIS16240_OUTPUTS; i++) { + xfers[i].bits_per_word = 8; + xfers[i].cs_change = 1; + xfers[i].len = 2; + xfers[i].delay_usecs = 30; + xfers[i].tx_buf = st->tx + 2 * i; + st->tx[2 * i] + = ADIS16240_READ_REG(ADIS16240_SUPPLY_OUT + 2 * i); + st->tx[2 * i + 1] = 0; + if (i >= 1) + xfers[i].rx_buf = rx + 2 * (i - 1); + spi_message_add_tail(&xfers[i], &msg); + } + + ret = spi_sync(st->us, &msg); + if (ret) + dev_err(&st->us->dev, "problem when burst reading"); + + mutex_unlock(&st->buf_lock); + + return ret; +} + + +static void adis16240_trigger_bh_to_ring(struct work_struct *work_s) +{ + struct adis16240_state *st + = container_of(work_s, struct adis16240_state, + work_trigger_to_ring); + + int i = 0; + s16 *data; + size_t datasize = st->indio_dev + ->ring->access.get_bpd(st->indio_dev->ring); + + data = kmalloc(datasize , GFP_KERNEL); + if (data == NULL) { + dev_err(&st->us->dev, "memory alloc failed in ring bh"); + return; + } + + if (st->indio_dev->scan_count) + if (adis16240_read_ring_data(&st->indio_dev->dev, st->rx) >= 0) + for (; i < st->indio_dev->scan_count; i++) { + data[i] = combine_8_to_16(st->rx[i*2+1], + st->rx[i*2]); + } + + /* Guaranteed to be aligned with 8 byte boundary */ + if (st->indio_dev->scan_timestamp) + *((s64 *)(data + ((i + 3)/4)*4)) = st->last_timestamp; + + st->indio_dev->ring->access.store_to(st->indio_dev->ring, + (u8 *)data, + st->last_timestamp); + + iio_trigger_notify_done(st->indio_dev->trig); + kfree(data); + + return; +} + +static int adis16240_data_rdy_ring_preenable(struct iio_dev *indio_dev) +{ + size_t size; + dev_dbg(&indio_dev->dev, "%s\n", __func__); + /* Check if there are any scan elements enabled, if not fail*/ + if (!(indio_dev->scan_count || indio_dev->scan_timestamp)) + return -EINVAL; + + if (indio_dev->ring->access.set_bpd) { + if (indio_dev->scan_timestamp) + if (indio_dev->scan_count) + /* Timestamp (aligned sizeof(s64) and data */ + size = (((indio_dev->scan_count * sizeof(s16)) + + sizeof(s64) - 1) + & ~(sizeof(s64) - 1)) + + sizeof(s64); + else /* Timestamp only */ + size = sizeof(s64); + else /* Data only */ + size = indio_dev->scan_count*sizeof(s16); + indio_dev->ring->access.set_bpd(indio_dev->ring, size); + } + + return 0; +} + +static int adis16240_data_rdy_ring_postenable(struct iio_dev *indio_dev) +{ + return indio_dev->trig + ? iio_trigger_attach_poll_func(indio_dev->trig, + indio_dev->pollfunc) + : 0; +} + +static int adis16240_data_rdy_ring_predisable(struct iio_dev *indio_dev) +{ + return indio_dev->trig + ? iio_trigger_dettach_poll_func(indio_dev->trig, + indio_dev->pollfunc) + : 0; +} + +void adis16240_unconfigure_ring(struct iio_dev *indio_dev) +{ + kfree(indio_dev->pollfunc); + iio_sw_rb_free(indio_dev->ring); +} + +int adis16240_configure_ring(struct iio_dev *indio_dev) +{ + int ret = 0; + struct adis16240_state *st = indio_dev->dev_data; + struct iio_ring_buffer *ring; + INIT_WORK(&st->work_trigger_to_ring, adis16240_trigger_bh_to_ring); + /* Set default scan mode */ + + iio_scan_mask_set(indio_dev, iio_scan_el_supply.number); + iio_scan_mask_set(indio_dev, iio_scan_el_accel_x.number); + iio_scan_mask_set(indio_dev, iio_scan_el_accel_y.number); + iio_scan_mask_set(indio_dev, iio_scan_el_accel_z.number); + iio_scan_mask_set(indio_dev, iio_scan_el_temp.number); + iio_scan_mask_set(indio_dev, iio_scan_el_aux_adc.number); + indio_dev->scan_timestamp = true; + + indio_dev->scan_el_attrs = &adis16240_scan_el_group; + + ring = iio_sw_rb_allocate(indio_dev); + if (!ring) { + ret = -ENOMEM; + return ret; + } + indio_dev->ring = ring; + /* Effectively select the ring buffer implementation */ + iio_ring_sw_register_funcs(&ring->access); + ring->preenable = &adis16240_data_rdy_ring_preenable; + ring->postenable = &adis16240_data_rdy_ring_postenable; + ring->predisable = &adis16240_data_rdy_ring_predisable; + ring->owner = THIS_MODULE; + + indio_dev->pollfunc = kzalloc(sizeof(*indio_dev->pollfunc), GFP_KERNEL); + if (indio_dev->pollfunc == NULL) { + ret = -ENOMEM; + goto error_iio_sw_rb_free;; + } + indio_dev->pollfunc->poll_func_main = &adis16240_poll_func_th; + indio_dev->pollfunc->private_data = indio_dev; + indio_dev->modes |= INDIO_RING_TRIGGERED; + return 0; + +error_iio_sw_rb_free: + iio_sw_rb_free(indio_dev->ring); + return ret; +} + +int adis16240_initialize_ring(struct iio_ring_buffer *ring) +{ + return iio_ring_buffer_register(ring, 0); +} + +void adis16240_uninitialize_ring(struct iio_ring_buffer *ring) +{ + iio_ring_buffer_unregister(ring); +} diff --git a/drivers/staging/iio/accel/adis16240_trigger.c b/drivers/staging/iio/accel/adis16240_trigger.c new file mode 100644 index 000000000000..df1312e17f4b --- /dev/null +++ b/drivers/staging/iio/accel/adis16240_trigger.c @@ -0,0 +1,124 @@ +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/mutex.h> +#include <linux/device.h> +#include <linux/kernel.h> +#include <linux/sysfs.h> +#include <linux/list.h> +#include <linux/spi/spi.h> + +#include "../iio.h" +#include "../sysfs.h" +#include "../trigger.h" +#include "adis16240.h" + +/** + * adis16240_data_rdy_trig_poll() the event handler for the data rdy trig + **/ +static int adis16240_data_rdy_trig_poll(struct iio_dev *dev_info, + int index, + s64 timestamp, + int no_test) +{ + struct adis16240_state *st = iio_dev_get_devdata(dev_info); + struct iio_trigger *trig = st->trig; + + trig->timestamp = timestamp; + iio_trigger_poll(trig); + + return IRQ_HANDLED; +} + +IIO_EVENT_SH(data_rdy_trig, &adis16240_data_rdy_trig_poll); + +static DEVICE_ATTR(name, S_IRUGO, iio_trigger_read_name, NULL); + +static struct attribute *adis16240_trigger_attrs[] = { + &dev_attr_name.attr, + NULL, +}; + +static const struct attribute_group adis16240_trigger_attr_group = { + .attrs = adis16240_trigger_attrs, +}; + +/** + * adis16240_data_rdy_trigger_set_state() set datardy interrupt state + **/ +static int adis16240_data_rdy_trigger_set_state(struct iio_trigger *trig, + bool state) +{ + struct adis16240_state *st = trig->private_data; + struct iio_dev *indio_dev = st->indio_dev; + int ret = 0; + + dev_dbg(&indio_dev->dev, "%s (%d)\n", __func__, state); + ret = adis16240_set_irq(&st->indio_dev->dev, state); + if (state == false) { + iio_remove_event_from_list(&iio_event_data_rdy_trig, + &indio_dev->interrupts[0] + ->ev_list); + flush_scheduled_work(); + } else { + iio_add_event_to_list(&iio_event_data_rdy_trig, + &indio_dev->interrupts[0]->ev_list); + } + return ret; +} + +/** + * adis16240_trig_try_reen() try renabling irq for data rdy trigger + * @trig: the datardy trigger + **/ +static int adis16240_trig_try_reen(struct iio_trigger *trig) +{ + struct adis16240_state *st = trig->private_data; + enable_irq(st->us->irq); + return 0; +} + +int adis16240_probe_trigger(struct iio_dev *indio_dev) +{ + int ret; + struct adis16240_state *st = indio_dev->dev_data; + + st->trig = iio_allocate_trigger(); + st->trig->name = kmalloc(IIO_TRIGGER_NAME_LENGTH, GFP_KERNEL); + if (!st->trig->name) { + ret = -ENOMEM; + goto error_free_trig; + } + snprintf((char *)st->trig->name, + IIO_TRIGGER_NAME_LENGTH, + "adis16240-dev%d", indio_dev->id); + st->trig->dev.parent = &st->us->dev; + st->trig->owner = THIS_MODULE; + st->trig->private_data = st; + st->trig->set_trigger_state = &adis16240_data_rdy_trigger_set_state; + st->trig->try_reenable = &adis16240_trig_try_reen; + st->trig->control_attrs = &adis16240_trigger_attr_group; + ret = iio_trigger_register(st->trig); + + /* select default trigger */ + indio_dev->trig = st->trig; + if (ret) + goto error_free_trig_name; + + return 0; + +error_free_trig_name: + kfree(st->trig->name); +error_free_trig: + iio_free_trigger(st->trig); + + return ret; +} + +void adis16240_remove_trigger(struct iio_dev *indio_dev) +{ + struct adis16240_state *state = indio_dev->dev_data; + + iio_trigger_unregister(state->trig); + kfree(state->trig->name); + iio_free_trigger(state->trig); +} diff --git a/drivers/staging/iio/accel/inclinometer.h b/drivers/staging/iio/accel/inclinometer.h new file mode 100644 index 000000000000..5b49f835eac5 --- /dev/null +++ b/drivers/staging/iio/accel/inclinometer.h @@ -0,0 +1,23 @@ +/* + * Inclinometer related attributes + */ +#include "../sysfs.h" + +#define IIO_DEV_ATTR_INCLI_X(_show, _addr) \ + IIO_DEVICE_ATTR(incli_x_raw, S_IRUGO, _show, NULL, _addr) + +#define IIO_DEV_ATTR_INCLI_Y(_show, _addr) \ + IIO_DEVICE_ATTR(incli_y_raw, S_IRUGO, _show, NULL, _addr) + +#define IIO_DEV_ATTR_INCLI_Z(_show, _addr) \ + IIO_DEVICE_ATTR(incli_z_raw, S_IRUGO, _show, NULL, _addr) + +#define IIO_DEV_ATTR_INCLI_X_OFFSET(_mode, _show, _store, _addr) \ + IIO_DEVICE_ATTR(incli_x_offset, _mode, _show, _store, _addr) + +#define IIO_DEV_ATTR_INCLI_Y_OFFSET(_mode, _show, _store, _addr) \ + IIO_DEVICE_ATTR(incli_y_offset, _mode, _show, _store, _addr) + +#define IIO_DEV_ATTR_INCLI_Z_OFFSET(_mode, _show, _store, _addr) \ + IIO_DEVICE_ATTR(incli_z_offset, _mode, _show, _store, _addr) + diff --git a/drivers/staging/iio/accel/kxsd9.c b/drivers/staging/iio/accel/kxsd9.c index db2dd537ffb0..ae7ffe114fc5 100644 --- a/drivers/staging/iio/accel/kxsd9.c +++ b/drivers/staging/iio/accel/kxsd9.c @@ -26,6 +26,7 @@ #include <linux/rtc.h> #include <linux/delay.h> #include <linux/slab.h> +#include <linux/string.h> #include "../iio.h" #include "../sysfs.h" @@ -51,8 +52,10 @@ #define KXSD9_READ(a) (0x80 | (a)) #define KXSD9_WRITE(a) (a) -#define IIO_DEV_ATTR_ACCEL_SET_RANGE(_mode, _show, _store) \ - IIO_DEVICE_ATTR(accel_range, _mode, _show, _store, 0) +#define KXSD9_SCALE_2G "0.011978" +#define KXSD9_SCALE_4G "0.023927" +#define KXSD9_SCALE_6G "0.035934" +#define KXSD9_SCALE_8G "0.047853" #define KXSD9_STATE_RX_SIZE 2 #define KXSD9_STATE_TX_SIZE 4 @@ -73,9 +76,9 @@ struct kxsd9_state { }; /* This may want to move to mili g to allow for non integer ranges */ -static ssize_t kxsd9_read_accel_range(struct device *dev, - struct device_attribute *attr, - char *buf) +static ssize_t kxsd9_read_scale(struct device *dev, + struct device_attribute *attr, + char *buf) { int ret; ssize_t len = 0; @@ -101,16 +104,16 @@ static ssize_t kxsd9_read_accel_range(struct device *dev, switch (st->rx[1] & KXSD9_FS_MASK) { case KXSD9_FS_8: - len += sprintf(buf, "8\n"); + len += sprintf(buf, "%s\n", KXSD9_SCALE_8G); break; case KXSD9_FS_6: - len += sprintf(buf, "6\n"); + len += sprintf(buf, "%s\n", KXSD9_SCALE_6G); break; case KXSD9_FS_4: - len += sprintf(buf, "4\n"); + len += sprintf(buf, "%s\n", KXSD9_SCALE_4G); break; case KXSD9_FS_2: - len += sprintf(buf, "2\n"); + len += sprintf(buf, "%s\n", KXSD9_SCALE_2G); break; } @@ -119,12 +122,12 @@ error_ret: return ret ? ret : len; } -static ssize_t kxsd9_write_accel_range(struct device *dev, - struct device_attribute *attr, - const char *buf, - size_t len) +static ssize_t kxsd9_write_scale(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t len) { - long readin; + struct spi_message msg; int ret; struct iio_dev *indio_dev = dev_get_drvdata(dev); @@ -145,25 +148,25 @@ static ssize_t kxsd9_write_accel_range(struct device *dev, }, }; - ret = strict_strtol(buf, 10, &readin); - if (ret) - return ret; - switch (readin) { - case 8: + if (!strncmp(buf, KXSD9_SCALE_8G, + strlen(buf) < strlen(KXSD9_SCALE_8G) + ? strlen(buf) : strlen(KXSD9_SCALE_8G))) val = KXSD9_FS_8; - break; - case 6: + else if (!strncmp(buf, KXSD9_SCALE_6G, + strlen(buf) < strlen(KXSD9_SCALE_6G) + ? strlen(buf) : strlen(KXSD9_SCALE_6G))) val = KXSD9_FS_6; - break; - case 4: + else if (!strncmp(buf, KXSD9_SCALE_4G, + strlen(buf) < strlen(KXSD9_SCALE_4G) + ? strlen(buf) : strlen(KXSD9_SCALE_4G))) val = KXSD9_FS_4; - break; - case 2: + else if (!strncmp(buf, KXSD9_SCALE_2G, + strlen(buf) < strlen(KXSD9_SCALE_2G) + ? strlen(buf) : strlen(KXSD9_SCALE_2G))) val = KXSD9_FS_2; - break; - default: + else return -EINVAL; - } + mutex_lock(&st->buf_lock); st->tx[0] = KXSD9_READ(KXSD9_REG_CTRL_C); st->tx[1] = 0; @@ -182,6 +185,7 @@ error_ret: mutex_unlock(&st->buf_lock); return ret ? ret : len; } + static ssize_t kxsd9_read_accel(struct device *dev, struct device_attribute *attr, char *buf) @@ -227,17 +231,27 @@ error_ret: static IIO_DEV_ATTR_ACCEL_X(kxsd9_read_accel, KXSD9_REG_X); static IIO_DEV_ATTR_ACCEL_Y(kxsd9_read_accel, KXSD9_REG_Y); static IIO_DEV_ATTR_ACCEL_Z(kxsd9_read_accel, KXSD9_REG_Z); -static IIO_DEV_ATTR_ADC(0, kxsd9_read_accel, KXSD9_REG_AUX); -static IIO_DEV_ATTR_ACCEL_SET_RANGE(S_IRUGO | S_IWUSR, - kxsd9_read_accel_range, - kxsd9_write_accel_range); +static IIO_DEV_ATTR_IN_RAW(0, kxsd9_read_accel, KXSD9_REG_AUX); + +static IIO_DEVICE_ATTR(accel_scale, + S_IRUGO | S_IWUSR, + kxsd9_read_scale, + kxsd9_write_scale, + 0); + +static IIO_CONST_ATTR(accel_scale_available, + KXSD9_SCALE_2G " " + KXSD9_SCALE_4G " " + KXSD9_SCALE_6G " " + KXSD9_SCALE_8G); static struct attribute *kxsd9_attributes[] = { - &iio_dev_attr_accel_x.dev_attr.attr, - &iio_dev_attr_accel_y.dev_attr.attr, - &iio_dev_attr_accel_z.dev_attr.attr, - &iio_dev_attr_adc_0.dev_attr.attr, - &iio_dev_attr_accel_range.dev_attr.attr, + &iio_dev_attr_accel_x_raw.dev_attr.attr, + &iio_dev_attr_accel_y_raw.dev_attr.attr, + &iio_dev_attr_accel_z_raw.dev_attr.attr, + &iio_dev_attr_in0_raw.dev_attr.attr, + &iio_dev_attr_accel_scale.dev_attr.attr, + &iio_const_attr_accel_scale_available.dev_attr.attr, NULL, }; diff --git a/drivers/staging/iio/accel/lis3l02dq.h b/drivers/staging/iio/accel/lis3l02dq.h index 91a5375408c2..e76a97937a36 100644 --- a/drivers/staging/iio/accel/lis3l02dq.h +++ b/drivers/staging/iio/accel/lis3l02dq.h @@ -179,10 +179,6 @@ int lis3l02dq_spi_read_reg_8(struct device *dev, int lis3l02dq_spi_write_reg_8(struct device *dev, u8 reg_address, u8 *val); -#define LIS3L02DQ_SCAN_ACC_X 0 -#define LIS3L02DQ_SCAN_ACC_Y 1 -#define LIS3L02DQ_SCAN_ACC_Z 2 - #ifdef CONFIG_IIO_RING_BUFFER /* At the moment triggers are only used for ring buffer diff --git a/drivers/staging/iio/accel/lis3l02dq_core.c b/drivers/staging/iio/accel/lis3l02dq_core.c index 82e43588e8a5..6b5577d7d8de 100644 --- a/drivers/staging/iio/accel/lis3l02dq_core.c +++ b/drivers/staging/iio/accel/lis3l02dq_core.c @@ -458,41 +458,39 @@ err_ret: return ret; } -static IIO_DEV_ATTR_ACCEL_X_OFFSET(S_IWUSR | S_IRUGO, - lis3l02dq_read_signed, - lis3l02dq_write_signed, - LIS3L02DQ_REG_OFFSET_X_ADDR); - -static IIO_DEV_ATTR_ACCEL_Y_OFFSET(S_IWUSR | S_IRUGO, - lis3l02dq_read_signed, - lis3l02dq_write_signed, - LIS3L02DQ_REG_OFFSET_Y_ADDR); - -static IIO_DEV_ATTR_ACCEL_Z_OFFSET(S_IWUSR | S_IRUGO, - lis3l02dq_read_signed, - lis3l02dq_write_signed, - LIS3L02DQ_REG_OFFSET_Z_ADDR); - -static IIO_DEV_ATTR_ACCEL_X_GAIN(S_IWUSR | S_IRUGO, - lis3l02dq_read_unsigned, - lis3l02dq_write_unsigned, - LIS3L02DQ_REG_GAIN_X_ADDR); - -static IIO_DEV_ATTR_ACCEL_Y_GAIN(S_IWUSR | S_IRUGO, - lis3l02dq_read_unsigned, - lis3l02dq_write_unsigned, - LIS3L02DQ_REG_GAIN_Y_ADDR); - -static IIO_DEV_ATTR_ACCEL_Z_GAIN(S_IWUSR | S_IRUGO, - lis3l02dq_read_unsigned, - lis3l02dq_write_unsigned, - LIS3L02DQ_REG_GAIN_Z_ADDR); - -static IIO_DEV_ATTR_ACCEL_THRESH(S_IWUSR | S_IRUGO, - lis3l02dq_read_16bit_signed, - lis3l02dq_write_16bit_signed, - LIS3L02DQ_REG_THS_L_ADDR); - +#define LIS3L02DQ_SIGNED_ATTR(name, reg) \ + IIO_DEVICE_ATTR(name, \ + S_IWUSR | S_IRUGO, \ + lis3l02dq_read_signed, \ + lis3l02dq_write_signed, \ + reg); + +#define LIS3L02DQ_UNSIGNED_ATTR(name, reg) \ + IIO_DEVICE_ATTR(name, \ + S_IWUSR | S_IRUGO, \ + lis3l02dq_read_unsigned, \ + lis3l02dq_write_unsigned, \ + reg); + +static LIS3L02DQ_SIGNED_ATTR(accel_x_calibbias, + LIS3L02DQ_REG_OFFSET_X_ADDR); +static LIS3L02DQ_SIGNED_ATTR(accel_y_calibbias, + LIS3L02DQ_REG_OFFSET_Y_ADDR); +static LIS3L02DQ_SIGNED_ATTR(accel_z_calibbias, + LIS3L02DQ_REG_OFFSET_Z_ADDR); + +static LIS3L02DQ_UNSIGNED_ATTR(accel_x_calibscale, + LIS3L02DQ_REG_GAIN_X_ADDR); +static LIS3L02DQ_UNSIGNED_ATTR(accel_y_calibscale, + LIS3L02DQ_REG_GAIN_Y_ADDR); +static LIS3L02DQ_UNSIGNED_ATTR(accel_z_calibscale, + LIS3L02DQ_REG_GAIN_Z_ADDR); + +static IIO_DEVICE_ATTR(accel_mag_either_rising_value, + S_IWUSR | S_IRUGO, + lis3l02dq_read_16bit_signed, + lis3l02dq_write_16bit_signed, + LIS3L02DQ_REG_THS_L_ADDR); /* RFC The reading method for these will change depending on whether * ring buffer capture is in use. Is it worth making these take two * functions and let the core handle which to call, or leave as in this @@ -512,7 +510,7 @@ static IIO_DEV_ATTR_SAMP_FREQ(S_IWUSR | S_IRUGO, lis3l02dq_read_frequency, lis3l02dq_write_frequency); -static IIO_CONST_ATTR_AVAIL_SAMP_FREQ("280 560 1120 4480"); +static IIO_CONST_ATTR_SAMP_FREQ_AVAIL("280 560 1120 4480"); static ssize_t lis3l02dq_read_interrupt_config(struct device *dev, struct device_attribute *attr, @@ -522,7 +520,7 @@ static ssize_t lis3l02dq_read_interrupt_config(struct device *dev, s8 val; struct iio_event_attr *this_attr = to_iio_event_attr(attr); - ret = lis3l02dq_spi_read_reg_8(dev, + ret = lis3l02dq_spi_read_reg_8(dev->parent, LIS3L02DQ_REG_WAKE_UP_CFG_ADDR, (u8 *)&val); @@ -545,7 +543,7 @@ static ssize_t lis3l02dq_write_interrupt_config(struct device *dev, mutex_lock(&indio_dev->mlock); /* read current value */ - ret = lis3l02dq_spi_read_reg_8(dev, + ret = lis3l02dq_spi_read_reg_8(dev->parent, LIS3L02DQ_REG_WAKE_UP_CFG_ADDR, &valold); if (ret) @@ -668,43 +666,51 @@ static void lis3l02dq_thresh_handler_bh_no_check(struct work_struct *work_s) /* A shared handler for a number of threshold types */ IIO_EVENT_SH(threshold, &lis3l02dq_thresh_handler_th); -IIO_EVENT_ATTR_ACCEL_X_HIGH_SH(iio_event_threshold, - lis3l02dq_read_interrupt_config, - lis3l02dq_write_interrupt_config, - LIS3L02DQ_REG_WAKE_UP_CFG_INTERRUPT_X_HIGH); - -IIO_EVENT_ATTR_ACCEL_Y_HIGH_SH(iio_event_threshold, - lis3l02dq_read_interrupt_config, - lis3l02dq_write_interrupt_config, - LIS3L02DQ_REG_WAKE_UP_CFG_INTERRUPT_Y_HIGH); - -IIO_EVENT_ATTR_ACCEL_Z_HIGH_SH(iio_event_threshold, - lis3l02dq_read_interrupt_config, - lis3l02dq_write_interrupt_config, - LIS3L02DQ_REG_WAKE_UP_CFG_INTERRUPT_Z_HIGH); - -IIO_EVENT_ATTR_ACCEL_X_LOW_SH(iio_event_threshold, - lis3l02dq_read_interrupt_config, - lis3l02dq_write_interrupt_config, - LIS3L02DQ_REG_WAKE_UP_CFG_INTERRUPT_X_LOW); - -IIO_EVENT_ATTR_ACCEL_Y_LOW_SH(iio_event_threshold, - lis3l02dq_read_interrupt_config, - lis3l02dq_write_interrupt_config, - LIS3L02DQ_REG_WAKE_UP_CFG_INTERRUPT_Y_LOW); +IIO_EVENT_ATTR_SH(accel_x_mag_pos_rising_en, + iio_event_threshold, + lis3l02dq_read_interrupt_config, + lis3l02dq_write_interrupt_config, + LIS3L02DQ_REG_WAKE_UP_CFG_INTERRUPT_X_HIGH); + +IIO_EVENT_ATTR_SH(accel_y_mag_pos_rising_en, + iio_event_threshold, + lis3l02dq_read_interrupt_config, + lis3l02dq_write_interrupt_config, + LIS3L02DQ_REG_WAKE_UP_CFG_INTERRUPT_Y_HIGH); + +IIO_EVENT_ATTR_SH(accel_z_mag_pos_rising_en, + iio_event_threshold, + lis3l02dq_read_interrupt_config, + lis3l02dq_write_interrupt_config, + LIS3L02DQ_REG_WAKE_UP_CFG_INTERRUPT_Z_HIGH); + +IIO_EVENT_ATTR_SH(accel_x_mag_neg_rising_en, + iio_event_threshold, + lis3l02dq_read_interrupt_config, + lis3l02dq_write_interrupt_config, + LIS3L02DQ_REG_WAKE_UP_CFG_INTERRUPT_X_LOW); + +IIO_EVENT_ATTR_SH(accel_y_mag_neg_rising_en, + iio_event_threshold, + lis3l02dq_read_interrupt_config, + lis3l02dq_write_interrupt_config, + LIS3L02DQ_REG_WAKE_UP_CFG_INTERRUPT_Y_LOW); + +IIO_EVENT_ATTR_SH(accel_z_mag_neg_rising_en, + iio_event_threshold, + lis3l02dq_read_interrupt_config, + lis3l02dq_write_interrupt_config, + LIS3L02DQ_REG_WAKE_UP_CFG_INTERRUPT_Z_LOW); -IIO_EVENT_ATTR_ACCEL_Z_LOW_SH(iio_event_threshold, - lis3l02dq_read_interrupt_config, - lis3l02dq_write_interrupt_config, - LIS3L02DQ_REG_WAKE_UP_CFG_INTERRUPT_Z_LOW); static struct attribute *lis3l02dq_event_attributes[] = { - &iio_event_attr_accel_x_high.dev_attr.attr, - &iio_event_attr_accel_y_high.dev_attr.attr, - &iio_event_attr_accel_z_high.dev_attr.attr, - &iio_event_attr_accel_x_low.dev_attr.attr, - &iio_event_attr_accel_y_low.dev_attr.attr, - &iio_event_attr_accel_z_low.dev_attr.attr, + &iio_event_attr_accel_x_mag_pos_rising_en.dev_attr.attr, + &iio_event_attr_accel_y_mag_pos_rising_en.dev_attr.attr, + &iio_event_attr_accel_z_mag_pos_rising_en.dev_attr.attr, + &iio_event_attr_accel_x_mag_neg_rising_en.dev_attr.attr, + &iio_event_attr_accel_y_mag_neg_rising_en.dev_attr.attr, + &iio_event_attr_accel_z_mag_neg_rising_en.dev_attr.attr, + &iio_dev_attr_accel_mag_either_rising_value.dev_attr.attr, NULL }; @@ -713,20 +719,21 @@ static struct attribute_group lis3l02dq_event_attribute_group = { }; static IIO_CONST_ATTR(name, "lis3l02dq"); +static IIO_CONST_ATTR(accel_scale, "0.00958"); static struct attribute *lis3l02dq_attributes[] = { - &iio_dev_attr_accel_x_offset.dev_attr.attr, - &iio_dev_attr_accel_y_offset.dev_attr.attr, - &iio_dev_attr_accel_z_offset.dev_attr.attr, - &iio_dev_attr_accel_x_gain.dev_attr.attr, - &iio_dev_attr_accel_y_gain.dev_attr.attr, - &iio_dev_attr_accel_z_gain.dev_attr.attr, - &iio_dev_attr_thresh.dev_attr.attr, - &iio_dev_attr_accel_x.dev_attr.attr, - &iio_dev_attr_accel_y.dev_attr.attr, - &iio_dev_attr_accel_z.dev_attr.attr, + &iio_dev_attr_accel_x_calibbias.dev_attr.attr, + &iio_dev_attr_accel_y_calibbias.dev_attr.attr, + &iio_dev_attr_accel_z_calibbias.dev_attr.attr, + &iio_dev_attr_accel_x_calibscale.dev_attr.attr, + &iio_dev_attr_accel_y_calibscale.dev_attr.attr, + &iio_dev_attr_accel_z_calibscale.dev_attr.attr, + &iio_const_attr_accel_scale.dev_attr.attr, + &iio_dev_attr_accel_x_raw.dev_attr.attr, + &iio_dev_attr_accel_y_raw.dev_attr.attr, + &iio_dev_attr_accel_z_raw.dev_attr.attr, &iio_dev_attr_sampling_frequency.dev_attr.attr, - &iio_const_attr_available_sampling_frequency.dev_attr.attr, + &iio_const_attr_sampling_frequency_available.dev_attr.attr, &iio_const_attr_name.dev_attr.attr, NULL }; diff --git a/drivers/staging/iio/accel/lis3l02dq_ring.c b/drivers/staging/iio/accel/lis3l02dq_ring.c index a4d97ea0df3d..e4e202e6cb3a 100644 --- a/drivers/staging/iio/accel/lis3l02dq_ring.c +++ b/drivers/staging/iio/accel/lis3l02dq_ring.c @@ -75,16 +75,16 @@ error_ret: return ret; } -static IIO_SCAN_EL_C(accel_x, LIS3L02DQ_SCAN_ACC_X, IIO_SIGNED(16), +static IIO_SCAN_EL_C(accel_x, 0, IIO_SIGNED(16), LIS3L02DQ_REG_OUT_X_L_ADDR, &lis3l02dq_scan_el_set_state); -static IIO_SCAN_EL_C(accel_y, LIS3L02DQ_SCAN_ACC_Y, IIO_SIGNED(16), +static IIO_SCAN_EL_C(accel_y, 1, IIO_SIGNED(16), LIS3L02DQ_REG_OUT_Y_L_ADDR, &lis3l02dq_scan_el_set_state); -static IIO_SCAN_EL_C(accel_z, LIS3L02DQ_SCAN_ACC_Z, IIO_SIGNED(16), +static IIO_SCAN_EL_C(accel_z, 2, IIO_SIGNED(16), LIS3L02DQ_REG_OUT_Z_L_ADDR, &lis3l02dq_scan_el_set_state); -static IIO_SCAN_EL_TIMESTAMP; +static IIO_SCAN_EL_TIMESTAMP(3); static struct attribute *lis3l02dq_scan_el_attrs[] = { &iio_scan_el_accel_x.dev_attr.attr, @@ -192,8 +192,7 @@ error_ret: } -static const u8 read_all_tx_array[] = -{ +static const u8 read_all_tx_array[] = { LIS3L02DQ_READ_REG(LIS3L02DQ_REG_OUT_X_L_ADDR), 0, LIS3L02DQ_READ_REG(LIS3L02DQ_REG_OUT_X_H_ADDR), 0, LIS3L02DQ_READ_REG(LIS3L02DQ_REG_OUT_Y_L_ADDR), 0, @@ -208,7 +207,7 @@ static const u8 read_all_tx_array[] = * @rx_array: (dma capable) recieve array, must be at least * 4*number of channels **/ -int lis3l02dq_read_all(struct lis3l02dq_state *st, u8 *rx_array) +static int lis3l02dq_read_all(struct lis3l02dq_state *st, u8 *rx_array) { struct spi_transfer *xfers; struct spi_message msg; @@ -358,10 +357,10 @@ static int lis3l02dq_data_rdy_ring_predisable(struct iio_dev *indio_dev) /* Caller responsible for locking as necessary. */ -static int __lis3l02dq_write_data_ready_config(struct device *dev, - struct - iio_event_handler_list *list, - bool state) +static int +__lis3l02dq_write_data_ready_config(struct device *dev, + struct iio_event_handler_list *list, + bool state) { int ret; u8 valold; @@ -584,7 +583,7 @@ error_iio_sw_rb_free: int lis3l02dq_initialize_ring(struct iio_ring_buffer *ring) { - return iio_ring_buffer_register(ring); + return iio_ring_buffer_register(ring, 0); } void lis3l02dq_uninitialize_ring(struct iio_ring_buffer *ring) @@ -592,7 +591,6 @@ void lis3l02dq_uninitialize_ring(struct iio_ring_buffer *ring) iio_ring_buffer_unregister(ring); } - int lis3l02dq_set_ring_length(struct iio_dev *indio_dev, int length) { /* Set sensible defaults for the ring buffer */ diff --git a/drivers/staging/iio/accel/sca3000.h b/drivers/staging/iio/accel/sca3000.h index da7d3cb5ae71..e5321999b263 100644 --- a/drivers/staging/iio/accel/sca3000.h +++ b/drivers/staging/iio/accel/sca3000.h @@ -187,6 +187,7 @@ struct sca3000_state { /** * struct sca3000_chip_info - model dependant parameters * @name: model identification + * @scale: string containing floating point scale factor * @temp_output: some devices have temperature sensors. * @measurement_mode_freq: normal mode sampling frequency * @option_mode_1: first optional mode. Not all models have one @@ -199,6 +200,7 @@ struct sca3000_state { **/ struct sca3000_chip_info { const char *name; + const char *scale; bool temp_output; int measurement_mode_freq; int option_mode_1; diff --git a/drivers/staging/iio/accel/sca3000_core.c b/drivers/staging/iio/accel/sca3000_core.c index 1c229869a22d..d4f82c39f335 100644 --- a/drivers/staging/iio/accel/sca3000_core.c +++ b/drivers/staging/iio/accel/sca3000_core.c @@ -27,11 +27,9 @@ enum sca3000_variant { d01, - d03, e02, e04, e05, - l01, }; /* Note where option modes are not defined, the chip simply does not @@ -44,21 +42,20 @@ enum sca3000_variant { static const struct sca3000_chip_info sca3000_spi_chip_info_tbl[] = { { .name = "sca3000-d01", + .scale = " 0.0073575", .temp_output = true, .measurement_mode_freq = 250, .option_mode_1 = SCA3000_OP_MODE_BYPASS, .option_mode_1_freq = 250, }, { - /* No data sheet available - may be the same as the 3100-d03?*/ - .name = "sca3000-d03", - .temp_output = true, - }, { .name = "sca3000-e02", + .scale = "0.00981", .measurement_mode_freq = 125, .option_mode_1 = SCA3000_OP_MODE_NARROW, .option_mode_1_freq = 63, }, { .name = "sca3000-e04", + .scale = "0.01962", .measurement_mode_freq = 100, .option_mode_1 = SCA3000_OP_MODE_NARROW, .option_mode_1_freq = 50, @@ -66,18 +63,12 @@ static const struct sca3000_chip_info sca3000_spi_chip_info_tbl[] = { .option_mode_2_freq = 400, }, { .name = "sca3000-e05", + .scale = "0.0613125", .measurement_mode_freq = 200, .option_mode_1 = SCA3000_OP_MODE_NARROW, .option_mode_1_freq = 50, .option_mode_2 = SCA3000_OP_MODE_WIDE, .option_mode_2_freq = 400, - }, { - /* No data sheet available. - * Frequencies are unknown. - */ - .name = "sca3000-l01", - .temp_output = true, - .option_mode_1 = SCA3000_OP_MODE_BYPASS, }, }; @@ -286,7 +277,7 @@ static int sca3000_check_status(struct device *dev) if (ret < 0) goto error_ret; if (rx[1] & SCA3000_EEPROM_CS_ERROR) - dev_err(dev, "eeprom error \n"); + dev_err(dev, "eeprom error\n"); if (rx[1] & SCA3000_SPI_FRAME_ERROR) dev_err(dev, "Previous SPI Frame was corrupt\n"); kfree(rx); @@ -327,6 +318,14 @@ error_ret: return ret ? ret : len; } +static ssize_t sca3000_show_scale(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct iio_dev *dev_info = dev_get_drvdata(dev); + struct sca3000_state *st = dev_info->dev_data; + return sprintf(buf, "%s\n", st->info->scale); +} static ssize_t sca3000_show_name(struct device *dev, struct device_attribute *attr, @@ -395,7 +394,7 @@ sca3000_show_available_measurement_modes(struct device *dev, break; } /* always supported */ - len += sprintf(buf + len, " 3 - motion detection \n"); + len += sprintf(buf + len, " 3 - motion detection\n"); return len; } @@ -495,7 +494,7 @@ error_ret: /* Not even vaguely standard attributes so defined here rather than * in the relevant IIO core headers */ -static IIO_DEVICE_ATTR(available_measurement_modes, S_IRUGO, +static IIO_DEVICE_ATTR(measurement_mode_available, S_IRUGO, sca3000_show_available_measurement_modes, NULL, 0); @@ -508,6 +507,8 @@ static IIO_DEVICE_ATTR(measurement_mode, S_IRUGO | S_IWUSR, static IIO_DEV_ATTR_NAME(sca3000_show_name); static IIO_DEV_ATTR_REV(sca3000_show_rev); +static IIO_DEVICE_ATTR(accel_scale, S_IRUGO, sca3000_show_scale, + NULL, 0); static IIO_DEV_ATTR_ACCEL_X(sca3000_read_13bit_signed, SCA3000_REG_ADDR_X_MSB); @@ -683,7 +684,7 @@ error_free_lock: /* Should only really be registered if ring buffer support is compiled in. * Does no harm however and doing it right would add a fair bit of complexity */ -static IIO_DEV_ATTR_AVAIL_SAMP_FREQ(sca3000_read_av_freq); +static IIO_DEV_ATTR_SAMP_FREQ_AVAIL(sca3000_read_av_freq); static IIO_DEV_ATTR_SAMP_FREQ(S_IWUSR | S_IRUGO, sca3000_read_frequency, @@ -718,7 +719,10 @@ static ssize_t sca3000_read_temp(struct device *dev, error_ret: return ret; } -static IIO_DEV_ATTR_TEMP(sca3000_read_temp); +static IIO_DEV_ATTR_TEMP_RAW(sca3000_read_temp); + +static IIO_CONST_ATTR(temp_scale, "0.555556"); +static IIO_CONST_ATTR(temp_offset, "-214.6"); /** * sca3000_show_thresh() sysfs query of a threshold @@ -770,31 +774,34 @@ static ssize_t sca3000_write_thresh(struct device *dev, return ret ? ret : len; } -static IIO_DEV_ATTR_ACCEL_THRESH_X(S_IRUGO | S_IWUSR, - sca3000_show_thresh, - sca3000_write_thresh, - SCA3000_REG_CTRL_SEL_MD_X_TH); -static IIO_DEV_ATTR_ACCEL_THRESH_Y(S_IRUGO | S_IWUSR, - sca3000_show_thresh, - sca3000_write_thresh, - SCA3000_REG_CTRL_SEL_MD_Y_TH); -static IIO_DEV_ATTR_ACCEL_THRESH_Z(S_IRUGO | S_IWUSR, - sca3000_show_thresh, - sca3000_write_thresh, - SCA3000_REG_CTRL_SEL_MD_Z_TH); +static IIO_DEVICE_ATTR(accel_x_mag_either_rising_value, + S_IRUGO | S_IWUSR, + sca3000_show_thresh, + sca3000_write_thresh, + SCA3000_REG_CTRL_SEL_MD_X_TH); + +static IIO_DEVICE_ATTR(accel_y_mag_either_rising_value, + S_IRUGO | S_IWUSR, + sca3000_show_thresh, + sca3000_write_thresh, + SCA3000_REG_CTRL_SEL_MD_Y_TH); + +static IIO_DEVICE_ATTR(accel_z_mag_either_rising_value, + S_IRUGO | S_IWUSR, + sca3000_show_thresh, + sca3000_write_thresh, + SCA3000_REG_CTRL_SEL_MD_Z_TH); static struct attribute *sca3000_attributes[] = { &iio_dev_attr_name.dev_attr.attr, &iio_dev_attr_revision.dev_attr.attr, - &iio_dev_attr_accel_x.dev_attr.attr, - &iio_dev_attr_accel_y.dev_attr.attr, - &iio_dev_attr_accel_z.dev_attr.attr, - &iio_dev_attr_thresh_accel_x.dev_attr.attr, - &iio_dev_attr_thresh_accel_y.dev_attr.attr, - &iio_dev_attr_thresh_accel_z.dev_attr.attr, - &iio_dev_attr_available_measurement_modes.dev_attr.attr, + &iio_dev_attr_accel_scale.dev_attr.attr, + &iio_dev_attr_accel_x_raw.dev_attr.attr, + &iio_dev_attr_accel_y_raw.dev_attr.attr, + &iio_dev_attr_accel_z_raw.dev_attr.attr, + &iio_dev_attr_measurement_mode_available.dev_attr.attr, &iio_dev_attr_measurement_mode.dev_attr.attr, - &iio_dev_attr_available_sampling_frequency.dev_attr.attr, + &iio_dev_attr_sampling_frequency_available.dev_attr.attr, &iio_dev_attr_sampling_frequency.dev_attr.attr, NULL, }; @@ -802,18 +809,18 @@ static struct attribute *sca3000_attributes[] = { static struct attribute *sca3000_attributes_with_temp[] = { &iio_dev_attr_name.dev_attr.attr, &iio_dev_attr_revision.dev_attr.attr, - &iio_dev_attr_accel_x.dev_attr.attr, - &iio_dev_attr_accel_y.dev_attr.attr, - &iio_dev_attr_accel_z.dev_attr.attr, - &iio_dev_attr_thresh_accel_x.dev_attr.attr, - &iio_dev_attr_thresh_accel_y.dev_attr.attr, - &iio_dev_attr_thresh_accel_z.dev_attr.attr, - &iio_dev_attr_available_measurement_modes.dev_attr.attr, + &iio_dev_attr_accel_scale.dev_attr.attr, + &iio_dev_attr_accel_x_raw.dev_attr.attr, + &iio_dev_attr_accel_y_raw.dev_attr.attr, + &iio_dev_attr_accel_z_raw.dev_attr.attr, + &iio_dev_attr_measurement_mode_available.dev_attr.attr, &iio_dev_attr_measurement_mode.dev_attr.attr, - &iio_dev_attr_available_sampling_frequency.dev_attr.attr, + &iio_dev_attr_sampling_frequency_available.dev_attr.attr, &iio_dev_attr_sampling_frequency.dev_attr.attr, /* Only present if temp sensor is */ - &iio_dev_attr_temp.dev_attr.attr, + &iio_dev_attr_temp_raw.dev_attr.attr, + &iio_const_attr_temp_offset.dev_attr.attr, + &iio_const_attr_temp_scale.dev_attr.attr, NULL, }; @@ -910,7 +917,7 @@ static ssize_t sca3000_query_mo_det(struct device *dev, struct device_attribute *attr, char *buf) { - struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct iio_dev *indio_dev = dev_get_drvdata(dev->parent); struct sca3000_state *st = indio_dev->dev_data; struct iio_event_attr *this_attr = to_iio_event_attr(attr); int ret, len = 0; @@ -975,7 +982,7 @@ static ssize_t sca3000_query_ring_int(struct device *dev, struct iio_event_attr *this_attr = to_iio_event_attr(attr); int ret, len; u8 *rx; - struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct iio_dev *indio_dev = dev_get_drvdata(dev->parent); struct sca3000_state *st = indio_dev->dev_data; mutex_lock(&st->lock); ret = sca3000_read_data(st, SCA3000_REG_ADDR_INT_MASK, &rx, 1); @@ -995,7 +1002,7 @@ static ssize_t sca3000_set_ring_int(struct device *dev, const char *buf, size_t len) { - struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct iio_dev *indio_dev = dev_get_drvdata(dev->parent); struct sca3000_state *st = indio_dev->dev_data; struct iio_event_attr *this_attr = to_iio_event_attr(attr); @@ -1085,7 +1092,7 @@ static ssize_t sca3000_set_mo_det(struct device *dev, const char *buf, size_t len) { - struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct iio_dev *indio_dev = dev_get_drvdata(dev->parent); struct sca3000_state *st = indio_dev->dev_data; struct iio_event_attr *this_attr = to_iio_event_attr(attr); long val; @@ -1155,20 +1162,23 @@ IIO_EVENT_ATTR_FREE_FALL_DETECT_SH(iio_event_all, 0) /* Motion detector related event attributes */ -IIO_EVENT_ATTR_ACCEL_X_HIGH_SH(iio_event_all, - sca3000_query_mo_det, - sca3000_set_mo_det, - SCA3000_MD_CTRL_OR_X); - -IIO_EVENT_ATTR_ACCEL_Y_HIGH_SH(iio_event_all, - sca3000_query_mo_det, - sca3000_set_mo_det, - SCA3000_MD_CTRL_OR_Y); - -IIO_EVENT_ATTR_ACCEL_Z_HIGH_SH(iio_event_all, - sca3000_query_mo_det, - sca3000_set_mo_det, - SCA3000_MD_CTRL_OR_Z); +IIO_EVENT_ATTR_SH(accel_x_mag_either_rising_en, + iio_event_all, + sca3000_query_mo_det, + sca3000_set_mo_det, + SCA3000_MD_CTRL_OR_X); + +IIO_EVENT_ATTR_SH(accel_y_mag_either_rising_en, + iio_event_all, + sca3000_query_mo_det, + sca3000_set_mo_det, + SCA3000_MD_CTRL_OR_Y); + +IIO_EVENT_ATTR_SH(accel_z_mag_either_rising_en, + iio_event_all, + sca3000_query_mo_det, + sca3000_set_mo_det, + SCA3000_MD_CTRL_OR_Z); /* Hardware ring buffer related event attributes */ IIO_EVENT_ATTR_RING_50_FULL_SH(iio_event_all, @@ -1183,11 +1193,14 @@ IIO_EVENT_ATTR_RING_75_FULL_SH(iio_event_all, static struct attribute *sca3000_event_attributes[] = { &iio_event_attr_free_fall.dev_attr.attr, - &iio_event_attr_accel_x_high.dev_attr.attr, - &iio_event_attr_accel_y_high.dev_attr.attr, - &iio_event_attr_accel_z_high.dev_attr.attr, + &iio_event_attr_accel_x_mag_either_rising_en.dev_attr.attr, + &iio_event_attr_accel_y_mag_either_rising_en.dev_attr.attr, + &iio_event_attr_accel_z_mag_either_rising_en.dev_attr.attr, &iio_event_attr_ring_50_full.dev_attr.attr, &iio_event_attr_ring_75_full.dev_attr.attr, + &iio_dev_attr_accel_x_mag_either_rising_value.dev_attr.attr, + &iio_dev_attr_accel_y_mag_either_rising_value.dev_attr.attr, + &iio_dev_attr_accel_z_mag_either_rising_value.dev_attr.attr, NULL, }; @@ -1325,7 +1338,7 @@ static int __devinit __sca3000_probe(struct spi_device *spi, if (ret < 0) goto error_free_dev; regdone = 1; - ret = iio_ring_buffer_register(st->indio_dev->ring); + ret = iio_ring_buffer_register(st->indio_dev->ring, 0); if (ret < 0) goto error_unregister_dev; if (spi->irq && gpio_is_valid(irq_to_gpio(spi->irq)) > 0) { @@ -1344,9 +1357,10 @@ static int __devinit __sca3000_probe(struct spi_device *spi, * is overkill. At very least a simpler registration method * might be worthwhile. */ - iio_add_event_to_list(iio_event_attr_accel_z_high.listel, - &st->indio_dev - ->interrupts[0]->ev_list); + iio_add_event_to_list( + iio_event_attr_accel_z_mag_either_rising_en.listel, + &st->indio_dev + ->interrupts[0]->ev_list); } sca3000_register_ring_funcs(st->indio_dev); ret = sca3000_clean_setup(st); @@ -1437,9 +1451,6 @@ static int sca3000_remove(struct spi_device *spi) SCA3000_VARIANT_PROBE(d01); static SCA3000_VARIANT_SPI_DRIVER(d01); -SCA3000_VARIANT_PROBE(d03); -static SCA3000_VARIANT_SPI_DRIVER(d03); - SCA3000_VARIANT_PROBE(e02); static SCA3000_VARIANT_SPI_DRIVER(e02); @@ -1449,9 +1460,6 @@ static SCA3000_VARIANT_SPI_DRIVER(e04); SCA3000_VARIANT_PROBE(e05); static SCA3000_VARIANT_SPI_DRIVER(e05); -SCA3000_VARIANT_PROBE(l01); -static SCA3000_VARIANT_SPI_DRIVER(l01); - static __init int sca3000_init(void) { int ret; @@ -1459,32 +1467,22 @@ static __init int sca3000_init(void) ret = spi_register_driver(&sca3000_d01_driver); if (ret) goto error_ret; - ret = spi_register_driver(&sca3000_d03_driver); - if (ret) - goto error_unreg_d01; ret = spi_register_driver(&sca3000_e02_driver); if (ret) - goto error_unreg_d03; + goto error_unreg_d01; ret = spi_register_driver(&sca3000_e04_driver); if (ret) goto error_unreg_e02; ret = spi_register_driver(&sca3000_e05_driver); if (ret) goto error_unreg_e04; - ret = spi_register_driver(&sca3000_l01_driver); - if (ret) - goto error_unreg_e05; return 0; -error_unreg_e05: - spi_unregister_driver(&sca3000_e05_driver); error_unreg_e04: spi_unregister_driver(&sca3000_e04_driver); error_unreg_e02: spi_unregister_driver(&sca3000_e02_driver); -error_unreg_d03: - spi_unregister_driver(&sca3000_d03_driver); error_unreg_d01: spi_unregister_driver(&sca3000_d01_driver); error_ret: @@ -1494,11 +1492,9 @@ error_ret: static __exit void sca3000_exit(void) { - spi_unregister_driver(&sca3000_l01_driver); spi_unregister_driver(&sca3000_e05_driver); spi_unregister_driver(&sca3000_e04_driver); spi_unregister_driver(&sca3000_e02_driver); - spi_unregister_driver(&sca3000_d03_driver); spi_unregister_driver(&sca3000_d01_driver); } diff --git a/drivers/staging/iio/accel/sca3000_ring.c b/drivers/staging/iio/accel/sca3000_ring.c index 40cbab2a6592..8e8c068d401b 100644 --- a/drivers/staging/iio/accel/sca3000_ring.c +++ b/drivers/staging/iio/accel/sca3000_ring.c @@ -186,11 +186,29 @@ static ssize_t sca3000_store_ring_bpse(struct device *dev, return ret ? ret : len; } -static IIO_CONST_ATTR(bpse_available, "8 11"); +static IIO_SCAN_EL_C(accel_x, 0, 0, 0, NULL); +static IIO_SCAN_EL_C(accel_y, 1, 0, 0, NULL); +static IIO_SCAN_EL_C(accel_z, 2, 0, 0, NULL); +static IIO_CONST_ATTR(accel_precision_available, "8 11"); +static IIO_DEVICE_ATTR(accel_precision, + S_IRUGO | S_IWUSR, + sca3000_show_ring_bpse, + sca3000_store_ring_bpse, + 0); + +static struct attribute *sca3000_scan_el_attrs[] = { + &iio_scan_el_accel_x.dev_attr.attr, + &iio_scan_el_accel_y.dev_attr.attr, + &iio_scan_el_accel_z.dev_attr.attr, + &iio_const_attr_accel_precision_available.dev_attr.attr, + &iio_dev_attr_accel_precision.dev_attr.attr, + NULL +}; -static IIO_DEV_ATTR_BPSE(S_IRUGO | S_IWUSR, - sca3000_show_ring_bpse, - sca3000_store_ring_bpse); +static struct attribute_group sca3000_scan_el_group = { + .attrs = sca3000_scan_el_attrs, + .name = "scan_elements", +}; /* * Ring buffer attributes @@ -198,17 +216,15 @@ static IIO_DEV_ATTR_BPSE(S_IRUGO | S_IWUSR, * only apply to the ring buffer. At all times full rate and accuracy * is available via direct reading from registers. */ -static struct attribute *iio_ring_attributes[] = { +static struct attribute *sca3000_ring_attributes[] = { &dev_attr_length.attr, &dev_attr_bps.attr, &dev_attr_ring_enable.attr, - &iio_dev_attr_bpse.dev_attr.attr, - &iio_const_attr_bpse_available.dev_attr.attr, NULL, }; static struct attribute_group sca3000_ring_attr = { - .attrs = iio_ring_attributes, + .attrs = sca3000_ring_attributes, }; static const struct attribute_group *sca3000_ring_attr_groups[] = { @@ -228,7 +244,7 @@ static struct iio_ring_buffer *sca3000_rb_allocate(struct iio_dev *indio_dev) ring = kzalloc(sizeof *ring, GFP_KERNEL); if (!ring) - return 0; + return NULL; ring->private = indio_dev; buf = &ring->buf; iio_ring_buffer_init(buf, indio_dev); @@ -248,6 +264,7 @@ static inline void sca3000_rb_free(struct iio_ring_buffer *r) int sca3000_configure_ring(struct iio_dev *indio_dev) { + indio_dev->scan_el_attrs = &sca3000_scan_el_group; indio_dev->ring = sca3000_rb_allocate(indio_dev); if (indio_dev->ring == NULL) return -ENOMEM; diff --git a/drivers/staging/iio/adc/Kconfig b/drivers/staging/iio/adc/Kconfig index 3989c0ca0e0d..0835fbc86c2a 100644 --- a/drivers/staging/iio/adc/Kconfig +++ b/drivers/staging/iio/adc/Kconfig @@ -7,11 +7,16 @@ config MAX1363 tristate "MAXIM max1363 ADC driver" depends on I2C select IIO_TRIGGER if IIO_RING_BUFFER + select MAX1363_RING_BUFFER help Say yes here to build support for many MAXIM i2c analog to digital - convertors (ADC). (max1361, max1362, max1363, max1364, max1136, - max1136, max1137, max1138, max1139, max1236, max1237, max11238, - max1239) Provides direct access via sysfs. + convertors (ADC). (max1361, max1362, max1363, max1364, max1036, + max1037, max1038, max1039, max1136, max1136, max1137, max1138, + max1139, max1236, max1237, max11238, max1239, max11600, max11601, + max11602, max11603, max11604, max11605, max11606, max11607, + max11608, max11609, max11610, max11611, max11612, max11613, + max11614, max11615, max11616, max11617) Provides direct access + via sysfs. config MAX1363_RING_BUFFER bool "MAXIM max1363: use ring buffer" diff --git a/drivers/staging/iio/adc/Makefile b/drivers/staging/iio/adc/Makefile index 08cee5c22b92..18c9376ecbb2 100644 --- a/drivers/staging/iio/adc/Makefile +++ b/drivers/staging/iio/adc/Makefile @@ -3,6 +3,6 @@ # max1363-y := max1363_core.o -max1363-$(CONFIG_MAX1363_RING_BUFFER) += max1363_ring.o +max1363-y += max1363_ring.o obj-$(CONFIG_MAX1363) += max1363.o diff --git a/drivers/staging/iio/adc/adc.h b/drivers/staging/iio/adc/adc.h index d925b2c5e38e..04eb16fd0a95 100644 --- a/drivers/staging/iio/adc/adc.h +++ b/drivers/staging/iio/adc/adc.h @@ -9,5 +9,20 @@ * */ +/* Deprecated */ #define IIO_DEV_ATTR_ADC(_num, _show, _addr) \ IIO_DEVICE_ATTR(adc_##_num, S_IRUGO, _show, NULL, _addr) + +#define IIO_DEV_ATTR_IN_RAW(_num, _show, _addr) \ + IIO_DEVICE_ATTR(in##_num##_raw, S_IRUGO, _show, NULL, _addr) + +#define IIO_DEV_ATTR_IN_NAMED_RAW(_name, _show, _addr) \ + IIO_DEVICE_ATTR(in_##_name##_raw, S_IRUGO, _show, NULL, _addr) + +#define IIO_DEV_ATTR_IN_DIFF_RAW(_nump, _numn, _show, _addr) \ + IIO_DEVICE_ATTR_NAMED(in##_nump##min##_numn##_raw, \ + in##_nump-in##_numn##_raw, \ + S_IRUGO, \ + _show, \ + NULL, \ + _addr) diff --git a/drivers/staging/iio/adc/max1363.h b/drivers/staging/iio/adc/max1363.h index c112fbef2705..72cf36709368 100644 --- a/drivers/staging/iio/adc/max1363.h +++ b/drivers/staging/iio/adc/max1363.h @@ -72,77 +72,54 @@ * @numvals: The number of values returned by a single scan */ struct max1363_mode { - const char *name; int8_t conf; - int numvals; + long modemask; }; -#define MAX1363_MODE_SINGLE(_num) { \ - .name = #_num, \ - .conf = MAX1363_CHANNEL_SEL(_num) \ +#define MAX1363_MODE_SINGLE(_num, _mask) { \ + .conf = MAX1363_CHANNEL_SEL(_num) \ | MAX1363_CONFIG_SCAN_SINGLE_1 \ | MAX1363_CONFIG_SE, \ - .numvals = 1, \ + .modemask = _mask, \ } -#define MAX1363_MODE_SINGLE_TIMES_8(_num) { \ - .name = #_num"x8", \ - .conf = MAX1363_CHANNEL_SEL(_num) \ - | MAX1363_CONFIG_SCAN_SINGLE_8 \ - | MAX1363_CONFIG_SE, \ - .numvals = 8, \ - } - -#define MAX1363_MODE_SCAN_TO_CHANNEL(_num) { \ - .name = "0..."#_num, \ - .conf = MAX1363_CHANNEL_SEL(_num) \ +#define MAX1363_MODE_SCAN_TO_CHANNEL(_num, _mask) { \ + .conf = MAX1363_CHANNEL_SEL(_num) \ | MAX1363_CONFIG_SCAN_TO_CS \ | MAX1363_CONFIG_SE, \ - .numvals = _num + 1, \ + .modemask = _mask, \ } /* note not available for max1363 hence naming */ -#define MAX1236_MODE_SCAN_MID_TO_CHANNEL(_mid, _num) { \ - .name = #_mid"..."#_num, \ - .conf = MAX1363_CHANNEL_SEL(_num) \ +#define MAX1236_MODE_SCAN_MID_TO_CHANNEL(_mid, _num, _mask) { \ + .conf = MAX1363_CHANNEL_SEL(_num) \ | MAX1236_SCAN_MID_TO_CHANNEL \ | MAX1363_CONFIG_SE, \ - .numvals = _num - _mid + 1 \ + .modemask = _mask \ } -#define MAX1363_MODE_DIFF_SINGLE(_nump, _numm) { \ - .name = #_nump"-"#_numm, \ - .conf = MAX1363_CHANNEL_SEL(_nump) \ +#define MAX1363_MODE_DIFF_SINGLE(_nump, _numm, _mask) { \ + .conf = MAX1363_CHANNEL_SEL(_nump) \ | MAX1363_CONFIG_SCAN_SINGLE_1 \ | MAX1363_CONFIG_DE, \ - .numvals = 1, \ - } - -#define MAX1363_MODE_DIFF_SINGLE_TIMES_8(_nump, _numm) { \ - .name = #_nump"-"#_numm, \ - .conf = MAX1363_CHANNEL_SEL(_nump) \ - | MAX1363_CONFIG_SCAN_SINGLE_8 \ - | MAX1363_CONFIG_DE, \ - .numvals = 1, \ + .modemask = _mask \ } /* Can't think how to automate naming so specify for now */ -#define MAX1363_MODE_DIFF_SCAN_TO_CHANNEL_NAMED(_name, _num, _numvals) { \ - .name = #_name, \ - .conf = MAX1363_CHANNEL_SEL(_num) \ +#define MAX1363_MODE_DIFF_SCAN_TO_CHANNEL(_num, _numvals, _mask) { \ + .conf = MAX1363_CHANNEL_SEL(_num) \ | MAX1363_CONFIG_SCAN_TO_CS \ | MAX1363_CONFIG_DE, \ - .numvals = _numvals, \ + .modemask = _mask \ } /* note only available for max1363 hence naming */ -#define MAX1236_MODE_DIFF_SCAN_MID_TO_CHANNEL_NAMED(_name, _num, _numvals) { \ - .name = #_name, \ - .conf = MAX1363_CHANNEL_SEL(_num) \ +#define MAX1236_MODE_DIFF_SCAN_MID_TO_CHANNEL(_num, _numvals, _mask) { \ + .conf = MAX1363_CHANNEL_SEL(_num) \ | MAX1236_SCAN_MID_TO_CHANNEL \ | MAX1363_CONFIG_SE, \ - .numvals = _numvals, \ + .modemask = _mask \ } /* Not currently handled */ @@ -158,35 +135,43 @@ struct max1363_mode { * clear what all the various options actually do. Alternative suggestions * that don't require user to have intimate knowledge of the chip welcomed. */ +enum max1363_channels { + max1363_in0, max1363_in1, max1363_in2, max1363_in3, + max1363_in4, max1363_in5, max1363_in6, max1363_in7, + max1363_in8, max1363_in9, max1363_in10, max1363_in11, + + max1363_in0min1, max1363_in2min3, + max1363_in4min5, max1363_in6min7, + max1363_in8min9, max1363_in10min11, + + max1363_in1min0, max1363_in3min2, + max1363_in5min4, max1363_in7min6, + max1363_in9min8, max1363_in11min10, + }; /* This must be maintained along side the max1363_mode_table in max1363_core */ enum max1363_modes { /* Single read of a single channel */ _s0, _s1, _s2, _s3, _s4, _s5, _s6, _s7, _s8, _s9, _s10, _s11, - /* Eight reads of a single channel */ - se0, se1, se2, se3, se4, se5, se6, se7, se8, se9, se10, se11, - /* Scan to channel */ - s0to1, s0to2, s0to3, s0to4, s0to5, s0to6, - s0to7, s0to8, s0to9, s0to10, s0to11, /* Differential single read */ d0m1, d2m3, d4m5, d6m7, d8m9, d10m11, d1m0, d3m2, d5m4, d7m6, d9m8, d11m10, - /* Differential single read 8 times */ - de0m1, de2m3, de4m5, de6m7, de8m9, de10m11, - de1m0, de3m2, de5m4, de7m6, de9m8, de11m10, - /* Differential scan to channel */ - d0m1to2m3, d0m1to4m5, d0m1to6m7, d0m1to8m9, d0m1to10m11, - d1m0to3m2, d1m0to5m4, d1m0to7m6, d1m0to9m8, d1m0to11m10, - /* Scan mid to channel max123{6-9} only */ - s2to3, s6to7, s6to8, s6to9, s6to10, s6to11, - /* Differential scan mid to channel */ - s6m7to8m9, s6m7to10m11, s7m6to9m8, s7m6to11m10, + /* Scan to channel and mid to channel where overlapping */ + s0to1, s0to2, s2to3, s0to3, s0to4, s0to5, s0to6, + s6to7, s0to7, s6to8, s0to8, s6to9, + s0to9, s6to10, s0to10, s6to11, s0to11, + /* Differential scan to channel and mid to channel where overlapping */ + d0m1to2m3, d0m1to4m5, d0m1to6m7, d6m7to8m9, + d0m1to8m9, d6m7to10m11, d0m1to10m11, d1m0to3m2, + d1m0to5m4, d1m0to7m6, d7m6to9m8, d1m0to9m8, + d7m6to11m10, d1m0to11m10, }; /** * struct max1363_chip_info - chip specifc information * @name: indentification string for chip * @num_inputs: number of physical inputs on chip + * @bits: accuracy of the adc in bits * @int_vref_mv: the internal reference voltage * @monitor_mode: whether the chip supports monitor interrupts * @mode_list: array of available scan modes @@ -196,11 +181,14 @@ enum max1363_modes { struct max1363_chip_info { const char *name; u8 num_inputs; + u8 bits; u16 int_vref_mv; bool monitor_mode; const enum max1363_modes *mode_list; int num_modes; enum max1363_modes default_mode; + struct attribute_group *dev_attrs; + struct attribute_group *scan_attrs; }; @@ -212,6 +200,7 @@ struct max1363_chip_info { * @configbyte: cache of current device config byte * @chip_info: chip model specific constants, available modes etc * @current_mode: the scan mode of this chip + * @requestedmask: a valid requested set of channels * @poll_work: bottom half of polling interrupt handler * @protect_ring: used to ensure only one polling bh running at a time * @reg: supply regulator @@ -223,16 +212,21 @@ struct max1363_state { char configbyte; const struct max1363_chip_info *chip_info; const struct max1363_mode *current_mode; + u32 requestedmask; struct work_struct poll_work; atomic_t protect_ring; struct iio_trigger *trig; struct regulator *reg; }; + +const struct max1363_mode +*max1363_match_mode(u32 mask, const struct max1363_chip_info *ci); + +int max1363_set_scan_mode(struct max1363_state *st); + #ifdef CONFIG_MAX1363_RING_BUFFER -ssize_t max1363_scan_from_ring(struct device *dev, - struct device_attribute *attr, - char *buf); +int max1363_single_channel_from_ring(long mask, struct max1363_state *st); int max1363_register_ring_funcs_and_init(struct iio_dev *indio_dev); void max1363_ring_cleanup(struct iio_dev *indio_dev); @@ -250,14 +244,12 @@ static inline int max1363_initialize_ring(struct iio_ring_buffer *ring) return 0; }; - -static inline ssize_t max1363_scan_from_ring(struct device *dev, - struct device_attribute *attr, - char *buf) +int max1363_single_channel_from_ring(long mask, struct max1363_state *st) { - return 0; + return -EINVAL; }; + static inline int max1363_register_ring_funcs_and_init(struct iio_dev *indio_dev) { diff --git a/drivers/staging/iio/adc/max1363_core.c b/drivers/staging/iio/adc/max1363_core.c index 773f1d1d9c6e..905f8560d31f 100644 --- a/drivers/staging/iio/adc/max1363_core.c +++ b/drivers/staging/iio/adc/max1363_core.c @@ -1,27 +1,26 @@ /* - * linux/drivers/industrialio/adc/max1363.c - * Copyright (C) 2008 Jonathan Cameron - * - * based on linux/drivers/i2c/chips/max123x - * Copyright (C) 2002-2004 Stefan Eletzhofer - * - * based on linux/drivers/acron/char/pcf8583.c - * Copyright (C) 2000 Russell King - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * max1363.c - * - * Partial support for max1363 and similar chips. - * - * Not currently implemented. - * - * - Monitor interrrupt generation. - * - Control of internal reference. - * - Sysfs scan interface currently assumes unipolar mode. - */ + * iio/adc/max1363.c + * Copyright (C) 2008-2010 Jonathan Cameron + * + * based on linux/drivers/i2c/chips/max123x + * Copyright (C) 2002-2004 Stefan Eletzhofer + * + * based on linux/drivers/acron/char/pcf8583.c + * Copyright (C) 2000 Russell King + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * max1363.c + * + * Partial support for max1363 and similar chips. + * + * Not currently implemented. + * + * - Monitor interrrupt generation. + * - Control of internal reference. + */ #include <linux/interrupt.h> #include <linux/gpio.h> @@ -38,118 +37,328 @@ #include "../iio.h" #include "../sysfs.h" +#include "../ring_generic.h" +#include "adc.h" #include "max1363.h" -/* Available scan modes. - * Awkwardly the associated enum is in the header so it is available to - * the ring buffer code. - */ -static const struct max1363_mode max1363_mode_table[] = { - MAX1363_MODE_SINGLE(0), - MAX1363_MODE_SINGLE(1), - MAX1363_MODE_SINGLE(2), - MAX1363_MODE_SINGLE(3), - MAX1363_MODE_SINGLE(4), - MAX1363_MODE_SINGLE(5), - MAX1363_MODE_SINGLE(6), - MAX1363_MODE_SINGLE(7), - MAX1363_MODE_SINGLE(8), - MAX1363_MODE_SINGLE(9), - MAX1363_MODE_SINGLE(10), - MAX1363_MODE_SINGLE(11), - - MAX1363_MODE_SINGLE_TIMES_8(0), - MAX1363_MODE_SINGLE_TIMES_8(1), - MAX1363_MODE_SINGLE_TIMES_8(2), - MAX1363_MODE_SINGLE_TIMES_8(3), - MAX1363_MODE_SINGLE_TIMES_8(4), - MAX1363_MODE_SINGLE_TIMES_8(5), - MAX1363_MODE_SINGLE_TIMES_8(6), - MAX1363_MODE_SINGLE_TIMES_8(7), - MAX1363_MODE_SINGLE_TIMES_8(8), - MAX1363_MODE_SINGLE_TIMES_8(9), - MAX1363_MODE_SINGLE_TIMES_8(10), - MAX1363_MODE_SINGLE_TIMES_8(11), - - MAX1363_MODE_SCAN_TO_CHANNEL(1), - MAX1363_MODE_SCAN_TO_CHANNEL(2), - MAX1363_MODE_SCAN_TO_CHANNEL(3), - MAX1363_MODE_SCAN_TO_CHANNEL(4), - MAX1363_MODE_SCAN_TO_CHANNEL(5), - MAX1363_MODE_SCAN_TO_CHANNEL(6), - MAX1363_MODE_SCAN_TO_CHANNEL(7), - MAX1363_MODE_SCAN_TO_CHANNEL(8), - MAX1363_MODE_SCAN_TO_CHANNEL(9), - MAX1363_MODE_SCAN_TO_CHANNEL(10), - MAX1363_MODE_SCAN_TO_CHANNEL(11), - - MAX1363_MODE_DIFF_SINGLE(0, 1), - MAX1363_MODE_DIFF_SINGLE(2, 3), - MAX1363_MODE_DIFF_SINGLE(4, 5), - MAX1363_MODE_DIFF_SINGLE(6, 7), - MAX1363_MODE_DIFF_SINGLE(8, 9), - MAX1363_MODE_DIFF_SINGLE(10, 11), - MAX1363_MODE_DIFF_SINGLE(1, 0), - MAX1363_MODE_DIFF_SINGLE(3, 2), - MAX1363_MODE_DIFF_SINGLE(5, 4), - MAX1363_MODE_DIFF_SINGLE(7, 6), - MAX1363_MODE_DIFF_SINGLE(9, 8), - MAX1363_MODE_DIFF_SINGLE(11, 10), - - MAX1363_MODE_DIFF_SINGLE_TIMES_8(0, 1), - MAX1363_MODE_DIFF_SINGLE_TIMES_8(2, 3), - MAX1363_MODE_DIFF_SINGLE_TIMES_8(4, 5), - MAX1363_MODE_DIFF_SINGLE_TIMES_8(6, 7), - MAX1363_MODE_DIFF_SINGLE_TIMES_8(8, 9), - MAX1363_MODE_DIFF_SINGLE_TIMES_8(10, 11), - MAX1363_MODE_DIFF_SINGLE_TIMES_8(1, 0), - MAX1363_MODE_DIFF_SINGLE_TIMES_8(3, 2), - MAX1363_MODE_DIFF_SINGLE_TIMES_8(5, 4), - MAX1363_MODE_DIFF_SINGLE_TIMES_8(7, 6), - MAX1363_MODE_DIFF_SINGLE_TIMES_8(9, 8), - MAX1363_MODE_DIFF_SINGLE_TIMES_8(11, 10), - - MAX1363_MODE_DIFF_SCAN_TO_CHANNEL_NAMED(0-1...2-3, 2, 2), - MAX1363_MODE_DIFF_SCAN_TO_CHANNEL_NAMED(0-1...4-5, 4, 3), - MAX1363_MODE_DIFF_SCAN_TO_CHANNEL_NAMED(0-1...6-7, 6, 4), - MAX1363_MODE_DIFF_SCAN_TO_CHANNEL_NAMED(0-1...8-9, 8, 5), - MAX1363_MODE_DIFF_SCAN_TO_CHANNEL_NAMED(0-1...10-11, 10, 6), - MAX1363_MODE_DIFF_SCAN_TO_CHANNEL_NAMED(1-0...3-2, 3, 2), - MAX1363_MODE_DIFF_SCAN_TO_CHANNEL_NAMED(1-0...5-4, 5, 3), - MAX1363_MODE_DIFF_SCAN_TO_CHANNEL_NAMED(1-0...7-6, 7, 4), - MAX1363_MODE_DIFF_SCAN_TO_CHANNEL_NAMED(1-0...9-8, 9, 5), - MAX1363_MODE_DIFF_SCAN_TO_CHANNEL_NAMED(1-0...11-10, 11, 6), - - MAX1236_MODE_SCAN_MID_TO_CHANNEL(2, 3), - MAX1236_MODE_SCAN_MID_TO_CHANNEL(6, 7), - MAX1236_MODE_SCAN_MID_TO_CHANNEL(6, 8), - MAX1236_MODE_SCAN_MID_TO_CHANNEL(6, 9), - MAX1236_MODE_SCAN_MID_TO_CHANNEL(6, 10), - MAX1236_MODE_SCAN_MID_TO_CHANNEL(6, 11), - - MAX1236_MODE_DIFF_SCAN_MID_TO_CHANNEL_NAMED(6-7...8-9, 8, 2), - MAX1236_MODE_DIFF_SCAN_MID_TO_CHANNEL_NAMED(6-7...10-11, 10, 3), - MAX1236_MODE_DIFF_SCAN_MID_TO_CHANNEL_NAMED(7-6...9-8, 9, 2), - MAX1236_MODE_DIFF_SCAN_MID_TO_CHANNEL_NAMED(7-6...11-10, 11, 3), +/* Here we claim all are 16 bits. This currently does no harm and saves + * us a lot of scan element listings */ + +#define MAX1363_SCAN_EL(number) \ + IIO_SCAN_EL_C(in##number, number, IIO_UNSIGNED(16), 0, NULL); +#define MAX1363_SCAN_EL_D(p, n, number) \ + IIO_SCAN_NAMED_EL_C(in##p##m##in##n, in##p-in##n, \ + number, IIO_SIGNED(16), 0 , NULL); + +static MAX1363_SCAN_EL(0); +static MAX1363_SCAN_EL(1); +static MAX1363_SCAN_EL(2); +static MAX1363_SCAN_EL(3); +static MAX1363_SCAN_EL(4); +static MAX1363_SCAN_EL(5); +static MAX1363_SCAN_EL(6); +static MAX1363_SCAN_EL(7); +static MAX1363_SCAN_EL(8); +static MAX1363_SCAN_EL(9); +static MAX1363_SCAN_EL(10); +static MAX1363_SCAN_EL(11); +static MAX1363_SCAN_EL_D(0, 1, 12); +static MAX1363_SCAN_EL_D(2, 3, 13); +static MAX1363_SCAN_EL_D(4, 5, 14); +static MAX1363_SCAN_EL_D(6, 7, 15); +static MAX1363_SCAN_EL_D(8, 9, 16); +static MAX1363_SCAN_EL_D(10, 11, 17); +static MAX1363_SCAN_EL_D(1, 0, 18); +static MAX1363_SCAN_EL_D(3, 2, 19); +static MAX1363_SCAN_EL_D(5, 4, 20); +static MAX1363_SCAN_EL_D(7, 6, 21); +static MAX1363_SCAN_EL_D(9, 8, 22); +static MAX1363_SCAN_EL_D(11, 10, 23); + +static const struct max1363_mode max1363_mode_table[] = { + /* All of the single channel options first */ + MAX1363_MODE_SINGLE(0, 1 << 0), + MAX1363_MODE_SINGLE(1, 1 << 1), + MAX1363_MODE_SINGLE(2, 1 << 2), + MAX1363_MODE_SINGLE(3, 1 << 3), + MAX1363_MODE_SINGLE(4, 1 << 4), + MAX1363_MODE_SINGLE(5, 1 << 5), + MAX1363_MODE_SINGLE(6, 1 << 6), + MAX1363_MODE_SINGLE(7, 1 << 7), + MAX1363_MODE_SINGLE(8, 1 << 8), + MAX1363_MODE_SINGLE(9, 1 << 9), + MAX1363_MODE_SINGLE(10, 1 << 10), + MAX1363_MODE_SINGLE(11, 1 << 11), + + MAX1363_MODE_DIFF_SINGLE(0, 1, 1 << 12), + MAX1363_MODE_DIFF_SINGLE(2, 3, 1 << 13), + MAX1363_MODE_DIFF_SINGLE(4, 5, 1 << 14), + MAX1363_MODE_DIFF_SINGLE(6, 7, 1 << 15), + MAX1363_MODE_DIFF_SINGLE(8, 9, 1 << 16), + MAX1363_MODE_DIFF_SINGLE(10, 11, 1 << 17), + MAX1363_MODE_DIFF_SINGLE(1, 0, 1 << 18), + MAX1363_MODE_DIFF_SINGLE(3, 2, 1 << 19), + MAX1363_MODE_DIFF_SINGLE(5, 4, 1 << 20), + MAX1363_MODE_DIFF_SINGLE(7, 6, 1 << 21), + MAX1363_MODE_DIFF_SINGLE(9, 8, 1 << 22), + MAX1363_MODE_DIFF_SINGLE(11, 10, 1 << 23), + + /* The multichannel scans next */ + MAX1363_MODE_SCAN_TO_CHANNEL(1, 0x003), + MAX1363_MODE_SCAN_TO_CHANNEL(2, 0x007), + MAX1236_MODE_SCAN_MID_TO_CHANNEL(2, 3, 0x00C), + MAX1363_MODE_SCAN_TO_CHANNEL(3, 0x00F), + MAX1363_MODE_SCAN_TO_CHANNEL(4, 0x01F), + MAX1363_MODE_SCAN_TO_CHANNEL(5, 0x03F), + MAX1363_MODE_SCAN_TO_CHANNEL(6, 0x07F), + MAX1236_MODE_SCAN_MID_TO_CHANNEL(6, 7, 0x0C0), + MAX1363_MODE_SCAN_TO_CHANNEL(7, 0x0FF), + MAX1236_MODE_SCAN_MID_TO_CHANNEL(6, 8, 0x1C0), + MAX1363_MODE_SCAN_TO_CHANNEL(8, 0x1FF), + MAX1236_MODE_SCAN_MID_TO_CHANNEL(6, 9, 0x3C0), + MAX1363_MODE_SCAN_TO_CHANNEL(9, 0x3FF), + MAX1236_MODE_SCAN_MID_TO_CHANNEL(6, 10, 0x7C0), + MAX1363_MODE_SCAN_TO_CHANNEL(10, 0x7FF), + MAX1236_MODE_SCAN_MID_TO_CHANNEL(6, 11, 0xFC0), + MAX1363_MODE_SCAN_TO_CHANNEL(11, 0xFFF), + + MAX1363_MODE_DIFF_SCAN_TO_CHANNEL(2, 2, 0x003000), + MAX1363_MODE_DIFF_SCAN_TO_CHANNEL(4, 3, 0x007000), + MAX1363_MODE_DIFF_SCAN_TO_CHANNEL(6, 4, 0x00F000), + MAX1236_MODE_DIFF_SCAN_MID_TO_CHANNEL(8, 2, 0x018000), + MAX1363_MODE_DIFF_SCAN_TO_CHANNEL(8, 5, 0x01F000), + MAX1236_MODE_DIFF_SCAN_MID_TO_CHANNEL(10, 3, 0x038000), + MAX1363_MODE_DIFF_SCAN_TO_CHANNEL(10, 6, 0x3F000), + MAX1363_MODE_DIFF_SCAN_TO_CHANNEL(3, 2, 0x0C0000), + MAX1363_MODE_DIFF_SCAN_TO_CHANNEL(5, 3, 0x1C0000), + MAX1363_MODE_DIFF_SCAN_TO_CHANNEL(7, 4, 0x3C0000), + MAX1236_MODE_DIFF_SCAN_MID_TO_CHANNEL(9, 2, 0x600000), + MAX1363_MODE_DIFF_SCAN_TO_CHANNEL(9, 5, 0x7C0000), + MAX1236_MODE_DIFF_SCAN_MID_TO_CHANNEL(11, 3, 0xE00000), + MAX1363_MODE_DIFF_SCAN_TO_CHANNEL(11, 6, 0xFC0000), }; +const struct max1363_mode +*max1363_match_mode(u32 mask, const struct max1363_chip_info *ci) +{ + int i; + if (mask) + for (i = 0; i < ci->num_modes; i++) + if (!((~max1363_mode_table[ci->mode_list[i]].modemask) & + mask)) + return &max1363_mode_table[ci->mode_list[i]]; + return NULL; +}; + +static ssize_t max1363_show_precision(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct iio_dev *dev_info = dev_get_drvdata(dev); + struct max1363_state *st = iio_dev_get_devdata(dev_info); + return sprintf(buf, "%d\n", st->chip_info->bits); +} + +static IIO_DEVICE_ATTR(in_precision, S_IRUGO, max1363_show_precision, + NULL, 0); + +static int max1363_write_basic_config(struct i2c_client *client, + unsigned char d1, + unsigned char d2) +{ + int ret; + u8 *tx_buf = kmalloc(2 , GFP_KERNEL); + + if (!tx_buf) + return -ENOMEM; + tx_buf[0] = d1; + tx_buf[1] = d2; + + ret = i2c_master_send(client, tx_buf, 2); + kfree(tx_buf); + + return (ret > 0) ? 0 : ret; +} + +int max1363_set_scan_mode(struct max1363_state *st) +{ + st->configbyte &= ~(MAX1363_CHANNEL_SEL_MASK + | MAX1363_SCAN_MASK + | MAX1363_SE_DE_MASK); + st->configbyte |= st->current_mode->conf; + + return max1363_write_basic_config(st->client, + st->setupbyte, + st->configbyte); +} + +static ssize_t max1363_read_single_channel(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct iio_dev *dev_info = dev_get_drvdata(dev); + struct max1363_state *st = iio_dev_get_devdata(dev_info); + struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); + struct i2c_client *client = st->client; + int ret = 0, len = 0; + s32 data ; + char rxbuf[2]; + long mask; + + mutex_lock(&dev_info->mlock); + /* If ring buffer capture is occuring, query the buffer */ + if (iio_ring_enabled(dev_info)) { + mask = max1363_mode_table[this_attr->address].modemask; + data = max1363_single_channel_from_ring(mask, st); + if (data < 0) { + ret = data; + goto error_ret; + } + } else { + /* Check to see if current scan mode is correct */ + if (st->current_mode != + &max1363_mode_table[this_attr->address]) { + /* Update scan mode if needed */ + st->current_mode + = &max1363_mode_table[this_attr->address]; + ret = max1363_set_scan_mode(st); + if (ret) + goto error_ret; + } + if (st->chip_info->bits != 8) { + /* Get reading */ + data = i2c_master_recv(client, rxbuf, 2); + if (data < 0) { + ret = data; + goto error_ret; + } + + data = (s32)(rxbuf[1]) | ((s32)(rxbuf[0] & 0x0F)) << 8; + } else { + /* Get reading */ + data = i2c_master_recv(client, rxbuf, 1); + if (data < 0) { + ret = data; + goto error_ret; + } + data = rxbuf[0]; + } + } + /* Pretty print the result */ + len = sprintf(buf, "%u\n", data); + +error_ret: + mutex_unlock(&dev_info->mlock); + return ret ? ret : len; +} + +/* Direct read attribtues */ +static IIO_DEV_ATTR_IN_RAW(0, max1363_read_single_channel, _s0); +static IIO_DEV_ATTR_IN_RAW(1, max1363_read_single_channel, _s1); +static IIO_DEV_ATTR_IN_RAW(2, max1363_read_single_channel, _s2); +static IIO_DEV_ATTR_IN_RAW(3, max1363_read_single_channel, _s3); +static IIO_DEV_ATTR_IN_RAW(4, max1363_read_single_channel, _s4); +static IIO_DEV_ATTR_IN_RAW(5, max1363_read_single_channel, _s5); +static IIO_DEV_ATTR_IN_RAW(6, max1363_read_single_channel, _s6); +static IIO_DEV_ATTR_IN_RAW(7, max1363_read_single_channel, _s7); +static IIO_DEV_ATTR_IN_RAW(8, max1363_read_single_channel, _s8); +static IIO_DEV_ATTR_IN_RAW(9, max1363_read_single_channel, _s9); +static IIO_DEV_ATTR_IN_RAW(10, max1363_read_single_channel, _s10); +static IIO_DEV_ATTR_IN_RAW(11, max1363_read_single_channel, _s11); + +static IIO_DEV_ATTR_IN_DIFF_RAW(0, 1, max1363_read_single_channel, d0m1); +static IIO_DEV_ATTR_IN_DIFF_RAW(2, 3, max1363_read_single_channel, d2m3); +static IIO_DEV_ATTR_IN_DIFF_RAW(4, 5, max1363_read_single_channel, d4m5); +static IIO_DEV_ATTR_IN_DIFF_RAW(6, 7, max1363_read_single_channel, d6m7); +static IIO_DEV_ATTR_IN_DIFF_RAW(8, 9, max1363_read_single_channel, d8m9); +static IIO_DEV_ATTR_IN_DIFF_RAW(10, 11, max1363_read_single_channel, d10m11); +static IIO_DEV_ATTR_IN_DIFF_RAW(1, 0, max1363_read_single_channel, d1m0); +static IIO_DEV_ATTR_IN_DIFF_RAW(3, 2, max1363_read_single_channel, d3m2); +static IIO_DEV_ATTR_IN_DIFF_RAW(5, 4, max1363_read_single_channel, d5m4); +static IIO_DEV_ATTR_IN_DIFF_RAW(7, 6, max1363_read_single_channel, d7m6); +static IIO_DEV_ATTR_IN_DIFF_RAW(9, 8, max1363_read_single_channel, d9m8); +static IIO_DEV_ATTR_IN_DIFF_RAW(11, 10, max1363_read_single_channel, d11m10); + + +static ssize_t max1363_show_scale(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + /* Driver currently only support internal vref */ + struct iio_dev *dev_info = dev_get_drvdata(dev); + struct max1363_state *st = iio_dev_get_devdata(dev_info); + /* Corresponds to Vref / 2^(bits) */ + + if ((1 << (st->chip_info->bits + 1)) + > st->chip_info->int_vref_mv) + return sprintf(buf, "0.5\n"); + else + return sprintf(buf, "%d\n", + st->chip_info->int_vref_mv >> st->chip_info->bits); +} + +static IIO_DEVICE_ATTR(in_scale, S_IRUGO, max1363_show_scale, NULL, 0); + +static ssize_t max1363_show_name(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct iio_dev *dev_info = dev_get_drvdata(dev); + struct max1363_state *st = iio_dev_get_devdata(dev_info); + return sprintf(buf, "%s\n", st->chip_info->name); +} + +static IIO_DEVICE_ATTR(name, S_IRUGO, max1363_show_name, NULL, 0); + /* Applies to max1363 */ static const enum max1363_modes max1363_mode_list[] = { _s0, _s1, _s2, _s3, - se0, se1, se2, se3, s0to1, s0to2, s0to3, d0m1, d2m3, d1m0, d3m2, - de0m1, de2m3, de1m0, de3m2, d0m1to2m3, d1m0to3m2, }; +static struct attribute *max1363_device_attrs[] = { + &iio_dev_attr_in0_raw.dev_attr.attr, + &iio_dev_attr_in1_raw.dev_attr.attr, + &iio_dev_attr_in2_raw.dev_attr.attr, + &iio_dev_attr_in3_raw.dev_attr.attr, + &iio_dev_attr_in0min1_raw.dev_attr.attr, + &iio_dev_attr_in2min3_raw.dev_attr.attr, + &iio_dev_attr_in1min0_raw.dev_attr.attr, + &iio_dev_attr_in3min2_raw.dev_attr.attr, + &iio_dev_attr_name.dev_attr.attr, + &iio_dev_attr_in_scale.dev_attr.attr, + NULL +}; + +static struct attribute_group max1363_dev_attr_group = { + .attrs = max1363_device_attrs, +}; + +static struct attribute *max1363_scan_el_attrs[] = { + &iio_scan_el_in0.dev_attr.attr, + &iio_scan_el_in1.dev_attr.attr, + &iio_scan_el_in2.dev_attr.attr, + &iio_scan_el_in3.dev_attr.attr, + &iio_scan_el_in0min1.dev_attr.attr, + &iio_scan_el_in2min3.dev_attr.attr, + &iio_scan_el_in1min0.dev_attr.attr, + &iio_scan_el_in3min2.dev_attr.attr, + &iio_dev_attr_in_precision.dev_attr.attr, + NULL, +}; + +static struct attribute_group max1363_scan_el_group = { + .name = "scan_elements", + .attrs = max1363_scan_el_attrs, +}; + /* Appies to max1236, max1237 */ static const enum max1363_modes max1236_mode_list[] = { _s0, _s1, _s2, _s3, - se0, se1, se2, se3, s0to1, s0to2, s0to3, d0m1, d2m3, d1m0, d3m2, - de0m1, de2m3, de1m0, de3m2, d0m1to2m3, d1m0to3m2, s2to3, }; @@ -157,24 +366,162 @@ static const enum max1363_modes max1236_mode_list[] = { /* Applies to max1238, max1239 */ static const enum max1363_modes max1238_mode_list[] = { _s0, _s1, _s2, _s3, _s4, _s5, _s6, _s7, _s8, _s9, _s10, _s11, - se0, se1, se2, se3, se4, se5, se6, se7, se8, se9, se10, se11, s0to1, s0to2, s0to3, s0to4, s0to5, s0to6, s0to7, s0to8, s0to9, s0to10, s0to11, d0m1, d2m3, d4m5, d6m7, d8m9, d10m11, d1m0, d3m2, d5m4, d7m6, d9m8, d11m10, - de0m1, de2m3, de4m5, de6m7, de8m9, de10m11, - de1m0, de3m2, de5m4, de7m6, de9m8, de11m10, d0m1to2m3, d0m1to4m5, d0m1to6m7, d0m1to8m9, d0m1to10m11, d1m0to3m2, d1m0to5m4, d1m0to7m6, d1m0to9m8, d1m0to11m10, s6to7, s6to8, s6to9, s6to10, s6to11, - s6m7to8m9, s6m7to10m11, s7m6to9m8, s7m6to11m10, + d6m7to8m9, d6m7to10m11, d7m6to9m8, d7m6to11m10, +}; + +static struct attribute *max1238_device_attrs[] = { + &iio_dev_attr_in0_raw.dev_attr.attr, + &iio_dev_attr_in1_raw.dev_attr.attr, + &iio_dev_attr_in2_raw.dev_attr.attr, + &iio_dev_attr_in3_raw.dev_attr.attr, + &iio_dev_attr_in4_raw.dev_attr.attr, + &iio_dev_attr_in5_raw.dev_attr.attr, + &iio_dev_attr_in6_raw.dev_attr.attr, + &iio_dev_attr_in7_raw.dev_attr.attr, + &iio_dev_attr_in8_raw.dev_attr.attr, + &iio_dev_attr_in9_raw.dev_attr.attr, + &iio_dev_attr_in10_raw.dev_attr.attr, + &iio_dev_attr_in11_raw.dev_attr.attr, + &iio_dev_attr_in0min1_raw.dev_attr.attr, + &iio_dev_attr_in2min3_raw.dev_attr.attr, + &iio_dev_attr_in4min5_raw.dev_attr.attr, + &iio_dev_attr_in6min7_raw.dev_attr.attr, + &iio_dev_attr_in8min9_raw.dev_attr.attr, + &iio_dev_attr_in10min11_raw.dev_attr.attr, + &iio_dev_attr_in1min0_raw.dev_attr.attr, + &iio_dev_attr_in3min2_raw.dev_attr.attr, + &iio_dev_attr_in5min4_raw.dev_attr.attr, + &iio_dev_attr_in7min6_raw.dev_attr.attr, + &iio_dev_attr_in9min8_raw.dev_attr.attr, + &iio_dev_attr_in11min10_raw.dev_attr.attr, + &iio_dev_attr_name.dev_attr.attr, + &iio_dev_attr_in_scale.dev_attr.attr, + NULL +}; + +static struct attribute_group max1238_dev_attr_group = { + .attrs = max1238_device_attrs, +}; + +static struct attribute *max1238_scan_el_attrs[] = { + &iio_scan_el_in0.dev_attr.attr, + &iio_scan_el_in1.dev_attr.attr, + &iio_scan_el_in2.dev_attr.attr, + &iio_scan_el_in3.dev_attr.attr, + &iio_scan_el_in4.dev_attr.attr, + &iio_scan_el_in5.dev_attr.attr, + &iio_scan_el_in6.dev_attr.attr, + &iio_scan_el_in7.dev_attr.attr, + &iio_scan_el_in8.dev_attr.attr, + &iio_scan_el_in9.dev_attr.attr, + &iio_scan_el_in10.dev_attr.attr, + &iio_scan_el_in11.dev_attr.attr, + &iio_scan_el_in0min1.dev_attr.attr, + &iio_scan_el_in2min3.dev_attr.attr, + &iio_scan_el_in4min5.dev_attr.attr, + &iio_scan_el_in6min7.dev_attr.attr, + &iio_scan_el_in8min9.dev_attr.attr, + &iio_scan_el_in10min11.dev_attr.attr, + &iio_scan_el_in1min0.dev_attr.attr, + &iio_scan_el_in3min2.dev_attr.attr, + &iio_scan_el_in5min4.dev_attr.attr, + &iio_scan_el_in7min6.dev_attr.attr, + &iio_scan_el_in9min8.dev_attr.attr, + &iio_scan_el_in11min10.dev_attr.attr, + &iio_dev_attr_in_precision.dev_attr.attr, + NULL, +}; + +static struct attribute_group max1238_scan_el_group = { + .name = "scan_elements", + .attrs = max1238_scan_el_attrs, }; +static const enum max1363_modes max11607_mode_list[] = { + _s0, _s1, _s2, _s3, + s0to1, s0to2, s0to3, + s2to3, + d0m1, d2m3, d1m0, d3m2, + d0m1to2m3, d1m0to3m2, +}; + +static const enum max1363_modes max11608_mode_list[] = { + _s0, _s1, _s2, _s3, _s4, _s5, _s6, _s7, + s0to1, s0to2, s0to3, s0to4, s0to5, s0to6, s0to7, + s6to7, + d0m1, d2m3, d4m5, d6m7, + d1m0, d3m2, d5m4, d7m6, + d0m1to2m3, d0m1to4m5, d0m1to6m7, + d1m0to3m2, d1m0to5m4, d1m0to7m6, +}; + +static struct attribute *max11608_device_attrs[] = { + &iio_dev_attr_in0_raw.dev_attr.attr, + &iio_dev_attr_in1_raw.dev_attr.attr, + &iio_dev_attr_in2_raw.dev_attr.attr, + &iio_dev_attr_in3_raw.dev_attr.attr, + &iio_dev_attr_in4_raw.dev_attr.attr, + &iio_dev_attr_in5_raw.dev_attr.attr, + &iio_dev_attr_in6_raw.dev_attr.attr, + &iio_dev_attr_in7_raw.dev_attr.attr, + &iio_dev_attr_in0min1_raw.dev_attr.attr, + &iio_dev_attr_in2min3_raw.dev_attr.attr, + &iio_dev_attr_in4min5_raw.dev_attr.attr, + &iio_dev_attr_in6min7_raw.dev_attr.attr, + &iio_dev_attr_in1min0_raw.dev_attr.attr, + &iio_dev_attr_in3min2_raw.dev_attr.attr, + &iio_dev_attr_in5min4_raw.dev_attr.attr, + &iio_dev_attr_in7min6_raw.dev_attr.attr, + &iio_dev_attr_name.dev_attr.attr, + &iio_dev_attr_in_scale.dev_attr.attr, + NULL +}; + +static struct attribute_group max11608_dev_attr_group = { + .attrs = max11608_device_attrs, +}; + +static struct attribute *max11608_scan_el_attrs[] = { + &iio_scan_el_in0.dev_attr.attr, + &iio_scan_el_in1.dev_attr.attr, + &iio_scan_el_in2.dev_attr.attr, + &iio_scan_el_in3.dev_attr.attr, + &iio_scan_el_in4.dev_attr.attr, + &iio_scan_el_in5.dev_attr.attr, + &iio_scan_el_in6.dev_attr.attr, + &iio_scan_el_in7.dev_attr.attr, + &iio_scan_el_in0min1.dev_attr.attr, + &iio_scan_el_in2min3.dev_attr.attr, + &iio_scan_el_in4min5.dev_attr.attr, + &iio_scan_el_in6min7.dev_attr.attr, + &iio_scan_el_in1min0.dev_attr.attr, + &iio_scan_el_in3min2.dev_attr.attr, + &iio_scan_el_in5min4.dev_attr.attr, + &iio_scan_el_in7min6.dev_attr.attr, + &iio_dev_attr_in_precision.dev_attr.attr, +}; + +static struct attribute_group max11608_scan_el_group = { + .name = "scan_elements", + .attrs = max11608_scan_el_attrs, +}; + enum { max1361, max1362, max1363, max1364, + max1036, + max1037, + max1038, + max1039, max1136, max1137, max1138, @@ -183,6 +530,24 @@ enum { max1361, max1237, max1238, max1239, + max11600, + max11601, + max11602, + max11603, + max11604, + max11605, + max11606, + max11607, + max11608, + max11609, + max11610, + max11611, + max11612, + max11613, + max11614, + max11615, + max11616, + max11617, }; /* max1363 and max1368 tested - rest from data sheet */ @@ -190,118 +555,350 @@ static const struct max1363_chip_info max1363_chip_info_tbl[] = { { .name = "max1361", .num_inputs = 4, + .bits = 10, + .int_vref_mv = 2048, .monitor_mode = 1, .mode_list = max1363_mode_list, .num_modes = ARRAY_SIZE(max1363_mode_list), .default_mode = s0to3, + .dev_attrs = &max1363_dev_attr_group, + .scan_attrs = &max1363_scan_el_group, }, { .name = "max1362", .num_inputs = 4, + .bits = 10, + .int_vref_mv = 4096, .monitor_mode = 1, .mode_list = max1363_mode_list, .num_modes = ARRAY_SIZE(max1363_mode_list), .default_mode = s0to3, + .dev_attrs = &max1363_dev_attr_group, + .scan_attrs = &max1363_scan_el_group, }, { .name = "max1363", .num_inputs = 4, + .bits = 12, + .int_vref_mv = 2048, .monitor_mode = 1, .mode_list = max1363_mode_list, .num_modes = ARRAY_SIZE(max1363_mode_list), .default_mode = s0to3, + .dev_attrs = &max1363_dev_attr_group, + .scan_attrs = &max1363_scan_el_group, }, { .name = "max1364", .num_inputs = 4, + .bits = 12, + .int_vref_mv = 4096, .monitor_mode = 1, .mode_list = max1363_mode_list, .num_modes = ARRAY_SIZE(max1363_mode_list), .default_mode = s0to3, + .dev_attrs = &max1363_dev_attr_group, + .scan_attrs = &max1363_scan_el_group, + }, { + .name = "max1036", + .num_inputs = 4, + .bits = 8, + .int_vref_mv = 4096, + .mode_list = max1236_mode_list, + .num_modes = ARRAY_SIZE(max1236_mode_list), + .default_mode = s0to3, + .dev_attrs = &max1363_dev_attr_group, + .scan_attrs = &max1363_scan_el_group, + }, { + .name = "max1037", + .num_inputs = 4, + .bits = 8, + .int_vref_mv = 2048, + .mode_list = max1236_mode_list, + .num_modes = ARRAY_SIZE(max1236_mode_list), + .default_mode = s0to3, + .dev_attrs = &max1363_dev_attr_group, + .scan_attrs = &max1363_scan_el_group, + }, { + .name = "max1038", + .num_inputs = 12, + .bits = 8, + .int_vref_mv = 4096, + .mode_list = max1238_mode_list, + .num_modes = ARRAY_SIZE(max1238_mode_list), + .default_mode = s0to11, + .dev_attrs = &max1238_dev_attr_group, + .scan_attrs = &max1238_scan_el_group, + }, { + .name = "max1039", + .num_inputs = 12, + .bits = 8, + .int_vref_mv = 2048, + .mode_list = max1238_mode_list, + .num_modes = ARRAY_SIZE(max1238_mode_list), + .default_mode = s0to11, + .dev_attrs = &max1238_dev_attr_group, + .scan_attrs = &max1238_scan_el_group, }, { .name = "max1136", .num_inputs = 4, + .bits = 10, .int_vref_mv = 4096, .mode_list = max1236_mode_list, .num_modes = ARRAY_SIZE(max1236_mode_list), .default_mode = s0to3, + .dev_attrs = &max1363_dev_attr_group, + .scan_attrs = &max1363_scan_el_group, }, { .name = "max1137", .num_inputs = 4, + .bits = 10, .int_vref_mv = 2048, .mode_list = max1236_mode_list, .num_modes = ARRAY_SIZE(max1236_mode_list), .default_mode = s0to3, + .dev_attrs = &max1363_dev_attr_group, + .scan_attrs = &max1363_scan_el_group, }, { .name = "max1138", .num_inputs = 12, + .bits = 10, .int_vref_mv = 4096, .mode_list = max1238_mode_list, .num_modes = ARRAY_SIZE(max1238_mode_list), .default_mode = s0to11, + .dev_attrs = &max1238_dev_attr_group, + .scan_attrs = &max1238_scan_el_group, }, { .name = "max1139", .num_inputs = 12, + .bits = 10, .int_vref_mv = 2048, .mode_list = max1238_mode_list, .num_modes = ARRAY_SIZE(max1238_mode_list), .default_mode = s0to11, + .dev_attrs = &max1238_dev_attr_group, + .scan_attrs = &max1238_scan_el_group, }, { .name = "max1236", .num_inputs = 4, + .bits = 12, .int_vref_mv = 4096, .mode_list = max1236_mode_list, .num_modes = ARRAY_SIZE(max1236_mode_list), .default_mode = s0to3, + .dev_attrs = &max1363_dev_attr_group, + .scan_attrs = &max1363_scan_el_group, }, { .name = "max1237", .num_inputs = 4, + .bits = 12, .int_vref_mv = 2048, .mode_list = max1236_mode_list, .num_modes = ARRAY_SIZE(max1236_mode_list), .default_mode = s0to3, + .dev_attrs = &max1363_dev_attr_group, + .scan_attrs = &max1363_scan_el_group, }, { .name = "max1238", .num_inputs = 12, + .bits = 12, .int_vref_mv = 4096, .mode_list = max1238_mode_list, .num_modes = ARRAY_SIZE(max1238_mode_list), .default_mode = s0to11, + .dev_attrs = &max1238_dev_attr_group, + .scan_attrs = &max1238_scan_el_group, }, { .name = "max1239", .num_inputs = 12, + .bits = 12, .int_vref_mv = 2048, .mode_list = max1238_mode_list, .num_modes = ARRAY_SIZE(max1238_mode_list), .default_mode = s0to11, - }, + .dev_attrs = &max1238_dev_attr_group, + .scan_attrs = &max1238_scan_el_group, + }, { + .name = "max11600", + .num_inputs = 4, + .bits = 8, + .int_vref_mv = 4096, + .mode_list = max11607_mode_list, + .num_modes = ARRAY_SIZE(max11607_mode_list), + .default_mode = s0to3, + .dev_attrs = &max1363_dev_attr_group, + .scan_attrs = &max1363_scan_el_group, + }, { + .name = "max11601", + .num_inputs = 4, + .bits = 8, + .int_vref_mv = 2048, + .mode_list = max11607_mode_list, + .num_modes = ARRAY_SIZE(max11607_mode_list), + .default_mode = s0to3, + .dev_attrs = &max1363_dev_attr_group, + .scan_attrs = &max1363_scan_el_group, + }, { + .name = "max11602", + .num_inputs = 8, + .bits = 8, + .int_vref_mv = 4096, + .mode_list = max11608_mode_list, + .num_modes = ARRAY_SIZE(max11608_mode_list), + .default_mode = s0to7, + .dev_attrs = &max11608_dev_attr_group, + .scan_attrs = &max11608_scan_el_group, + }, { + .name = "max11603", + .num_inputs = 8, + .bits = 8, + .int_vref_mv = 2048, + .mode_list = max11608_mode_list, + .num_modes = ARRAY_SIZE(max11608_mode_list), + .default_mode = s0to7, + .dev_attrs = &max11608_dev_attr_group, + .scan_attrs = &max11608_scan_el_group, + }, { + .name = "max11604", + .num_inputs = 12, + .bits = 8, + .int_vref_mv = 4098, + .mode_list = max1238_mode_list, + .num_modes = ARRAY_SIZE(max1238_mode_list), + .default_mode = s0to11, + .dev_attrs = &max1238_dev_attr_group, + .scan_attrs = &max1238_scan_el_group, + }, { + .name = "max11605", + .num_inputs = 12, + .bits = 8, + .int_vref_mv = 2048, + .mode_list = max1238_mode_list, + .num_modes = ARRAY_SIZE(max1238_mode_list), + .default_mode = s0to11, + .dev_attrs = &max1238_dev_attr_group, + .scan_attrs = &max1238_scan_el_group, + }, { + .name = "max11606", + .num_inputs = 4, + .bits = 10, + .int_vref_mv = 4096, + .mode_list = max11607_mode_list, + .num_modes = ARRAY_SIZE(max11607_mode_list), + .default_mode = s0to3, + .dev_attrs = &max1363_dev_attr_group, + .scan_attrs = &max1363_scan_el_group, + }, { + .name = "max11607", + .num_inputs = 4, + .bits = 10, + .int_vref_mv = 2048, + .mode_list = max11607_mode_list, + .num_modes = ARRAY_SIZE(max11607_mode_list), + .default_mode = s0to3, + .dev_attrs = &max1363_dev_attr_group, + .scan_attrs = &max1363_scan_el_group, + }, { + .name = "max11608", + .num_inputs = 8, + .bits = 10, + .int_vref_mv = 4096, + .mode_list = max11608_mode_list, + .num_modes = ARRAY_SIZE(max11608_mode_list), + .default_mode = s0to7, + .dev_attrs = &max11608_dev_attr_group, + .scan_attrs = &max11608_scan_el_group, + }, { + .name = "max11609", + .num_inputs = 8, + .bits = 10, + .int_vref_mv = 2048, + .mode_list = max11608_mode_list, + .num_modes = ARRAY_SIZE(max11608_mode_list), + .default_mode = s0to7, + .dev_attrs = &max11608_dev_attr_group, + .scan_attrs = &max11608_scan_el_group, + }, { + .name = "max11610", + .num_inputs = 12, + .bits = 10, + .int_vref_mv = 4098, + .mode_list = max1238_mode_list, + .num_modes = ARRAY_SIZE(max1238_mode_list), + .default_mode = s0to11, + .dev_attrs = &max1238_dev_attr_group, + .scan_attrs = &max1238_scan_el_group, + }, { + .name = "max11611", + .num_inputs = 12, + .bits = 10, + .int_vref_mv = 2048, + .mode_list = max1238_mode_list, + .num_modes = ARRAY_SIZE(max1238_mode_list), + .default_mode = s0to11, + .dev_attrs = &max1238_dev_attr_group, + .scan_attrs = &max1238_scan_el_group, + }, { + .name = "max11612", + .num_inputs = 4, + .bits = 12, + .int_vref_mv = 4096, + .mode_list = max11607_mode_list, + .num_modes = ARRAY_SIZE(max11607_mode_list), + .default_mode = s0to3, + .dev_attrs = &max1363_dev_attr_group, + .scan_attrs = &max1363_scan_el_group, + }, { + .name = "max11613", + .num_inputs = 4, + .bits = 12, + .int_vref_mv = 2048, + .mode_list = max11607_mode_list, + .num_modes = ARRAY_SIZE(max11607_mode_list), + .default_mode = s0to3, + .dev_attrs = &max1363_dev_attr_group, + .scan_attrs = &max1363_scan_el_group, + }, { + .name = "max11614", + .num_inputs = 8, + .bits = 12, + .int_vref_mv = 4096, + .mode_list = max11608_mode_list, + .num_modes = ARRAY_SIZE(max11608_mode_list), + .default_mode = s0to7, + .dev_attrs = &max11608_dev_attr_group, + .scan_attrs = &max11608_scan_el_group, + }, { + .name = "max11615", + .num_inputs = 8, + .bits = 12, + .int_vref_mv = 2048, + .mode_list = max11608_mode_list, + .num_modes = ARRAY_SIZE(max11608_mode_list), + .default_mode = s0to7, + .dev_attrs = &max11608_dev_attr_group, + .scan_attrs = &max11608_scan_el_group, + }, { + .name = "max11616", + .num_inputs = 12, + .bits = 12, + .int_vref_mv = 4098, + .mode_list = max1238_mode_list, + .num_modes = ARRAY_SIZE(max1238_mode_list), + .default_mode = s0to11, + .dev_attrs = &max1238_dev_attr_group, + .scan_attrs = &max1238_scan_el_group, + }, { + .name = "max11617", + .num_inputs = 12, + .bits = 12, + .int_vref_mv = 2048, + .mode_list = max1238_mode_list, + .num_modes = ARRAY_SIZE(max1238_mode_list), + .default_mode = s0to11, + .dev_attrs = &max1238_dev_attr_group, + .scan_attrs = &max1238_scan_el_group, + } }; -static int max1363_write_basic_config(struct i2c_client *client, - unsigned char d1, - unsigned char d2) -{ - int ret; - u8 *tx_buf = kmalloc(2 , GFP_KERNEL); - if (!tx_buf) - return -ENOMEM; - tx_buf[0] = d1; - tx_buf[1] = d2; - - ret = i2c_master_send(client, tx_buf, 2); - kfree(tx_buf); - return (ret > 0) ? 0 : ret; -} - -static int max1363_set_scan_mode(struct max1363_state *st) -{ - st->configbyte &= ~(MAX1363_CHANNEL_SEL_MASK - | MAX1363_SCAN_MASK - | MAX1363_SE_DE_MASK); - st->configbyte |= st->current_mode->conf; - - return max1363_write_basic_config(st->client, - st->setupbyte, - st->configbyte); -} - static int max1363_initial_setup(struct max1363_state *st) { st->setupbyte = MAX1363_SETUP_AIN3_IS_AIN3_REF_IS_VDD @@ -318,167 +915,6 @@ static int max1363_initial_setup(struct max1363_state *st) return max1363_set_scan_mode(st); } -static ssize_t max1363_show_av_scan_modes(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - struct iio_dev *dev_info = dev_get_drvdata(dev); - struct max1363_state *st = dev_info->dev_data; - int i, len = 0; - - for (i = 0; i < st->chip_info->num_modes; i++) - len += sprintf(buf + len, "%s ", - max1363_mode_table[st->chip_info - ->mode_list[i]].name); - len += sprintf(buf + len, "\n"); - - return len; -} - - -/* The dev here is the sysfs related one, not the underlying i2c one */ -static ssize_t max1363_scan_direct(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - struct iio_dev *dev_info = dev_get_drvdata(dev); - struct max1363_state *st = dev_info->dev_data; - int len = 0, ret, i; - struct i2c_client *client = st->client; - char *rxbuf; - - if (st->current_mode->numvals == 0) - return 0; - rxbuf = kmalloc(st->current_mode->numvals*2, GFP_KERNEL); - if (rxbuf == NULL) - return -ENOMEM; - - /* Interpretation depends on whether these are signed or not!*/ - /* Assume not for now */ - ret = i2c_master_recv(client, rxbuf, st->current_mode->numvals*2); - - if (ret < 0) - return ret; - for (i = 0; i < st->current_mode->numvals; i++) - len += sprintf(buf+len, "%d ", - ((int)(rxbuf[i*2+0]&0x0F) << 8) - + ((int)(rxbuf[i*2+1]))); - kfree(rxbuf); - len += sprintf(buf + len, "\n"); - - return len; -} - -static ssize_t max1363_scan(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - struct iio_dev *dev_info = dev_get_drvdata(dev); - int ret; - - mutex_lock(&dev_info->mlock); - if (dev_info->currentmode == INDIO_RING_TRIGGERED) - ret = max1363_scan_from_ring(dev, attr, buf); - else - ret = max1363_scan_direct(dev, attr, buf); - mutex_unlock(&dev_info->mlock); - - return ret; -} - -/* Cannot query the device, so use local copy of state */ -static ssize_t max1363_show_scan_mode(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - struct iio_dev *dev_info = dev_get_drvdata(dev); - struct max1363_state *st = dev_info->dev_data; - - return sprintf(buf, "%s\n", st->current_mode->name); -} - -static const struct max1363_mode -*__max1363_find_mode_in_ci(const struct max1363_chip_info *info, - const char *buf) -{ - int i; - for (i = 0; i < info->num_modes; i++) - if (strcmp(max1363_mode_table[info->mode_list[i]].name, buf) - == 0) - return &max1363_mode_table[info->mode_list[i]]; - return NULL; -} - -static ssize_t max1363_store_scan_mode(struct device *dev, - struct device_attribute *attr, - const char *buf, - size_t len) -{ - struct iio_dev *dev_info = dev_get_drvdata(dev); - struct max1363_state *st = dev_info->dev_data; - const struct max1363_mode *new_mode; - int ret; - - mutex_lock(&dev_info->mlock); - new_mode = NULL; - /* Avoid state changes if a ring buffer is enabled */ - if (!iio_ring_enabled(dev_info)) { - new_mode - = __max1363_find_mode_in_ci(st->chip_info, buf); - if (!new_mode) { - ret = -EINVAL; - goto error_ret; - } - st->current_mode = new_mode; - ret = max1363_set_scan_mode(st); - if (ret) - goto error_ret; - } else { - ret = -EBUSY; - goto error_ret; - } - mutex_unlock(&dev_info->mlock); - - return len; - -error_ret: - mutex_unlock(&dev_info->mlock); - - return ret; -} - -IIO_DEV_ATTR_AVAIL_SCAN_MODES(max1363_show_av_scan_modes); -IIO_DEV_ATTR_SCAN_MODE(S_IRUGO | S_IWUSR, - max1363_show_scan_mode, - max1363_store_scan_mode); - -IIO_DEV_ATTR_SCAN(max1363_scan); - -static ssize_t max1363_show_name(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - struct iio_dev *dev_info = dev_get_drvdata(dev); - struct max1363_state *st = dev_info->dev_data; - return sprintf(buf, "%s\n", st->chip_info->name); -} - -IIO_DEVICE_ATTR(name, S_IRUGO, max1363_show_name, NULL, 0); - -/*name export */ - -static struct attribute *max1363_attributes[] = { - &iio_dev_attr_available_scan_modes.dev_attr.attr, - &iio_dev_attr_scan_mode.dev_attr.attr, - &iio_dev_attr_scan.dev_attr.attr, - &iio_dev_attr_name.dev_attr.attr, - NULL, -}; - -static const struct attribute_group max1363_attribute_group = { - .attrs = max1363_attributes, -}; - static int __devinit max1363_probe(struct i2c_client *client, const struct i2c_device_id *id) { @@ -506,6 +942,7 @@ static int __devinit max1363_probe(struct i2c_client *client, ret = -ENODEV; goto error_free_st; } + st->reg = regulator_get(&client->dev, "vcc"); if (!IS_ERR(st->reg)) { ret = regulator_enable(st->reg); @@ -520,20 +957,35 @@ static int __devinit max1363_probe(struct i2c_client *client, goto error_disable_reg; } + st->indio_dev->available_scan_masks + = kzalloc(sizeof(*st->indio_dev->available_scan_masks)* + (st->chip_info->num_modes + 1), GFP_KERNEL); + if (!st->indio_dev->available_scan_masks) { + ret = -ENOMEM; + goto error_free_device; + } + + for (i = 0; i < st->chip_info->num_modes; i++) + st->indio_dev->available_scan_masks[i] = + max1363_mode_table[st->chip_info->mode_list[i]] + .modemask; /* Estabilish that the iio_dev is a child of the i2c device */ st->indio_dev->dev.parent = &client->dev; - st->indio_dev->attrs = &max1363_attribute_group; + st->indio_dev->attrs = st->chip_info->dev_attrs; + + /* Todo: this shouldn't be here. */ + st->indio_dev->scan_el_attrs = st->chip_info->scan_attrs; st->indio_dev->dev_data = (void *)(st); st->indio_dev->driver_module = THIS_MODULE; st->indio_dev->modes = INDIO_DIRECT_MODE; ret = max1363_initial_setup(st); if (ret) - goto error_free_device; + goto error_free_available_scan_masks; ret = max1363_register_ring_funcs_and_init(st->indio_dev); if (ret) - goto error_free_device; + goto error_free_available_scan_masks; ret = iio_device_register(st->indio_dev); if (ret) @@ -545,6 +997,8 @@ static int __devinit max1363_probe(struct i2c_client *client, return 0; error_cleanup_ring: max1363_ring_cleanup(st->indio_dev); +error_free_available_scan_masks: + kfree(st->indio_dev->available_scan_masks); error_free_device: if (!regdone) iio_free_device(st->indio_dev); @@ -557,7 +1011,6 @@ error_put_reg: if (!IS_ERR(st->reg)) regulator_put(st->reg); error_free_st: - i2c_set_clientdata(client, NULL); kfree(st); error_ret: @@ -570,12 +1023,12 @@ static int max1363_remove(struct i2c_client *client) struct iio_dev *indio_dev = st->indio_dev; max1363_uninitialize_ring(indio_dev->ring); max1363_ring_cleanup(indio_dev); + kfree(st->indio_dev->available_scan_masks); iio_device_unregister(indio_dev); if (!IS_ERR(st->reg)) { regulator_disable(st->reg); regulator_put(st->reg); } - i2c_set_clientdata(client, NULL); kfree(st); return 0; @@ -586,6 +1039,10 @@ static const struct i2c_device_id max1363_id[] = { { "max1362", max1362 }, { "max1363", max1363 }, { "max1364", max1364 }, + { "max1036", max1036 }, + { "max1037", max1037 }, + { "max1038", max1038 }, + { "max1039", max1039 }, { "max1136", max1136 }, { "max1137", max1137 }, { "max1138", max1138 }, @@ -594,6 +1051,24 @@ static const struct i2c_device_id max1363_id[] = { { "max1237", max1237 }, { "max1238", max1238 }, { "max1239", max1239 }, + { "max11600", max11600 }, + { "max11601", max11601 }, + { "max11602", max11602 }, + { "max11603", max11603 }, + { "max11604", max11604 }, + { "max11605", max11605 }, + { "max11606", max11606 }, + { "max11607", max11607 }, + { "max11608", max11608 }, + { "max11609", max11609 }, + { "max11610", max11610 }, + { "max11611", max11611 }, + { "max11612", max11612 }, + { "max11613", max11613 }, + { "max11614", max11614 }, + { "max11615", max11615 }, + { "max11616", max11616 }, + { "max11617", max11617 }, {} }; diff --git a/drivers/staging/iio/adc/max1363_ring.c b/drivers/staging/iio/adc/max1363_ring.c index f94fe2d38a97..56688dc9c92f 100644 --- a/drivers/staging/iio/adc/max1363_ring.c +++ b/drivers/staging/iio/adc/max1363_ring.c @@ -17,6 +17,7 @@ #include <linux/sysfs.h> #include <linux/list.h> #include <linux/i2c.h> +#include <linux/bitops.h> #include "../iio.h" #include "../ring_generic.h" @@ -26,32 +27,39 @@ #include "max1363.h" -ssize_t max1363_scan_from_ring(struct device *dev, - struct device_attribute *attr, - char *buf) +/* Todo: test this */ +int max1363_single_channel_from_ring(long mask, struct max1363_state *st) { - struct iio_dev *dev_info = dev_get_drvdata(dev); - struct max1363_state *info = dev_info->dev_data; - int i, ret, len = 0; - char *ring_data; + unsigned long numvals; + int count = 0, ret; + u8 *ring_data; + if (!(st->current_mode->modemask & mask)) { + ret = -EBUSY; + goto error_ret; + } + numvals = hweight_long(st->current_mode->modemask); - ring_data = kmalloc(info->current_mode->numvals*2, GFP_KERNEL); + ring_data = kmalloc(numvals*2, GFP_KERNEL); if (ring_data == NULL) { ret = -ENOMEM; goto error_ret; } - ret = dev_info->ring->access.read_last(dev_info->ring, ring_data); + ret = st->indio_dev->ring->access.read_last(st->indio_dev->ring, + ring_data); if (ret) goto error_free_ring_data; - len += sprintf(buf+len, "ring "); - for (i = 0; i < info->current_mode->numvals; i++) - len += sprintf(buf + len, "%d ", - ((int)(ring_data[i*2 + 0] & 0x0F) << 8) - + ((int)(ring_data[i*2 + 1]))); - len += sprintf(buf + len, "\n"); - kfree(ring_data); - - return len; + /* Need a count of channels prior to this one */ + mask >>= 1; + while (mask) { + if (mask & st->current_mode->modemask) + count++; + mask >>= 1; + } + if (st->chip_info->bits != 8) + ret = ((int)(ring_data[count*2 + 0] & 0x0F) << 8) + + (int)(ring_data[count*2 + 1]); + else + ret = ring_data[count]; error_free_ring_data: kfree(ring_data); @@ -70,9 +78,25 @@ static int max1363_ring_preenable(struct iio_dev *indio_dev) { struct max1363_state *st = indio_dev->dev_data; size_t d_size; + unsigned long numvals; + /* + * Need to figure out the current mode based upon the requested + * scan mask in iio_dev + */ + st->current_mode = max1363_match_mode(st->indio_dev->scan_mask, + st->chip_info); + if (!st->current_mode) + return -EINVAL; + + max1363_set_scan_mode(st); + + numvals = hweight_long(st->current_mode->modemask); if (indio_dev->ring->access.set_bpd) { - d_size = st->current_mode->numvals*2 + sizeof(s64); + if (st->chip_info->bits != 8) + d_size = numvals*2 + sizeof(s64); + else + d_size = numvals + sizeof(s64); if (d_size % 8) d_size += 8 - (d_size % 8); indio_dev->ring->access.set_bpd(indio_dev->ring, d_size); @@ -118,7 +142,7 @@ static int max1363_ring_predisable(struct iio_dev *indio_dev) * then. Some triggers will generate their own time stamp. Currently * there is no way of notifying them when no one cares. **/ -void max1363_poll_func_th(struct iio_dev *indio_dev) +static void max1363_poll_func_th(struct iio_dev *indio_dev) { struct max1363_state *st = indio_dev->dev_data; @@ -145,9 +169,13 @@ static void max1363_poll_bh_to_ring(struct work_struct *work_s) __u8 *rxbuf; int b_sent; size_t d_size; + unsigned long numvals = hweight_long(st->current_mode->modemask); /* Ensure the timestamp is 8 byte aligned */ - d_size = st->current_mode->numvals*2 + sizeof(s64); + if (st->chip_info->bits != 8) + d_size = numvals*2 + sizeof(s64); + else + d_size = numvals + sizeof(s64); if (d_size % sizeof(s64)) d_size += sizeof(s64) - (d_size % sizeof(s64)); @@ -159,16 +187,16 @@ static void max1363_poll_bh_to_ring(struct work_struct *work_s) * might as well have this test in here in the meantime as it does * no harm. */ - if (st->current_mode->numvals == 0) + if (numvals == 0) return; rxbuf = kmalloc(d_size, GFP_KERNEL); if (rxbuf == NULL) return; - - b_sent = i2c_master_recv(st->client, - rxbuf, - st->current_mode->numvals*2); + if (st->chip_info->bits != 8) + b_sent = i2c_master_recv(st->client, rxbuf, numvals*2); + else + b_sent = i2c_master_recv(st->client, rxbuf, numvals); if (b_sent < 0) goto done; @@ -238,5 +266,5 @@ void max1363_uninitialize_ring(struct iio_ring_buffer *ring) int max1363_initialize_ring(struct iio_ring_buffer *ring) { - return iio_ring_buffer_register(ring); + return iio_ring_buffer_register(ring, 0); }; diff --git a/drivers/staging/iio/chrdev.h b/drivers/staging/iio/chrdev.h index f42bafb3a894..3f96f8696a41 100644 --- a/drivers/staging/iio/chrdev.h +++ b/drivers/staging/iio/chrdev.h @@ -94,7 +94,7 @@ struct iio_event_interface { struct iio_chrdev_minor_attr attr; struct module *owner; void *private; - char _name[20]; + char _name[35]; char _attrname[20]; }; 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 @@ +# +# IIO Digital Gyroscope Sensor drivers configuration +# +comment "Digital gyroscope sensors" + +config ADIS16260 + tristate "Analog Devices ADIS16260/5 Digital Gyroscope Sensor SPI driver" + depends on SPI + select IIO_TRIGGER if IIO_RING_BUFFER + select IIO_SW_RING if IIO_RING_BUFFER + help + Say yes here to build support for Analog Devices adis16260/5 + 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 @@ + +# Makefile for digital gyroscope sensor drivers +# + +adis16260-y := adis16260_core.o +adis16260-$(CONFIG_IIO_RING_BUFFER) += adis16260_ring.o adis16260_trigger.o +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 @@ +#ifndef SPI_ADIS16260_H_ +#define SPI_ADIS16260_H_ + +#define ADIS16260_STARTUP_DELAY 220 /* ms */ + +#define ADIS16260_READ_REG(a) a +#define ADIS16260_WRITE_REG(a) ((a) | 0x80) + +#define ADIS16260_FLASH_CNT 0x00 /* Flash memory write count */ +#define ADIS16260_SUPPLY_OUT 0x02 /* Power supply measurement */ +#define ADIS16260_GYRO_OUT 0x04 /* X-axis gyroscope output */ +#define ADIS16260_AUX_ADC 0x0A /* analog input channel measurement */ +#define ADIS16260_TEMP_OUT 0x0C /* internal temperature measurement */ +#define ADIS16260_ANGL_OUT 0x0E /* angle displacement */ +#define ADIS16260_GYRO_OFF 0x14 /* Calibration, offset/bias adjustment */ +#define ADIS16260_GYRO_SCALE 0x16 /* Calibration, scale adjustment */ +#define ADIS16260_ALM_MAG1 0x20 /* Alarm 1 magnitude/polarity setting */ +#define ADIS16260_ALM_MAG2 0x22 /* Alarm 2 magnitude/polarity setting */ +#define ADIS16260_ALM_SMPL1 0x24 /* Alarm 1 dynamic rate of change setting */ +#define ADIS16260_ALM_SMPL2 0x26 /* Alarm 2 dynamic rate of change setting */ +#define ADIS16260_ALM_CTRL 0x28 /* Alarm control */ +#define ADIS16260_AUX_DAC 0x30 /* Auxiliary DAC data */ +#define ADIS16260_GPIO_CTRL 0x32 /* Control, digital I/O line */ +#define ADIS16260_MSC_CTRL 0x34 /* Control, data ready, self-test settings */ +#define ADIS16260_SMPL_PRD 0x36 /* Control, internal sample rate */ +#define ADIS16260_SENS_AVG 0x38 /* Control, dynamic range, filtering */ +#define ADIS16260_SLP_CNT 0x3A /* Control, sleep mode initiation */ +#define ADIS16260_DIAG_STAT 0x3C /* Diagnostic, error flags */ +#define ADIS16260_GLOB_CMD 0x3E /* Control, global commands */ +#define ADIS16260_LOT_ID1 0x52 /* Lot Identification Code 1 */ +#define ADIS16260_LOT_ID2 0x54 /* Lot Identification Code 2 */ +#define ADIS16260_PROD_ID 0x56 /* Product identifier; + * convert to decimal = 16,265/16,260 */ +#define ADIS16260_SERIAL_NUM 0x58 /* Serial number */ + +#define ADIS16260_OUTPUTS 5 + +#define ADIS16260_ERROR_ACTIVE (1<<14) +#define ADIS16260_NEW_DATA (1<<15) + +/* MSC_CTRL */ +#define ADIS16260_MSC_CTRL_MEM_TEST (1<<11) +/* Internal self-test enable */ +#define ADIS16260_MSC_CTRL_INT_SELF_TEST (1<<10) +#define ADIS16260_MSC_CTRL_NEG_SELF_TEST (1<<9) +#define ADIS16260_MSC_CTRL_POS_SELF_TEST (1<<8) +#define ADIS16260_MSC_CTRL_DATA_RDY_EN (1<<2) +#define ADIS16260_MSC_CTRL_DATA_RDY_POL_HIGH (1<<1) +#define ADIS16260_MSC_CTRL_DATA_RDY_DIO2 (1<<0) + +/* SMPL_PRD */ +/* Time base (tB): 0 = 1.953 ms, 1 = 60.54 ms */ +#define ADIS16260_SMPL_PRD_TIME_BASE (1<<7) +#define ADIS16260_SMPL_PRD_DIV_MASK 0x7F + +/* SLP_CNT */ +#define ADIS16260_SLP_CNT_POWER_OFF 0x80 + +/* DIAG_STAT */ +#define ADIS16260_DIAG_STAT_ALARM2 (1<<9) +#define ADIS16260_DIAG_STAT_ALARM1 (1<<8) +#define ADIS16260_DIAG_STAT_FLASH_CHK (1<<6) +#define ADIS16260_DIAG_STAT_SELF_TEST (1<<5) +#define ADIS16260_DIAG_STAT_OVERFLOW (1<<4) +#define ADIS16260_DIAG_STAT_SPI_FAIL (1<<3) +#define ADIS16260_DIAG_STAT_FLASH_UPT (1<<2) +#define ADIS16260_DIAG_STAT_POWER_HIGH (1<<1) +#define ADIS16260_DIAG_STAT_POWER_LOW (1<<0) + +/* GLOB_CMD */ +#define ADIS16260_GLOB_CMD_SW_RESET (1<<7) +#define ADIS16260_GLOB_CMD_FLASH_UPD (1<<3) +#define ADIS16260_GLOB_CMD_DAC_LATCH (1<<2) +#define ADIS16260_GLOB_CMD_FAC_CALIB (1<<1) +#define ADIS16260_GLOB_CMD_AUTO_NULL (1<<0) + +#define ADIS16260_MAX_TX 24 +#define ADIS16260_MAX_RX 24 + +#define ADIS16260_SPI_SLOW (u32)(300 * 1000) +#define ADIS16260_SPI_BURST (u32)(1000 * 1000) +#define ADIS16260_SPI_FAST (u32)(2000 * 1000) + +/** + * struct adis16260_state - device instance specific data + * @us: actual spi_device + * @work_trigger_to_ring: bh for triggered event handling + * @work_cont_thresh: CLEAN + * @inter: used to check if new interrupt has been triggered + * @last_timestamp: passing timestamp from th to bh of interrupt handler + * @indio_dev: industrial I/O device structure + * @trig: data ready trigger registered with iio + * @tx: transmit buffer + * @rx: recieve buffer + * @buf_lock: mutex to protect tx and rx + **/ +struct adis16260_state { + struct spi_device *us; + struct work_struct work_trigger_to_ring; + struct iio_work_cont work_cont_thresh; + s64 last_timestamp; + struct iio_dev *indio_dev; + struct iio_trigger *trig; + u8 *tx; + u8 *rx; + struct mutex buf_lock; +}; + +int adis16260_set_irq(struct device *dev, bool enable); + +#ifdef CONFIG_IIO_RING_BUFFER +/* At the moment triggers are only used for ring buffer + * filling. This may change! + */ + +enum adis16260_scan { + ADIS16260_SCAN_SUPPLY, + ADIS16260_SCAN_GYRO, + ADIS16260_SCAN_AUX_ADC, + ADIS16260_SCAN_TEMP, + ADIS16260_SCAN_ANGL, +}; + +void adis16260_remove_trigger(struct iio_dev *indio_dev); +int adis16260_probe_trigger(struct iio_dev *indio_dev); + +ssize_t adis16260_read_data_from_ring(struct device *dev, + struct device_attribute *attr, + char *buf); + + +int adis16260_configure_ring(struct iio_dev *indio_dev); +void adis16260_unconfigure_ring(struct iio_dev *indio_dev); + +int adis16260_initialize_ring(struct iio_ring_buffer *ring); +void adis16260_uninitialize_ring(struct iio_ring_buffer *ring); +#else /* CONFIG_IIO_RING_BUFFER */ + +static inline void adis16260_remove_trigger(struct iio_dev *indio_dev) +{ +} + +static inline int adis16260_probe_trigger(struct iio_dev *indio_dev) +{ + return 0; +} + +static inline ssize_t +adis16260_read_data_from_ring(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return 0; +} + +static int adis16260_configure_ring(struct iio_dev *indio_dev) +{ + return 0; +} + +static inline void adis16260_unconfigure_ring(struct iio_dev *indio_dev) +{ +} + +static inline int adis16260_initialize_ring(struct iio_ring_buffer *ring) +{ + return 0; +} + +static inline void adis16260_uninitialize_ring(struct iio_ring_buffer *ring) +{ +} + +#endif /* CONFIG_IIO_RING_BUFFER */ +#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 @@ +/* + * ADIS16260 Programmable Digital Gyroscope Sensor Driver + * + * Copyright 2010 Analog Devices Inc. + * + * Licensed under the GPL-2 or later. + */ + +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/gpio.h> +#include <linux/delay.h> +#include <linux/mutex.h> +#include <linux/device.h> +#include <linux/kernel.h> +#include <linux/spi/spi.h> + +#include <linux/sysfs.h> +#include <linux/list.h> + +#include "../iio.h" +#include "../sysfs.h" +#include "../adc/adc.h" +#include "gyro.h" + +#include "adis16260.h" + +#define DRIVER_NAME "adis16260" + +static int adis16260_check_status(struct device *dev); + +/** + * adis16260_spi_write_reg_8() - write single byte to a register + * @dev: device associated with child of actual device (iio_dev or iio_trig) + * @reg_address: the address of the register to be written + * @val: the value to write + **/ +static int adis16260_spi_write_reg_8(struct device *dev, + u8 reg_address, + u8 val) +{ + int ret; + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct adis16260_state *st = iio_dev_get_devdata(indio_dev); + + mutex_lock(&st->buf_lock); + st->tx[0] = ADIS16260_WRITE_REG(reg_address); + st->tx[1] = val; + + ret = spi_write(st->us, st->tx, 2); + mutex_unlock(&st->buf_lock); + + return ret; +} + +/** + * adis16260_spi_write_reg_16() - write 2 bytes to a pair of registers + * @dev: device associated with child of actual device (iio_dev or iio_trig) + * @reg_address: the address of the lower of the two registers. Second register + * is assumed to have address one greater. + * @val: value to be written + **/ +static int adis16260_spi_write_reg_16(struct device *dev, + u8 lower_reg_address, + u16 value) +{ + int ret; + struct spi_message msg; + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct adis16260_state *st = iio_dev_get_devdata(indio_dev); + struct spi_transfer xfers[] = { + { + .tx_buf = st->tx, + .bits_per_word = 8, + .len = 2, + .cs_change = 1, + .delay_usecs = 20, + }, { + .tx_buf = st->tx + 2, + .bits_per_word = 8, + .len = 2, + .cs_change = 1, + .delay_usecs = 20, + }, + }; + + mutex_lock(&st->buf_lock); + st->tx[0] = ADIS16260_WRITE_REG(lower_reg_address); + st->tx[1] = value & 0xFF; + st->tx[2] = ADIS16260_WRITE_REG(lower_reg_address + 1); + st->tx[3] = (value >> 8) & 0xFF; + + spi_message_init(&msg); + spi_message_add_tail(&xfers[0], &msg); + spi_message_add_tail(&xfers[1], &msg); + ret = spi_sync(st->us, &msg); + mutex_unlock(&st->buf_lock); + + return ret; +} + +/** + * adis16260_spi_read_reg_16() - read 2 bytes from a 16-bit register + * @dev: device associated with child of actual device (iio_dev or iio_trig) + * @reg_address: the address of the lower of the two registers. Second register + * is assumed to have address one greater. + * @val: somewhere to pass back the value read + **/ +static int adis16260_spi_read_reg_16(struct device *dev, + u8 lower_reg_address, + u16 *val) +{ + struct spi_message msg; + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct adis16260_state *st = iio_dev_get_devdata(indio_dev); + int ret; + struct spi_transfer xfers[] = { + { + .tx_buf = st->tx, + .bits_per_word = 8, + .len = 2, + .cs_change = 1, + .delay_usecs = 30, + }, { + .rx_buf = st->rx, + .bits_per_word = 8, + .len = 2, + .cs_change = 1, + .delay_usecs = 30, + }, + }; + + mutex_lock(&st->buf_lock); + st->tx[0] = ADIS16260_READ_REG(lower_reg_address); + st->tx[1] = 0; + st->tx[2] = 0; + st->tx[3] = 0; + + spi_message_init(&msg); + spi_message_add_tail(&xfers[0], &msg); + spi_message_add_tail(&xfers[1], &msg); + ret = spi_sync(st->us, &msg); + if (ret) { + dev_err(&st->us->dev, + "problem when reading 16 bit register 0x%02X", + lower_reg_address); + goto error_ret; + } + *val = (st->rx[0] << 8) | st->rx[1]; + +error_ret: + mutex_unlock(&st->buf_lock); + return ret; +} + +static ssize_t adis16260_spi_read_signed(struct device *dev, + struct device_attribute *attr, + char *buf, + unsigned bits) +{ + int ret; + s16 val = 0; + unsigned shift = 16 - bits; + struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); + + ret = adis16260_spi_read_reg_16(dev, this_attr->address, (u16 *)&val); + if (ret) + return ret; + + if (val & ADIS16260_ERROR_ACTIVE) + adis16260_check_status(dev); + val = ((s16)(val << shift) >> shift); + return sprintf(buf, "%d\n", val); +} + +static ssize_t adis16260_read_12bit_unsigned(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int ret; + u16 val = 0; + struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); + + ret = adis16260_spi_read_reg_16(dev, this_attr->address, &val); + if (ret) + return ret; + + if (val & ADIS16260_ERROR_ACTIVE) + adis16260_check_status(dev); + + return sprintf(buf, "%u\n", val & 0x0FFF); +} + +static ssize_t adis16260_read_12bit_signed(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + ssize_t ret; + + /* Take the iio_dev status lock */ + mutex_lock(&indio_dev->mlock); + ret = adis16260_spi_read_signed(dev, attr, buf, 12); + mutex_unlock(&indio_dev->mlock); + + return ret; +} + +static ssize_t adis16260_read_14bit_signed(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + ssize_t ret; + + /* Take the iio_dev status lock */ + mutex_lock(&indio_dev->mlock); + ret = adis16260_spi_read_signed(dev, attr, buf, 14); + mutex_unlock(&indio_dev->mlock); + + return ret; +} + +static ssize_t adis16260_write_16bit(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t len) +{ + struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); + int ret; + long val; + + ret = strict_strtol(buf, 10, &val); + if (ret) + goto error_ret; + ret = adis16260_spi_write_reg_16(dev, this_attr->address, val); + +error_ret: + return ret ? ret : len; +} + +static ssize_t adis16260_read_frequency(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int ret, len = 0; + u16 t; + int sps; + ret = adis16260_spi_read_reg_16(dev, + ADIS16260_SMPL_PRD, + &t); + if (ret) + return ret; + sps = (t & ADIS16260_SMPL_PRD_TIME_BASE) ? 66 : 2048; + sps /= (t & ADIS16260_SMPL_PRD_DIV_MASK) + 1; + len = sprintf(buf, "%d SPS\n", sps); + return len; +} + +static ssize_t adis16260_write_frequency(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t len) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct adis16260_state *st = iio_dev_get_devdata(indio_dev); + long val; + int ret; + u8 t; + + ret = strict_strtol(buf, 10, &val); + if (ret) + return ret; + + mutex_lock(&indio_dev->mlock); + + t = (2048 / val); + if (t > 0) + t--; + t &= ADIS16260_SMPL_PRD_DIV_MASK; + if ((t & ADIS16260_SMPL_PRD_DIV_MASK) >= 0x0A) + st->us->max_speed_hz = ADIS16260_SPI_SLOW; + else + st->us->max_speed_hz = ADIS16260_SPI_FAST; + + ret = adis16260_spi_write_reg_8(dev, + ADIS16260_SMPL_PRD, + t); + + mutex_unlock(&indio_dev->mlock); + + return ret ? ret : len; +} + +static int adis16260_reset(struct device *dev) +{ + int ret; + ret = adis16260_spi_write_reg_8(dev, + ADIS16260_GLOB_CMD, + ADIS16260_GLOB_CMD_SW_RESET); + if (ret) + dev_err(dev, "problem resetting device"); + + return ret; +} + +static ssize_t adis16260_write_reset(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + if (len < 1) + return -EINVAL; + switch (buf[0]) { + case '1': + case 'y': + case 'Y': + return adis16260_reset(dev); + } + return -EINVAL; +} + +int adis16260_set_irq(struct device *dev, bool enable) +{ + int ret; + u16 msc; + ret = adis16260_spi_read_reg_16(dev, ADIS16260_MSC_CTRL, &msc); + if (ret) + goto error_ret; + + msc |= ADIS16260_MSC_CTRL_DATA_RDY_POL_HIGH; + if (enable) + msc |= ADIS16260_MSC_CTRL_DATA_RDY_EN; + else + msc &= ~ADIS16260_MSC_CTRL_DATA_RDY_EN; + + ret = adis16260_spi_write_reg_16(dev, ADIS16260_MSC_CTRL, msc); + if (ret) + goto error_ret; + +error_ret: + return ret; +} + +/* Power down the device */ +static int adis16260_stop_device(struct device *dev) +{ + int ret; + u16 val = ADIS16260_SLP_CNT_POWER_OFF; + + ret = adis16260_spi_write_reg_16(dev, ADIS16260_SLP_CNT, val); + if (ret) + dev_err(dev, "problem with turning device off: SLP_CNT"); + + return ret; +} + +static int adis16260_self_test(struct device *dev) +{ + int ret; + ret = adis16260_spi_write_reg_16(dev, + ADIS16260_MSC_CTRL, + ADIS16260_MSC_CTRL_MEM_TEST); + if (ret) { + dev_err(dev, "problem starting self test"); + goto err_ret; + } + + adis16260_check_status(dev); + +err_ret: + return ret; +} + +static int adis16260_check_status(struct device *dev) +{ + u16 status; + int ret; + + ret = adis16260_spi_read_reg_16(dev, ADIS16260_DIAG_STAT, &status); + + if (ret < 0) { + dev_err(dev, "Reading status failed\n"); + goto error_ret; + } + ret = status & 0x7F; + if (status & ADIS16260_DIAG_STAT_FLASH_CHK) + dev_err(dev, "Flash checksum error\n"); + if (status & ADIS16260_DIAG_STAT_SELF_TEST) + dev_err(dev, "Self test error\n"); + if (status & ADIS16260_DIAG_STAT_OVERFLOW) + dev_err(dev, "Sensor overrange\n"); + if (status & ADIS16260_DIAG_STAT_SPI_FAIL) + dev_err(dev, "SPI failure\n"); + if (status & ADIS16260_DIAG_STAT_FLASH_UPT) + dev_err(dev, "Flash update failed\n"); + if (status & ADIS16260_DIAG_STAT_POWER_HIGH) + dev_err(dev, "Power supply above 5.25V\n"); + if (status & ADIS16260_DIAG_STAT_POWER_LOW) + dev_err(dev, "Power supply below 4.75V\n"); + +error_ret: + return ret; +} + +static int adis16260_initial_setup(struct adis16260_state *st) +{ + int ret; + struct device *dev = &st->indio_dev->dev; + + /* Disable IRQ */ + ret = adis16260_set_irq(dev, false); + if (ret) { + dev_err(dev, "disable irq failed"); + goto err_ret; + } + + /* Do self test */ + ret = adis16260_self_test(dev); + if (ret) { + dev_err(dev, "self test failure"); + goto err_ret; + } + + /* Read status register to check the result */ + ret = adis16260_check_status(dev); + if (ret) { + adis16260_reset(dev); + dev_err(dev, "device not playing ball -> reset"); + msleep(ADIS16260_STARTUP_DELAY); + ret = adis16260_check_status(dev); + if (ret) { + dev_err(dev, "giving up"); + goto err_ret; + } + } + + printk(KERN_INFO DRIVER_NAME ": at CS%d (irq %d)\n", + st->us->chip_select, st->us->irq); + +err_ret: + return ret; +} + +static IIO_DEV_ATTR_IN_NAMED_RAW(supply, + adis16260_read_12bit_unsigned, + ADIS16260_SUPPLY_OUT); +static IIO_CONST_ATTR(in_supply_scale, "0.0018315"); + +static IIO_DEV_ATTR_GYRO(adis16260_read_14bit_signed, + ADIS16260_GYRO_OUT); +static IIO_DEV_ATTR_GYRO_SCALE(S_IWUSR | S_IRUGO, + adis16260_read_14bit_signed, + adis16260_write_16bit, + ADIS16260_GYRO_SCALE); +static IIO_DEV_ATTR_GYRO_OFFSET(S_IWUSR | S_IRUGO, + adis16260_read_12bit_signed, + adis16260_write_16bit, + ADIS16260_GYRO_OFF); + +static IIO_DEV_ATTR_TEMP_RAW(adis16260_read_12bit_unsigned); +static IIO_CONST_ATTR(temp_offset, "25"); +static IIO_CONST_ATTR(temp_scale, "0.1453"); + +static IIO_DEV_ATTR_IN_RAW(0, adis16260_read_12bit_unsigned, + ADIS16260_AUX_ADC); +static IIO_CONST_ATTR(in0_scale, "0.0006105"); + +static IIO_DEV_ATTR_SAMP_FREQ(S_IWUSR | S_IRUGO, + adis16260_read_frequency, + adis16260_write_frequency); +static IIO_DEV_ATTR_ANGL(adis16260_read_14bit_signed, + ADIS16260_ANGL_OUT); + +static IIO_DEVICE_ATTR(reset, S_IWUSR, NULL, adis16260_write_reset, 0); + +static IIO_CONST_ATTR_AVAIL_SAMP_FREQ("256 2048"); + +static IIO_CONST_ATTR(name, "adis16260"); + +static struct attribute *adis16260_event_attributes[] = { + NULL +}; + +static struct attribute_group adis16260_event_attribute_group = { + .attrs = adis16260_event_attributes, +}; + +static struct attribute *adis16260_attributes[] = { + &iio_dev_attr_in_supply_raw.dev_attr.attr, + &iio_const_attr_in_supply_scale.dev_attr.attr, + &iio_dev_attr_gyro_raw.dev_attr.attr, + &iio_dev_attr_gyro_scale.dev_attr.attr, + &iio_dev_attr_gyro_offset.dev_attr.attr, + &iio_dev_attr_angl_raw.dev_attr.attr, + &iio_dev_attr_temp_raw.dev_attr.attr, + &iio_const_attr_temp_offset.dev_attr.attr, + &iio_const_attr_temp_scale.dev_attr.attr, + &iio_dev_attr_in0_raw.dev_attr.attr, + &iio_const_attr_in0_scale.dev_attr.attr, + &iio_dev_attr_sampling_frequency.dev_attr.attr, + &iio_const_attr_available_sampling_frequency.dev_attr.attr, + &iio_dev_attr_reset.dev_attr.attr, + &iio_const_attr_name.dev_attr.attr, + NULL +}; + +static const struct attribute_group adis16260_attribute_group = { + .attrs = adis16260_attributes, +}; + +static int __devinit adis16260_probe(struct spi_device *spi) +{ + int ret, regdone = 0; + struct adis16260_state *st = kzalloc(sizeof *st, GFP_KERNEL); + if (!st) { + ret = -ENOMEM; + goto error_ret; + } + /* this is only used for removal purposes */ + spi_set_drvdata(spi, st); + + /* Allocate the comms buffers */ + st->rx = kzalloc(sizeof(*st->rx)*ADIS16260_MAX_RX, GFP_KERNEL); + if (st->rx == NULL) { + ret = -ENOMEM; + goto error_free_st; + } + st->tx = kzalloc(sizeof(*st->tx)*ADIS16260_MAX_TX, GFP_KERNEL); + if (st->tx == NULL) { + ret = -ENOMEM; + goto error_free_rx; + } + st->us = spi; + mutex_init(&st->buf_lock); + /* setup the industrialio driver allocated elements */ + st->indio_dev = iio_allocate_device(); + if (st->indio_dev == NULL) { + ret = -ENOMEM; + goto error_free_tx; + } + + st->indio_dev->dev.parent = &spi->dev; + st->indio_dev->num_interrupt_lines = 1; + st->indio_dev->event_attrs = &adis16260_event_attribute_group; + st->indio_dev->attrs = &adis16260_attribute_group; + st->indio_dev->dev_data = (void *)(st); + st->indio_dev->driver_module = THIS_MODULE; + st->indio_dev->modes = INDIO_DIRECT_MODE; + + ret = adis16260_configure_ring(st->indio_dev); + if (ret) + goto error_free_dev; + + ret = iio_device_register(st->indio_dev); + if (ret) + goto error_unreg_ring_funcs; + regdone = 1; + + ret = adis16260_initialize_ring(st->indio_dev->ring); + if (ret) { + printk(KERN_ERR "failed to initialize the ring\n"); + goto error_unreg_ring_funcs; + } + + if (spi->irq) { + ret = iio_register_interrupt_line(spi->irq, + st->indio_dev, + 0, + IRQF_TRIGGER_RISING, + "adis16260"); + if (ret) + goto error_uninitialize_ring; + + ret = adis16260_probe_trigger(st->indio_dev); + if (ret) + goto error_unregister_line; + } + + /* Get the device into a sane initial state */ + ret = adis16260_initial_setup(st); + if (ret) + goto error_remove_trigger; + return 0; + +error_remove_trigger: + adis16260_remove_trigger(st->indio_dev); +error_unregister_line: + if (spi->irq) + iio_unregister_interrupt_line(st->indio_dev, 0); +error_uninitialize_ring: + adis16260_uninitialize_ring(st->indio_dev->ring); +error_unreg_ring_funcs: + adis16260_unconfigure_ring(st->indio_dev); +error_free_dev: + if (regdone) + iio_device_unregister(st->indio_dev); + else + iio_free_device(st->indio_dev); +error_free_tx: + kfree(st->tx); +error_free_rx: + kfree(st->rx); +error_free_st: + kfree(st); +error_ret: + return ret; +} + +static int adis16260_remove(struct spi_device *spi) +{ + int ret; + struct adis16260_state *st = spi_get_drvdata(spi); + struct iio_dev *indio_dev = st->indio_dev; + + ret = adis16260_stop_device(&(indio_dev->dev)); + if (ret) + goto err_ret; + + flush_scheduled_work(); + + adis16260_remove_trigger(indio_dev); + if (spi->irq) + iio_unregister_interrupt_line(indio_dev, 0); + + adis16260_uninitialize_ring(indio_dev->ring); + iio_device_unregister(indio_dev); + adis16260_unconfigure_ring(indio_dev); + kfree(st->tx); + kfree(st->rx); + kfree(st); + + return 0; + +err_ret: + return ret; +} + +static struct spi_driver adis16260_driver = { + .driver = { + .name = "adis16260", + .owner = THIS_MODULE, + }, + .probe = adis16260_probe, + .remove = __devexit_p(adis16260_remove), +}; + +static __init int adis16260_init(void) +{ + return spi_register_driver(&adis16260_driver); +} +module_init(adis16260_init); + +static __exit void adis16260_exit(void) +{ + spi_unregister_driver(&adis16260_driver); +} +module_exit(adis16260_exit); + +MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>"); +MODULE_DESCRIPTION("Analog Devices ADIS16260/5 Digital Gyroscope Sensor"); +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 @@ +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/gpio.h> +#include <linux/workqueue.h> +#include <linux/mutex.h> +#include <linux/device.h> +#include <linux/kernel.h> +#include <linux/spi/spi.h> +#include <linux/sysfs.h> +#include <linux/list.h> + +#include "../iio.h" +#include "../sysfs.h" +#include "../ring_sw.h" +#include "../accel/accel.h" +#include "../trigger.h" +#include "adis16260.h" + +/** + * combine_8_to_16() utility function to munge to u8s into u16 + **/ +static inline u16 combine_8_to_16(u8 lower, u8 upper) +{ + u16 _lower = lower; + u16 _upper = upper; + return _lower | (_upper << 8); +} + +static IIO_SCAN_EL_C(supply, ADIS16260_SCAN_SUPPLY, IIO_UNSIGNED(12), + ADIS16260_SUPPLY_OUT, NULL); +static IIO_SCAN_EL_C(gyro, ADIS16260_SCAN_GYRO, IIO_SIGNED(14), + ADIS16260_GYRO_OUT, NULL); +static IIO_SCAN_EL_C(aux_adc, ADIS16260_SCAN_AUX_ADC, IIO_SIGNED(14), + ADIS16260_AUX_ADC, NULL); +static IIO_SCAN_EL_C(temp, ADIS16260_SCAN_TEMP, IIO_UNSIGNED(12), + ADIS16260_TEMP_OUT, NULL); +static IIO_SCAN_EL_C(angl, ADIS16260_SCAN_ANGL, IIO_UNSIGNED(12), + ADIS16260_ANGL_OUT, NULL); + +static IIO_SCAN_EL_TIMESTAMP(5); + +static struct attribute *adis16260_scan_el_attrs[] = { + &iio_scan_el_supply.dev_attr.attr, + &iio_scan_el_gyro.dev_attr.attr, + &iio_scan_el_aux_adc.dev_attr.attr, + &iio_scan_el_temp.dev_attr.attr, + &iio_scan_el_angl.dev_attr.attr, + &iio_scan_el_timestamp.dev_attr.attr, + NULL, +}; + +static struct attribute_group adis16260_scan_el_group = { + .attrs = adis16260_scan_el_attrs, + .name = "scan_elements", +}; + +/** + * adis16260_poll_func_th() top half interrupt handler called by trigger + * @private_data: iio_dev + **/ +static void adis16260_poll_func_th(struct iio_dev *indio_dev) +{ + struct adis16260_state *st = iio_dev_get_devdata(indio_dev); + st->last_timestamp = indio_dev->trig->timestamp; + schedule_work(&st->work_trigger_to_ring); +} + +/** + * adis16260_read_ring_data() read data registers which will be placed into ring + * @dev: device associated with child of actual device (iio_dev or iio_trig) + * @rx: somewhere to pass back the value read + **/ +static int adis16260_read_ring_data(struct device *dev, u8 *rx) +{ + struct spi_message msg; + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct adis16260_state *st = iio_dev_get_devdata(indio_dev); + struct spi_transfer xfers[ADIS16260_OUTPUTS + 1]; + int ret; + int i; + + mutex_lock(&st->buf_lock); + + spi_message_init(&msg); + + memset(xfers, 0, sizeof(xfers)); + for (i = 0; i <= ADIS16260_OUTPUTS; i++) { + xfers[i].bits_per_word = 8; + xfers[i].cs_change = 1; + xfers[i].len = 2; + xfers[i].delay_usecs = 30; + xfers[i].tx_buf = st->tx + 2 * i; + if (i < 2) /* SUPPLY_OUT:0x02 GYRO_OUT:0x04 */ + st->tx[2 * i] + = ADIS16260_READ_REG(ADIS16260_SUPPLY_OUT + + 2 * i); + else /* 0x06 to 0x09 is reserved */ + st->tx[2 * i] + = ADIS16260_READ_REG(ADIS16260_SUPPLY_OUT + + 2 * i + 4); + st->tx[2 * i + 1] = 0; + if (i >= 1) + xfers[i].rx_buf = rx + 2 * (i - 1); + spi_message_add_tail(&xfers[i], &msg); + } + + ret = spi_sync(st->us, &msg); + if (ret) + dev_err(&st->us->dev, "problem when burst reading"); + + mutex_unlock(&st->buf_lock); + + return ret; +} + + +static void adis16260_trigger_bh_to_ring(struct work_struct *work_s) +{ + struct adis16260_state *st + = container_of(work_s, struct adis16260_state, + work_trigger_to_ring); + + int i = 0; + s16 *data; + size_t datasize = st->indio_dev + ->ring->access.get_bpd(st->indio_dev->ring); + + data = kmalloc(datasize , GFP_KERNEL); + if (data == NULL) { + dev_err(&st->us->dev, "memory alloc failed in ring bh"); + return; + } + + if (st->indio_dev->scan_count) + if (adis16260_read_ring_data(&st->indio_dev->dev, st->rx) >= 0) + for (; i < st->indio_dev->scan_count; i++) { + data[i] = combine_8_to_16(st->rx[i*2+1], + st->rx[i*2]); + } + + /* Guaranteed to be aligned with 8 byte boundary */ + if (st->indio_dev->scan_timestamp) + *((s64 *)(data + ((i + 3)/4)*4)) = st->last_timestamp; + + st->indio_dev->ring->access.store_to(st->indio_dev->ring, + (u8 *)data, + st->last_timestamp); + + iio_trigger_notify_done(st->indio_dev->trig); + kfree(data); + + return; +} + +static int adis16260_data_rdy_ring_preenable(struct iio_dev *indio_dev) +{ + size_t size; + dev_dbg(&indio_dev->dev, "%s\n", __func__); + /* Check if there are any scan elements enabled, if not fail*/ + if (!(indio_dev->scan_count || indio_dev->scan_timestamp)) + return -EINVAL; + + if (indio_dev->ring->access.set_bpd) { + if (indio_dev->scan_timestamp) + if (indio_dev->scan_count) + /* Timestamp (aligned s64) and data */ + size = (((indio_dev->scan_count * sizeof(s16)) + + sizeof(s64) - 1) + & ~(sizeof(s64) - 1)) + + sizeof(s64); + else /* Timestamp only */ + size = sizeof(s64); + else /* Data only */ + size = indio_dev->scan_count*sizeof(s16); + indio_dev->ring->access.set_bpd(indio_dev->ring, size); + } + + return 0; +} + +static int adis16260_data_rdy_ring_postenable(struct iio_dev *indio_dev) +{ + return indio_dev->trig + ? iio_trigger_attach_poll_func(indio_dev->trig, + indio_dev->pollfunc) + : 0; +} + +static int adis16260_data_rdy_ring_predisable(struct iio_dev *indio_dev) +{ + return indio_dev->trig + ? iio_trigger_dettach_poll_func(indio_dev->trig, + indio_dev->pollfunc) + : 0; +} + +void adis16260_unconfigure_ring(struct iio_dev *indio_dev) +{ + kfree(indio_dev->pollfunc); + iio_sw_rb_free(indio_dev->ring); +} + +int adis16260_configure_ring(struct iio_dev *indio_dev) +{ + int ret = 0; + struct adis16260_state *st = indio_dev->dev_data; + struct iio_ring_buffer *ring; + INIT_WORK(&st->work_trigger_to_ring, adis16260_trigger_bh_to_ring); + /* Set default scan mode */ + + iio_scan_mask_set(indio_dev, iio_scan_el_supply.number); + iio_scan_mask_set(indio_dev, iio_scan_el_gyro.number); + iio_scan_mask_set(indio_dev, iio_scan_el_aux_adc.number); + iio_scan_mask_set(indio_dev, iio_scan_el_temp.number); + iio_scan_mask_set(indio_dev, iio_scan_el_angl.number); + indio_dev->scan_timestamp = true; + + indio_dev->scan_el_attrs = &adis16260_scan_el_group; + + ring = iio_sw_rb_allocate(indio_dev); + if (!ring) { + ret = -ENOMEM; + return ret; + } + indio_dev->ring = ring; + /* Effectively select the ring buffer implementation */ + iio_ring_sw_register_funcs(&ring->access); + ring->preenable = &adis16260_data_rdy_ring_preenable; + ring->postenable = &adis16260_data_rdy_ring_postenable; + ring->predisable = &adis16260_data_rdy_ring_predisable; + ring->owner = THIS_MODULE; + + indio_dev->pollfunc = kzalloc(sizeof(*indio_dev->pollfunc), GFP_KERNEL); + if (indio_dev->pollfunc == NULL) { + ret = -ENOMEM; + goto error_iio_sw_rb_free;; + } + indio_dev->pollfunc->poll_func_main = &adis16260_poll_func_th; + indio_dev->pollfunc->private_data = indio_dev; + indio_dev->modes |= INDIO_RING_TRIGGERED; + return 0; + +error_iio_sw_rb_free: + iio_sw_rb_free(indio_dev->ring); + return ret; +} + +int adis16260_initialize_ring(struct iio_ring_buffer *ring) +{ + return iio_ring_buffer_register(ring, 0); +} + +void adis16260_uninitialize_ring(struct iio_ring_buffer *ring) +{ + iio_ring_buffer_unregister(ring); +} 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 @@ +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/mutex.h> +#include <linux/device.h> +#include <linux/kernel.h> +#include <linux/sysfs.h> +#include <linux/list.h> +#include <linux/spi/spi.h> + +#include "../iio.h" +#include "../sysfs.h" +#include "../trigger.h" +#include "adis16260.h" + +/** + * adis16260_data_rdy_trig_poll() the event handler for the data rdy trig + **/ +static int adis16260_data_rdy_trig_poll(struct iio_dev *dev_info, + int index, + s64 timestamp, + int no_test) +{ + struct adis16260_state *st = iio_dev_get_devdata(dev_info); + struct iio_trigger *trig = st->trig; + + trig->timestamp = timestamp; + iio_trigger_poll(trig); + + return IRQ_HANDLED; +} + +IIO_EVENT_SH(data_rdy_trig, &adis16260_data_rdy_trig_poll); + +static DEVICE_ATTR(name, S_IRUGO, iio_trigger_read_name, NULL); + +static struct attribute *adis16260_trigger_attrs[] = { + &dev_attr_name.attr, + NULL, +}; + +static const struct attribute_group adis16260_trigger_attr_group = { + .attrs = adis16260_trigger_attrs, +}; + +/** + * adis16260_data_rdy_trigger_set_state() set datardy interrupt state + **/ +static int adis16260_data_rdy_trigger_set_state(struct iio_trigger *trig, + bool state) +{ + struct adis16260_state *st = trig->private_data; + struct iio_dev *indio_dev = st->indio_dev; + int ret = 0; + + dev_dbg(&indio_dev->dev, "%s (%d)\n", __func__, state); + ret = adis16260_set_irq(&st->indio_dev->dev, state); + if (state == false) { + iio_remove_event_from_list(&iio_event_data_rdy_trig, + &indio_dev->interrupts[0] + ->ev_list); + flush_scheduled_work(); + } else { + iio_add_event_to_list(&iio_event_data_rdy_trig, + &indio_dev->interrupts[0]->ev_list); + } + return ret; +} + +/** + * adis16260_trig_try_reen() try renabling irq for data rdy trigger + * @trig: the datardy trigger + **/ +static int adis16260_trig_try_reen(struct iio_trigger *trig) +{ + struct adis16260_state *st = trig->private_data; + enable_irq(st->us->irq); + return 0; +} + +int adis16260_probe_trigger(struct iio_dev *indio_dev) +{ + int ret; + struct adis16260_state *st = indio_dev->dev_data; + + st->trig = iio_allocate_trigger(); + st->trig->name = kmalloc(IIO_TRIGGER_NAME_LENGTH, GFP_KERNEL); + if (!st->trig->name) { + ret = -ENOMEM; + goto error_free_trig; + } + snprintf((char *)st->trig->name, + IIO_TRIGGER_NAME_LENGTH, + "adis16260-dev%d", indio_dev->id); + st->trig->dev.parent = &st->us->dev; + st->trig->owner = THIS_MODULE; + st->trig->private_data = st; + st->trig->set_trigger_state = &adis16260_data_rdy_trigger_set_state; + st->trig->try_reenable = &adis16260_trig_try_reen; + st->trig->control_attrs = &adis16260_trigger_attr_group; + ret = iio_trigger_register(st->trig); + + /* select default trigger */ + indio_dev->trig = st->trig; + if (ret) + goto error_free_trig_name; + + return 0; + +error_free_trig_name: + kfree(st->trig->name); +error_free_trig: + iio_free_trigger(st->trig); + + return ret; +} + +void adis16260_remove_trigger(struct iio_dev *indio_dev) +{ + struct adis16260_state *state = indio_dev->dev_data; + + iio_trigger_unregister(state->trig); + kfree(state->trig->name); + iio_free_trigger(state->trig); +} diff --git a/drivers/staging/iio/gyro/gyro.h b/drivers/staging/iio/gyro/gyro.h new file mode 100644 index 000000000000..f68edab8f30d --- /dev/null +++ b/drivers/staging/iio/gyro/gyro.h @@ -0,0 +1,43 @@ + +#include "../sysfs.h" + +/* Gyroscope types of attribute */ + +#define IIO_DEV_ATTR_GYRO_OFFSET(_mode, _show, _store, _addr) \ + IIO_DEVICE_ATTR(gyro_offset, _mode, _show, _store, _addr) + +#define IIO_DEV_ATTR_GYRO_X_OFFSET(_mode, _show, _store, _addr) \ + IIO_DEVICE_ATTR(gyro_x_offset, _mode, _show, _store, _addr) + +#define IIO_DEV_ATTR_GYRO_Y_OFFSET(_mode, _show, _store, _addr) \ + IIO_DEVICE_ATTR(gyro_y_offset, _mode, _show, _store, _addr) + +#define IIO_DEV_ATTR_GYRO_Z_OFFSET(_mode, _show, _store, _addr) \ + IIO_DEVICE_ATTR(gyro_z_offset, _mode, _show, _store, _addr) + +#define IIO_DEV_ATTR_GYRO_X_GAIN(_mode, _show, _store, _addr) \ + IIO_DEVICE_ATTR(gyro_x_gain, _mode, _show, _store, _addr) + +#define IIO_DEV_ATTR_GYRO_Y_GAIN(_mode, _show, _store, _addr) \ + IIO_DEVICE_ATTR(gyro_y_gain, _mode, _show, _store, _addr) + +#define IIO_DEV_ATTR_GYRO_Z_GAIN(_mode, _show, _store, _addr) \ + IIO_DEVICE_ATTR(gyro_z_gain, _mode, _show, _store, _addr) + +#define IIO_DEV_ATTR_GYRO_SCALE(_mode, _show, _store, _addr) \ + IIO_DEVICE_ATTR(gyro_scale, S_IRUGO, _show, _store, _addr) + +#define IIO_DEV_ATTR_GYRO(_show, _addr) \ + IIO_DEVICE_ATTR(gyro_raw, S_IRUGO, _show, NULL, _addr) + +#define IIO_DEV_ATTR_GYRO_X(_show, _addr) \ + IIO_DEVICE_ATTR(gyro_x_raw, S_IRUGO, _show, NULL, _addr) + +#define IIO_DEV_ATTR_GYRO_Y(_show, _addr) \ + IIO_DEVICE_ATTR(gyro_y_raw, S_IRUGO, _show, NULL, _addr) + +#define IIO_DEV_ATTR_GYRO_Z(_show, _addr) \ + IIO_DEVICE_ATTR(gyro_z_raw, S_IRUGO, _show, NULL, _addr) + +#define IIO_DEV_ATTR_ANGL(_show, _addr) \ + IIO_DEVICE_ATTR(angl_raw, S_IRUGO, _show, NULL, _addr) diff --git a/drivers/staging/iio/iio.h b/drivers/staging/iio/iio.h index 71dbfe12b579..fcee47cbe894 100644 --- a/drivers/staging/iio/iio.h +++ b/drivers/staging/iio/iio.h @@ -96,6 +96,7 @@ void iio_remove_event_from_list(struct iio_event_handler_list *el, * control method is used * @scan_count: [INTERN] the number of elements in the current scan mode * @scan_mask: [INTERN] bitmask used in masking scan mode elements + * @available_scan_masks: [DRIVER] optional array of allowed bitmasks * @scan_timestamp: [INTERN] does the scan mode include a timestamp * @trig: [INTERN] current device trigger (ring buffer modes) * @pollfunc: [DRIVER] function run on trigger being recieved @@ -122,7 +123,8 @@ struct iio_dev { struct attribute_group *scan_el_attrs; int scan_count; - u16 scan_mask; + u32 scan_mask; + u32 *available_scan_masks; bool scan_timestamp; struct iio_trigger *trig; struct iio_poll_func *pollfunc; @@ -132,22 +134,57 @@ struct iio_dev { * These are mainly provided to allow for a change of implementation if a device * has a large number of scan elements */ -#define IIO_MAX_SCAN_LENGTH 15 +#define IIO_MAX_SCAN_LENGTH 31 + +/* note 0 used as error indicator as it doesn't make sense. */ +static inline u32 iio_scan_mask_match(u32 *av_masks, u32 mask) +{ + while (*av_masks) { + if (!(~*av_masks & mask)) + return *av_masks; + av_masks++; + } + return 0; +} static inline int iio_scan_mask_query(struct iio_dev *dev_info, int bit) { + u32 mask; + if (bit > IIO_MAX_SCAN_LENGTH) return -EINVAL; + + if (!dev_info->scan_mask) + return 0; + + if (dev_info->available_scan_masks) + mask = iio_scan_mask_match(dev_info->available_scan_masks, + dev_info->scan_mask); else - return !!(dev_info->scan_mask & (1 << bit)); + mask = dev_info->scan_mask; + + if (!mask) + return -EINVAL; + + return !!(mask & (1 << bit)); }; static inline int iio_scan_mask_set(struct iio_dev *dev_info, int bit) { + u32 mask; + u32 trialmask = dev_info->scan_mask | (1 << bit); + if (bit > IIO_MAX_SCAN_LENGTH) return -EINVAL; - dev_info->scan_mask |= (1 << bit); + if (dev_info->available_scan_masks) { + mask = iio_scan_mask_match(dev_info->available_scan_masks, + trialmask); + if (!mask) + return -EINVAL; + } + dev_info->scan_mask = trialmask; dev_info->scan_count++; + return 0; }; @@ -340,7 +377,7 @@ void iio_deallocate_chrdev(struct iio_handler *handler); #define IIO_UNSIGNED(a) (a) extern dev_t iio_devt; -extern struct class iio_class; +extern struct bus_type iio_bus_type; /** * iio_put_device() - reference counted deallocation of struct device diff --git a/drivers/staging/iio/imu/Kconfig b/drivers/staging/iio/imu/Kconfig new file mode 100644 index 000000000000..6308d6faad57 --- /dev/null +++ b/drivers/staging/iio/imu/Kconfig @@ -0,0 +1,33 @@ +# +# IIO imu drivers configuration +# +comment "Inertial measurement units" + +config ADIS16300 + tristate "Analog Devices ADIS16300 IMU SPI driver" + depends on SPI + select IIO_SW_RING + select IIO_RING_BUFFER + select IIO_TRIGGER + help + Say yes here to build support for Analog Devices adis16300 four degrees + of freedom inertial sensor. + +config ADIS16350 + tristate "Analog Devices ADIS16350/54/55/60/62/64/65 IMU SPI driver" + depends on SPI + select IIO_TRIGGER if IIO_RING_BUFFER + select IIO_SW_RING if IIO_RING_BUFFER + help + Say yes here to build support for Analog Devices adis16350/54/55/60/62/64/65 + high precision tri-axis inertial sensor. + +config ADIS16400 + tristate "Analog Devices ADIS16400/5 IMU SPI driver" + depends on SPI + select IIO_SW_RING + select IIO_RING_BUFFER + select IIO_TRIGGER + help + Say yes here to build support for Analog Devices adis16400/5 triaxial + inertial sensor with Magnetometer.
\ No newline at end of file diff --git a/drivers/staging/iio/imu/Makefile b/drivers/staging/iio/imu/Makefile new file mode 100644 index 000000000000..31df7359e20f --- /dev/null +++ b/drivers/staging/iio/imu/Makefile @@ -0,0 +1,14 @@ +# +# Makefile for Inertial Measurement Units +# +adis16300-y := adis16300_core.o +adis16300-$(CONFIG_IIO_RING_BUFFER) += adis16300_ring.o adis16300_trigger.o +obj-$(CONFIG_ADIS16300) += adis16300.o + +adis16350-y := adis16350_core.o +adis16350-$(CONFIG_IIO_RING_BUFFER) += adis16350_ring.o adis16350_trigger.o +obj-$(CONFIG_ADIS16350) += adis16350.o + +adis16400-y := adis16400_core.o +adis16400-$(CONFIG_IIO_RING_BUFFER) += adis16400_ring.o adis16400_trigger.o +obj-$(CONFIG_ADIS16400) += adis16400.o
\ No newline at end of file diff --git a/drivers/staging/iio/imu/adis16300.h b/drivers/staging/iio/imu/adis16300.h new file mode 100644 index 000000000000..1c7ea5c840ef --- /dev/null +++ b/drivers/staging/iio/imu/adis16300.h @@ -0,0 +1,194 @@ +#ifndef SPI_ADIS16300_H_ +#define SPI_ADIS16300_H_ + +#define ADIS16300_STARTUP_DELAY 220 /* ms */ + +#define ADIS16300_READ_REG(a) a +#define ADIS16300_WRITE_REG(a) ((a) | 0x80) + +#define ADIS16300_FLASH_CNT 0x00 /* Flash memory write count */ +#define ADIS16300_SUPPLY_OUT 0x02 /* Power supply measurement */ +#define ADIS16300_XGYRO_OUT 0x04 /* X-axis gyroscope output */ +#define ADIS16300_XACCL_OUT 0x0A /* X-axis accelerometer output */ +#define ADIS16300_YACCL_OUT 0x0C /* Y-axis accelerometer output */ +#define ADIS16300_ZACCL_OUT 0x0E /* Z-axis accelerometer output */ +#define ADIS16300_TEMP_OUT 0x10 /* Temperature output */ +#define ADIS16300_XINCLI_OUT 0x12 /* X-axis inclinometer output measurement */ +#define ADIS16300_YINCLI_OUT 0x14 /* Y-axis inclinometer output measurement */ +#define ADIS16300_AUX_ADC 0x16 /* Auxiliary ADC measurement */ + +/* Calibration parameters */ +#define ADIS16300_XGYRO_OFF 0x1A /* X-axis gyroscope bias offset factor */ +#define ADIS16300_XACCL_OFF 0x20 /* X-axis acceleration bias offset factor */ +#define ADIS16300_YACCL_OFF 0x22 /* Y-axis acceleration bias offset factor */ +#define ADIS16300_ZACCL_OFF 0x24 /* Z-axis acceleration bias offset factor */ + +#define ADIS16300_GPIO_CTRL 0x32 /* Auxiliary digital input/output control */ +#define ADIS16300_MSC_CTRL 0x34 /* Miscellaneous control */ +#define ADIS16300_SMPL_PRD 0x36 /* Internal sample period (rate) control */ +#define ADIS16300_SENS_AVG 0x38 /* Dynamic range and digital filter control */ +#define ADIS16300_SLP_CNT 0x3A /* Sleep mode control */ +#define ADIS16300_DIAG_STAT 0x3C /* System status */ + +/* Alarm functions */ +#define ADIS16300_GLOB_CMD 0x3E /* System command */ +#define ADIS16300_ALM_MAG1 0x26 /* Alarm 1 amplitude threshold */ +#define ADIS16300_ALM_MAG2 0x28 /* Alarm 2 amplitude threshold */ +#define ADIS16300_ALM_SMPL1 0x2A /* Alarm 1 sample size */ +#define ADIS16300_ALM_SMPL2 0x2C /* Alarm 2 sample size */ +#define ADIS16300_ALM_CTRL 0x2E /* Alarm control */ +#define ADIS16300_AUX_DAC 0x30 /* Auxiliary DAC data */ + +#define ADIS16300_ERROR_ACTIVE (1<<14) +#define ADIS16300_NEW_DATA (1<<15) + +/* MSC_CTRL */ +#define ADIS16300_MSC_CTRL_MEM_TEST (1<<11) +#define ADIS16300_MSC_CTRL_INT_SELF_TEST (1<<10) +#define ADIS16300_MSC_CTRL_NEG_SELF_TEST (1<<9) +#define ADIS16300_MSC_CTRL_POS_SELF_TEST (1<<8) +#define ADIS16300_MSC_CTRL_GYRO_BIAS (1<<7) +#define ADIS16300_MSC_CTRL_ACCL_ALIGN (1<<6) +#define ADIS16300_MSC_CTRL_DATA_RDY_EN (1<<2) +#define ADIS16300_MSC_CTRL_DATA_RDY_POL_HIGH (1<<1) +#define ADIS16300_MSC_CTRL_DATA_RDY_DIO2 (1<<0) + +/* SMPL_PRD */ +#define ADIS16300_SMPL_PRD_TIME_BASE (1<<7) +#define ADIS16300_SMPL_PRD_DIV_MASK 0x7F + +/* DIAG_STAT */ +#define ADIS16300_DIAG_STAT_ZACCL_FAIL (1<<15) +#define ADIS16300_DIAG_STAT_YACCL_FAIL (1<<14) +#define ADIS16300_DIAG_STAT_XACCL_FAIL (1<<13) +#define ADIS16300_DIAG_STAT_XGYRO_FAIL (1<<10) +#define ADIS16300_DIAG_STAT_ALARM2 (1<<9) +#define ADIS16300_DIAG_STAT_ALARM1 (1<<8) +#define ADIS16300_DIAG_STAT_FLASH_CHK (1<<6) +#define ADIS16300_DIAG_STAT_SELF_TEST (1<<5) +#define ADIS16300_DIAG_STAT_OVERFLOW (1<<4) +#define ADIS16300_DIAG_STAT_SPI_FAIL (1<<3) +#define ADIS16300_DIAG_STAT_FLASH_UPT (1<<2) +#define ADIS16300_DIAG_STAT_POWER_HIGH (1<<1) +#define ADIS16300_DIAG_STAT_POWER_LOW (1<<0) + +/* GLOB_CMD */ +#define ADIS16300_GLOB_CMD_SW_RESET (1<<7) +#define ADIS16300_GLOB_CMD_P_AUTO_NULL (1<<4) +#define ADIS16300_GLOB_CMD_FLASH_UPD (1<<3) +#define ADIS16300_GLOB_CMD_DAC_LATCH (1<<2) +#define ADIS16300_GLOB_CMD_FAC_CALIB (1<<1) +#define ADIS16300_GLOB_CMD_AUTO_NULL (1<<0) + +/* SLP_CNT */ +#define ADIS16300_SLP_CNT_POWER_OFF (1<<8) + +#define ADIS16300_MAX_TX 18 +#define ADIS16300_MAX_RX 18 + +#define ADIS16300_SPI_SLOW (u32)(300 * 1000) +#define ADIS16300_SPI_BURST (u32)(1000 * 1000) +#define ADIS16300_SPI_FAST (u32)(2000 * 1000) + +/** + * struct adis16300_state - device instance specific data + * @us: actual spi_device + * @work_trigger_to_ring: bh for triggered event handling + * @work_cont_thresh: CLEAN + * @inter: used to check if new interrupt has been triggered + * @last_timestamp: passing timestamp from th to bh of interrupt handler + * @indio_dev: industrial I/O device structure + * @trig: data ready trigger registered with iio + * @tx: transmit buffer + * @rx: recieve buffer + * @buf_lock: mutex to protect tx and rx + **/ +struct adis16300_state { + struct spi_device *us; + struct work_struct work_trigger_to_ring; + struct iio_work_cont work_cont_thresh; + s64 last_timestamp; + struct iio_dev *indio_dev; + struct iio_trigger *trig; + u8 *tx; + u8 *rx; + struct mutex buf_lock; +}; + +int adis16300_spi_read_burst(struct device *dev, u8 *rx); + +int adis16300_set_irq(struct device *dev, bool enable); + +int adis16300_reset(struct device *dev); + +int adis16300_check_status(struct device *dev); + +#ifdef CONFIG_IIO_RING_BUFFER +/* At the moment triggers are only used for ring buffer + * filling. This may change! + */ + +enum adis16300_scan { + ADIS16300_SCAN_SUPPLY, + ADIS16300_SCAN_GYRO_X, + ADIS16300_SCAN_ACC_X, + ADIS16300_SCAN_ACC_Y, + ADIS16300_SCAN_ACC_Z, + ADIS16300_SCAN_TEMP, + ADIS16300_SCAN_ADC_0, + ADIS16300_SCAN_INCLI_X, + ADIS16300_SCAN_INCLI_Y, +}; + +void adis16300_remove_trigger(struct iio_dev *indio_dev); +int adis16300_probe_trigger(struct iio_dev *indio_dev); + +ssize_t adis16300_read_data_from_ring(struct device *dev, + struct device_attribute *attr, + char *buf); + + +int adis16300_configure_ring(struct iio_dev *indio_dev); +void adis16300_unconfigure_ring(struct iio_dev *indio_dev); + +int adis16300_initialize_ring(struct iio_ring_buffer *ring); +void adis16300_uninitialize_ring(struct iio_ring_buffer *ring); +#else /* CONFIG_IIO_RING_BUFFER */ + +static inline void adis16300_remove_trigger(struct iio_dev *indio_dev) +{ +} + +static inline int adis16300_probe_trigger(struct iio_dev *indio_dev) +{ + return 0; +} + +static inline ssize_t +adis16300_read_data_from_ring(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return 0; +} + +static int adis16300_configure_ring(struct iio_dev *indio_dev) +{ + return 0; +} + +static inline void adis16300_unconfigure_ring(struct iio_dev *indio_dev) +{ +} + +static inline int adis16300_initialize_ring(struct iio_ring_buffer *ring) +{ + return 0; +} + +static inline void adis16300_uninitialize_ring(struct iio_ring_buffer *ring) +{ +} + +#endif /* CONFIG_IIO_RING_BUFFER */ +#endif /* SPI_ADIS16300_H_ */ diff --git a/drivers/staging/iio/imu/adis16300_core.c b/drivers/staging/iio/imu/adis16300_core.c new file mode 100644 index 000000000000..5a7e5ef9bc5d --- /dev/null +++ b/drivers/staging/iio/imu/adis16300_core.c @@ -0,0 +1,768 @@ +/* + * ADIS16300 Four Degrees of Freedom Inertial Sensor Driver + * + * Copyright 2010 Analog Devices Inc. + * + * Licensed under the GPL-2 or later. + */ + +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/gpio.h> +#include <linux/delay.h> +#include <linux/mutex.h> +#include <linux/device.h> +#include <linux/kernel.h> +#include <linux/spi/spi.h> + +#include <linux/sysfs.h> +#include <linux/list.h> + +#include "../iio.h" +#include "../sysfs.h" +#include "../accel/accel.h" +#include "../accel/inclinometer.h" +#include "../gyro/gyro.h" +#include "../adc/adc.h" + +#include "adis16300.h" + +#define DRIVER_NAME "adis16300" + +/* At the moment the spi framework doesn't allow global setting of cs_change. + * It's in the likely to be added comment at the top of spi.h. + * This means that use cannot be made of spi_write etc. + */ + +/** + * adis16300_spi_write_reg_8() - write single byte to a register + * @dev: device associated with child of actual device (iio_dev or iio_trig) + * @reg_address: the address of the register to be written + * @val: the value to write + **/ +static int adis16300_spi_write_reg_8(struct device *dev, + u8 reg_address, + u8 val) +{ + int ret; + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct adis16300_state *st = iio_dev_get_devdata(indio_dev); + + mutex_lock(&st->buf_lock); + st->tx[0] = ADIS16300_WRITE_REG(reg_address); + st->tx[1] = val; + + ret = spi_write(st->us, st->tx, 2); + mutex_unlock(&st->buf_lock); + + return ret; +} + +/** + * adis16300_spi_write_reg_16() - write 2 bytes to a pair of registers + * @dev: device associated with child of actual device (iio_dev or iio_trig) + * @reg_address: the address of the lower of the two registers. Second register + * is assumed to have address one greater. + * @val: value to be written + **/ +static int adis16300_spi_write_reg_16(struct device *dev, + u8 lower_reg_address, + u16 value) +{ + int ret; + struct spi_message msg; + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct adis16300_state *st = iio_dev_get_devdata(indio_dev); + struct spi_transfer xfers[] = { + { + .tx_buf = st->tx, + .bits_per_word = 8, + .len = 2, + .cs_change = 1, + }, { + .tx_buf = st->tx + 2, + .bits_per_word = 8, + .len = 2, + .cs_change = 1, + }, + }; + + mutex_lock(&st->buf_lock); + st->tx[0] = ADIS16300_WRITE_REG(lower_reg_address); + st->tx[1] = value & 0xFF; + st->tx[2] = ADIS16300_WRITE_REG(lower_reg_address + 1); + st->tx[3] = (value >> 8) & 0xFF; + + spi_message_init(&msg); + spi_message_add_tail(&xfers[0], &msg); + spi_message_add_tail(&xfers[1], &msg); + ret = spi_sync(st->us, &msg); + mutex_unlock(&st->buf_lock); + + return ret; +} + +/** + * adis16300_spi_read_reg_16() - read 2 bytes from a 16-bit register + * @dev: device associated with child of actual device (iio_dev or iio_trig) + * @reg_address: the address of the lower of the two registers. Second register + * is assumed to have address one greater. + * @val: somewhere to pass back the value read + **/ +static int adis16300_spi_read_reg_16(struct device *dev, + u8 lower_reg_address, + u16 *val) +{ + struct spi_message msg; + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct adis16300_state *st = iio_dev_get_devdata(indio_dev); + int ret; + struct spi_transfer xfers[] = { + { + .tx_buf = st->tx, + .bits_per_word = 8, + .len = 2, + .cs_change = 0, + }, { + .rx_buf = st->rx, + .bits_per_word = 8, + .len = 2, + .cs_change = 0, + }, + }; + + mutex_lock(&st->buf_lock); + st->tx[0] = ADIS16300_READ_REG(lower_reg_address); + st->tx[1] = 0; + st->tx[2] = 0; + st->tx[3] = 0; + + spi_message_init(&msg); + spi_message_add_tail(&xfers[0], &msg); + spi_message_add_tail(&xfers[1], &msg); + ret = spi_sync(st->us, &msg); + if (ret) { + dev_err(&st->us->dev, + "problem when reading 16 bit register 0x%02X", + lower_reg_address); + goto error_ret; + } + *val = (st->rx[0] << 8) | st->rx[1]; + +error_ret: + mutex_unlock(&st->buf_lock); + return ret; +} + +/** + * adis16300_spi_read_burst() - read all data registers + * @dev: device associated with child of actual device (iio_dev or iio_trig) + * @rx: somewhere to pass back the value read (min size is 24 bytes) + **/ +int adis16300_spi_read_burst(struct device *dev, u8 *rx) +{ + struct spi_message msg; + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct adis16300_state *st = iio_dev_get_devdata(indio_dev); + u32 old_speed_hz = st->us->max_speed_hz; + int ret; + + struct spi_transfer xfers[] = { + { + .tx_buf = st->tx, + .bits_per_word = 8, + .len = 2, + .cs_change = 0, + }, { + .rx_buf = rx, + .bits_per_word = 8, + .len = 18, + .cs_change = 0, + }, + }; + + mutex_lock(&st->buf_lock); + st->tx[0] = ADIS16300_READ_REG(ADIS16300_GLOB_CMD); + st->tx[1] = 0; + + spi_message_init(&msg); + spi_message_add_tail(&xfers[0], &msg); + spi_message_add_tail(&xfers[1], &msg); + + st->us->max_speed_hz = min(ADIS16300_SPI_BURST, old_speed_hz); + spi_setup(st->us); + + ret = spi_sync(st->us, &msg); + if (ret) + dev_err(&st->us->dev, "problem when burst reading"); + + st->us->max_speed_hz = old_speed_hz; + spi_setup(st->us); + mutex_unlock(&st->buf_lock); + return ret; +} + +static ssize_t adis16300_spi_read_signed(struct device *dev, + struct device_attribute *attr, + char *buf, + unsigned bits) +{ + int ret; + s16 val = 0; + unsigned shift = 16 - bits; + struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); + + ret = adis16300_spi_read_reg_16(dev, this_attr->address, (u16 *)&val); + if (ret) + return ret; + + if (val & ADIS16300_ERROR_ACTIVE) + adis16300_check_status(dev); + val = ((s16)(val << shift) >> shift); + return sprintf(buf, "%d\n", val); +} + +static ssize_t adis16300_read_12bit_unsigned(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int ret; + u16 val = 0; + struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); + + ret = adis16300_spi_read_reg_16(dev, this_attr->address, &val); + if (ret) + return ret; + + if (val & ADIS16300_ERROR_ACTIVE) + adis16300_check_status(dev); + + return sprintf(buf, "%u\n", val & 0x0FFF); +} + +static ssize_t adis16300_read_14bit_signed(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + ssize_t ret; + + /* Take the iio_dev status lock */ + mutex_lock(&indio_dev->mlock); + ret = adis16300_spi_read_signed(dev, attr, buf, 14); + mutex_unlock(&indio_dev->mlock); + + return ret; +} + +static ssize_t adis16300_read_12bit_signed(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + ssize_t ret; + + /* Take the iio_dev status lock */ + mutex_lock(&indio_dev->mlock); + ret = adis16300_spi_read_signed(dev, attr, buf, 12); + mutex_unlock(&indio_dev->mlock); + + return ret; +} + +static ssize_t adis16300_read_13bit_signed(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + ssize_t ret; + + /* Take the iio_dev status lock */ + mutex_lock(&indio_dev->mlock); + ret = adis16300_spi_read_signed(dev, attr, buf, 13); + mutex_unlock(&indio_dev->mlock); + + return ret; +} + +static ssize_t adis16300_write_16bit(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t len) +{ + struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); + int ret; + long val; + + ret = strict_strtol(buf, 10, &val); + if (ret) + goto error_ret; + ret = adis16300_spi_write_reg_16(dev, this_attr->address, val); + +error_ret: + return ret ? ret : len; +} + +static ssize_t adis16300_read_frequency(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int ret, len = 0; + u16 t; + int sps; + ret = adis16300_spi_read_reg_16(dev, + ADIS16300_SMPL_PRD, + &t); + if (ret) + return ret; + sps = (t & ADIS16300_SMPL_PRD_TIME_BASE) ? 53 : 1638; + sps /= (t & ADIS16300_SMPL_PRD_DIV_MASK) + 1; + len = sprintf(buf, "%d SPS\n", sps); + return len; +} + +static ssize_t adis16300_write_frequency(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t len) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct adis16300_state *st = iio_dev_get_devdata(indio_dev); + long val; + int ret; + u8 t; + + ret = strict_strtol(buf, 10, &val); + if (ret) + return ret; + + mutex_lock(&indio_dev->mlock); + + t = (1638 / val); + if (t > 0) + t--; + t &= ADIS16300_SMPL_PRD_DIV_MASK; + if ((t & ADIS16300_SMPL_PRD_DIV_MASK) >= 0x0A) + st->us->max_speed_hz = ADIS16300_SPI_SLOW; + else + st->us->max_speed_hz = ADIS16300_SPI_FAST; + + ret = adis16300_spi_write_reg_8(dev, + ADIS16300_SMPL_PRD, + t); + + mutex_unlock(&indio_dev->mlock); + + return ret ? ret : len; +} + +static ssize_t adis16300_write_reset(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + if (len < 1) + return -1; + switch (buf[0]) { + case '1': + case 'y': + case 'Y': + return adis16300_reset(dev); + } + return -1; +} + + + +int adis16300_set_irq(struct device *dev, bool enable) +{ + int ret; + u16 msc; + ret = adis16300_spi_read_reg_16(dev, ADIS16300_MSC_CTRL, &msc); + if (ret) + goto error_ret; + + msc |= ADIS16300_MSC_CTRL_DATA_RDY_POL_HIGH; + msc &= ~ADIS16300_MSC_CTRL_DATA_RDY_DIO2; + if (enable) + msc |= ADIS16300_MSC_CTRL_DATA_RDY_EN; + else + msc &= ~ADIS16300_MSC_CTRL_DATA_RDY_EN; + + ret = adis16300_spi_write_reg_16(dev, ADIS16300_MSC_CTRL, msc); + if (ret) + goto error_ret; + +error_ret: + return ret; +} + +int adis16300_reset(struct device *dev) +{ + int ret; + ret = adis16300_spi_write_reg_8(dev, + ADIS16300_GLOB_CMD, + ADIS16300_GLOB_CMD_SW_RESET); + if (ret) + dev_err(dev, "problem resetting device"); + + return ret; +} + +/* Power down the device */ +static int adis16300_stop_device(struct device *dev) +{ + int ret; + u16 val = ADIS16300_SLP_CNT_POWER_OFF; + + ret = adis16300_spi_write_reg_16(dev, ADIS16300_SLP_CNT, val); + if (ret) + dev_err(dev, "problem with turning device off: SLP_CNT"); + + return ret; +} + +int adis16300_check_status(struct device *dev) +{ + u16 status; + int ret; + + ret = adis16300_spi_read_reg_16(dev, ADIS16300_DIAG_STAT, &status); + + if (ret < 0) { + dev_err(dev, "Reading status failed\n"); + goto error_ret; + } + ret = status; + if (status & ADIS16300_DIAG_STAT_ZACCL_FAIL) + dev_err(dev, "Z-axis accelerometer self-test failure\n"); + if (status & ADIS16300_DIAG_STAT_YACCL_FAIL) + dev_err(dev, "Y-axis accelerometer self-test failure\n"); + if (status & ADIS16300_DIAG_STAT_XACCL_FAIL) + dev_err(dev, "X-axis accelerometer self-test failure\n"); + if (status & ADIS16300_DIAG_STAT_XGYRO_FAIL) + dev_err(dev, "X-axis gyroscope self-test failure\n"); + if (status & ADIS16300_DIAG_STAT_ALARM2) + dev_err(dev, "Alarm 2 active\n"); + if (status & ADIS16300_DIAG_STAT_ALARM1) + dev_err(dev, "Alarm 1 active\n"); + if (status & ADIS16300_DIAG_STAT_FLASH_CHK) + dev_err(dev, "Flash checksum error\n"); + if (status & ADIS16300_DIAG_STAT_SELF_TEST) + dev_err(dev, "Self test error\n"); + if (status & ADIS16300_DIAG_STAT_OVERFLOW) + dev_err(dev, "Sensor overrange\n"); + if (status & ADIS16300_DIAG_STAT_SPI_FAIL) + dev_err(dev, "SPI failure\n"); + if (status & ADIS16300_DIAG_STAT_FLASH_UPT) + dev_err(dev, "Flash update failed\n"); + if (status & ADIS16300_DIAG_STAT_POWER_HIGH) + dev_err(dev, "Power supply above 5.25V\n"); + if (status & ADIS16300_DIAG_STAT_POWER_LOW) + dev_err(dev, "Power supply below 4.75V\n"); + +error_ret: + return ret; +} + +static int adis16300_initial_setup(struct adis16300_state *st) +{ + int ret; + u16 smp_prd; + struct device *dev = &st->indio_dev->dev; + + /* use low spi speed for init */ + st->us->max_speed_hz = ADIS16300_SPI_SLOW; + st->us->mode = SPI_MODE_3; + spi_setup(st->us); + + /* Disable IRQ */ + ret = adis16300_set_irq(dev, false); + if (ret) { + dev_err(dev, "disable irq failed"); + goto err_ret; + } + + /* Do self test */ + + /* Read status register to check the result */ + ret = adis16300_check_status(dev); + if (ret) { + adis16300_reset(dev); + dev_err(dev, "device not playing ball -> reset"); + msleep(ADIS16300_STARTUP_DELAY); + ret = adis16300_check_status(dev); + if (ret) { + dev_err(dev, "giving up"); + goto err_ret; + } + } + + printk(KERN_INFO DRIVER_NAME ": at CS%d (irq %d)\n", + st->us->chip_select, st->us->irq); + + /* use high spi speed if possible */ + ret = adis16300_spi_read_reg_16(dev, ADIS16300_SMPL_PRD, &smp_prd); + if (!ret && (smp_prd & ADIS16300_SMPL_PRD_DIV_MASK) < 0x0A) { + st->us->max_speed_hz = ADIS16300_SPI_SLOW; + spi_setup(st->us); + } + +err_ret: + return ret; +} + +static IIO_DEV_ATTR_ACCEL_X_OFFSET(S_IWUSR | S_IRUGO, + adis16300_read_12bit_signed, + adis16300_write_16bit, + ADIS16300_XACCL_OFF); + +static IIO_DEV_ATTR_ACCEL_Y_OFFSET(S_IWUSR | S_IRUGO, + adis16300_read_12bit_signed, + adis16300_write_16bit, + ADIS16300_YACCL_OFF); + +static IIO_DEV_ATTR_ACCEL_Z_OFFSET(S_IWUSR | S_IRUGO, + adis16300_read_12bit_signed, + adis16300_write_16bit, + ADIS16300_ZACCL_OFF); + +static IIO_DEV_ATTR_IN_NAMED_RAW(supply, adis16300_read_14bit_signed, + ADIS16300_SUPPLY_OUT); +static IIO_CONST_ATTR(in_supply_scale, "0.00242"); + +static IIO_DEV_ATTR_GYRO_X(adis16300_read_14bit_signed, + ADIS16300_XGYRO_OUT); +static IIO_CONST_ATTR(gyro_scale, "0.05 deg/s"); + +static IIO_DEV_ATTR_ACCEL_X(adis16300_read_14bit_signed, + ADIS16300_XACCL_OUT); +static IIO_DEV_ATTR_ACCEL_Y(adis16300_read_14bit_signed, + ADIS16300_YACCL_OUT); +static IIO_DEV_ATTR_ACCEL_Z(adis16300_read_14bit_signed, + ADIS16300_ZACCL_OUT); +static IIO_CONST_ATTR(accel_scale, "0.0006 g"); + +static IIO_DEV_ATTR_INCLI_X(adis16300_read_13bit_signed, + ADIS16300_XINCLI_OUT); +static IIO_DEV_ATTR_INCLI_Y(adis16300_read_13bit_signed, + ADIS16300_YINCLI_OUT); +static IIO_CONST_ATTR(incli_scale, "0.044 d"); + +static IIO_DEV_ATTR_TEMP_RAW(adis16300_read_12bit_signed); +static IIO_CONST_ATTR(temp_offset, "198.16 K"); +static IIO_CONST_ATTR(temp_scale, "0.14 K"); + +static IIO_DEV_ATTR_IN_RAW(0, adis16300_read_12bit_unsigned, + ADIS16300_AUX_ADC); +static IIO_CONST_ATTR(in0_scale, "0.000806"); + +static IIO_DEV_ATTR_SAMP_FREQ(S_IWUSR | S_IRUGO, + adis16300_read_frequency, + adis16300_write_frequency); + +static IIO_DEVICE_ATTR(reset, S_IWUSR, NULL, adis16300_write_reset, 0); + +static IIO_CONST_ATTR_AVAIL_SAMP_FREQ("409 546 819 1638"); + +static IIO_CONST_ATTR(name, "adis16300"); + +static struct attribute *adis16300_event_attributes[] = { + NULL +}; + +static struct attribute_group adis16300_event_attribute_group = { + .attrs = adis16300_event_attributes, +}; + +static struct attribute *adis16300_attributes[] = { + &iio_dev_attr_accel_x_offset.dev_attr.attr, + &iio_dev_attr_accel_y_offset.dev_attr.attr, + &iio_dev_attr_accel_z_offset.dev_attr.attr, + &iio_dev_attr_in_supply_raw.dev_attr.attr, + &iio_const_attr_in_supply_scale.dev_attr.attr, + &iio_dev_attr_gyro_x_raw.dev_attr.attr, + &iio_const_attr_gyro_scale.dev_attr.attr, + &iio_dev_attr_accel_x_raw.dev_attr.attr, + &iio_dev_attr_accel_y_raw.dev_attr.attr, + &iio_dev_attr_accel_z_raw.dev_attr.attr, + &iio_const_attr_accel_scale.dev_attr.attr, + &iio_dev_attr_incli_x_raw.dev_attr.attr, + &iio_dev_attr_incli_y_raw.dev_attr.attr, + &iio_const_attr_incli_scale.dev_attr.attr, + &iio_dev_attr_temp_raw.dev_attr.attr, + &iio_const_attr_temp_offset.dev_attr.attr, + &iio_const_attr_temp_scale.dev_attr.attr, + &iio_dev_attr_in0_raw.dev_attr.attr, + &iio_const_attr_in0_scale.dev_attr.attr, + &iio_dev_attr_sampling_frequency.dev_attr.attr, + &iio_const_attr_available_sampling_frequency.dev_attr.attr, + &iio_dev_attr_reset.dev_attr.attr, + &iio_const_attr_name.dev_attr.attr, + NULL +}; + +static const struct attribute_group adis16300_attribute_group = { + .attrs = adis16300_attributes, +}; + +static int __devinit adis16300_probe(struct spi_device *spi) +{ + int ret, regdone = 0; + struct adis16300_state *st = kzalloc(sizeof *st, GFP_KERNEL); + if (!st) { + ret = -ENOMEM; + goto error_ret; + } + /* this is only used for removal purposes */ + spi_set_drvdata(spi, st); + + /* Allocate the comms buffers */ + st->rx = kzalloc(sizeof(*st->rx)*ADIS16300_MAX_RX, GFP_KERNEL); + if (st->rx == NULL) { + ret = -ENOMEM; + goto error_free_st; + } + st->tx = kzalloc(sizeof(*st->tx)*ADIS16300_MAX_TX, GFP_KERNEL); + if (st->tx == NULL) { + ret = -ENOMEM; + goto error_free_rx; + } + st->us = spi; + mutex_init(&st->buf_lock); + /* setup the industrialio driver allocated elements */ + st->indio_dev = iio_allocate_device(); + if (st->indio_dev == NULL) { + ret = -ENOMEM; + goto error_free_tx; + } + + st->indio_dev->dev.parent = &spi->dev; + st->indio_dev->num_interrupt_lines = 1; + st->indio_dev->event_attrs = &adis16300_event_attribute_group; + st->indio_dev->attrs = &adis16300_attribute_group; + st->indio_dev->dev_data = (void *)(st); + st->indio_dev->driver_module = THIS_MODULE; + st->indio_dev->modes = INDIO_DIRECT_MODE; + + ret = adis16300_configure_ring(st->indio_dev); + if (ret) + goto error_free_dev; + + ret = iio_device_register(st->indio_dev); + if (ret) + goto error_unreg_ring_funcs; + regdone = 1; + + ret = adis16300_initialize_ring(st->indio_dev->ring); + if (ret) { + printk(KERN_ERR "failed to initialize the ring\n"); + goto error_unreg_ring_funcs; + } + + if (spi->irq && gpio_is_valid(irq_to_gpio(spi->irq)) > 0) { +#if 0 /* fixme: here we should support */ + iio_init_work_cont(&st->work_cont_thresh, + NULL, + adis16300_thresh_handler_bh_no_check, + 0, + 0, + st); +#endif + ret = iio_register_interrupt_line(spi->irq, + st->indio_dev, + 0, + IRQF_TRIGGER_RISING, + "adis16300"); + if (ret) + goto error_uninitialize_ring; + + ret = adis16300_probe_trigger(st->indio_dev); + if (ret) + goto error_unregister_line; + } + + /* Get the device into a sane initial state */ + ret = adis16300_initial_setup(st); + if (ret) + goto error_remove_trigger; + return 0; + +error_remove_trigger: + if (st->indio_dev->modes & INDIO_RING_TRIGGERED) + adis16300_remove_trigger(st->indio_dev); +error_unregister_line: + if (st->indio_dev->modes & INDIO_RING_TRIGGERED) + iio_unregister_interrupt_line(st->indio_dev, 0); +error_uninitialize_ring: + adis16300_uninitialize_ring(st->indio_dev->ring); +error_unreg_ring_funcs: + adis16300_unconfigure_ring(st->indio_dev); +error_free_dev: + if (regdone) + iio_device_unregister(st->indio_dev); + else + iio_free_device(st->indio_dev); +error_free_tx: + kfree(st->tx); +error_free_rx: + kfree(st->rx); +error_free_st: + kfree(st); +error_ret: + return ret; +} + +/* fixme, confirm ordering in this function */ +static int adis16300_remove(struct spi_device *spi) +{ + int ret; + struct adis16300_state *st = spi_get_drvdata(spi); + struct iio_dev *indio_dev = st->indio_dev; + + ret = adis16300_stop_device(&(indio_dev->dev)); + if (ret) + goto err_ret; + + flush_scheduled_work(); + + adis16300_remove_trigger(indio_dev); + if (spi->irq && gpio_is_valid(irq_to_gpio(spi->irq)) > 0) + iio_unregister_interrupt_line(indio_dev, 0); + + adis16300_uninitialize_ring(indio_dev->ring); + adis16300_unconfigure_ring(indio_dev); + iio_device_unregister(indio_dev); + kfree(st->tx); + kfree(st->rx); + kfree(st); + + return 0; + +err_ret: + return ret; +} + +static struct spi_driver adis16300_driver = { + .driver = { + .name = "adis16300", + .owner = THIS_MODULE, + }, + .probe = adis16300_probe, + .remove = __devexit_p(adis16300_remove), +}; + +static __init int adis16300_init(void) +{ + return spi_register_driver(&adis16300_driver); +} +module_init(adis16300_init); + +static __exit void adis16300_exit(void) +{ + spi_unregister_driver(&adis16300_driver); +} +module_exit(adis16300_exit); + +MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>"); +MODULE_DESCRIPTION("Analog Devices ADIS16300 IMU SPI driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/iio/imu/adis16300_ring.c b/drivers/staging/iio/imu/adis16300_ring.c new file mode 100644 index 000000000000..76cf8a6f3c3f --- /dev/null +++ b/drivers/staging/iio/imu/adis16300_ring.c @@ -0,0 +1,233 @@ +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/gpio.h> +#include <linux/workqueue.h> +#include <linux/mutex.h> +#include <linux/device.h> +#include <linux/kernel.h> +#include <linux/spi/spi.h> +#include <linux/sysfs.h> +#include <linux/list.h> + +#include "../iio.h" +#include "../sysfs.h" +#include "../ring_sw.h" +#include "../accel/accel.h" +#include "../trigger.h" +#include "adis16300.h" + +/** + * combine_8_to_16() utility function to munge to u8s into u16 + **/ +static inline u16 combine_8_to_16(u8 lower, u8 upper) +{ + u16 _lower = lower; + u16 _upper = upper; + return _lower | (_upper << 8); +} + +static IIO_SCAN_EL_C(supply, ADIS16300_SCAN_SUPPLY, IIO_SIGNED(14), + ADIS16300_SUPPLY_OUT, NULL); + +static IIO_SCAN_EL_C(gyro_x, ADIS16300_SCAN_GYRO_X, IIO_SIGNED(14), + ADIS16300_XGYRO_OUT, NULL); + +static IIO_SCAN_EL_C(accel_x, ADIS16300_SCAN_ACC_X, IIO_SIGNED(14), + ADIS16300_XACCL_OUT, NULL); +static IIO_SCAN_EL_C(accel_y, ADIS16300_SCAN_ACC_Y, IIO_SIGNED(14), + ADIS16300_YACCL_OUT, NULL); +static IIO_SCAN_EL_C(accel_z, ADIS16300_SCAN_ACC_Z, IIO_SIGNED(14), + ADIS16300_ZACCL_OUT, NULL); + +static IIO_SCAN_EL_C(temp, ADIS16300_SCAN_TEMP, IIO_SIGNED(12), + ADIS16300_TEMP_OUT, NULL); +static IIO_SCAN_EL_C(adc_0, ADIS16300_SCAN_ADC_0, IIO_SIGNED(12), + ADIS16300_AUX_ADC, NULL); + +static IIO_SCAN_EL_C(incli_x, ADIS16300_SCAN_INCLI_X, IIO_SIGNED(12), + ADIS16300_XINCLI_OUT, NULL); +static IIO_SCAN_EL_C(incli_y, ADIS16300_SCAN_INCLI_Y, IIO_SIGNED(12), + ADIS16300_YINCLI_OUT, NULL); + +static IIO_SCAN_EL_TIMESTAMP(9); + +static struct attribute *adis16300_scan_el_attrs[] = { + &iio_scan_el_supply.dev_attr.attr, + &iio_scan_el_gyro_x.dev_attr.attr, + &iio_scan_el_temp.dev_attr.attr, + &iio_scan_el_accel_x.dev_attr.attr, + &iio_scan_el_accel_y.dev_attr.attr, + &iio_scan_el_accel_z.dev_attr.attr, + &iio_scan_el_incli_x.dev_attr.attr, + &iio_scan_el_incli_y.dev_attr.attr, + &iio_scan_el_adc_0.dev_attr.attr, + &iio_scan_el_timestamp.dev_attr.attr, + NULL, +}; + +static struct attribute_group adis16300_scan_el_group = { + .attrs = adis16300_scan_el_attrs, + .name = "scan_elements", +}; + +/** + * adis16300_poll_func_th() top half interrupt handler called by trigger + * @private_data: iio_dev + **/ +static void adis16300_poll_func_th(struct iio_dev *indio_dev) +{ + struct adis16300_state *st = iio_dev_get_devdata(indio_dev); + st->last_timestamp = indio_dev->trig->timestamp; + schedule_work(&st->work_trigger_to_ring); + /* Indicate that this interrupt is being handled */ + + /* Technically this is trigger related, but without this + * handler running there is currently no way for the interrupt + * to clear. + */ +} + +/* Whilst this makes a lot of calls to iio_sw_ring functions - it is to device + * specific to be rolled into the core. + */ +static void adis16300_trigger_bh_to_ring(struct work_struct *work_s) +{ + struct adis16300_state *st + = container_of(work_s, struct adis16300_state, + work_trigger_to_ring); + + int i = 0; + s16 *data; + size_t datasize = st->indio_dev + ->ring->access.get_bpd(st->indio_dev->ring); + + data = kmalloc(datasize , GFP_KERNEL); + if (data == NULL) { + dev_err(&st->us->dev, "memory alloc failed in ring bh"); + return; + } + + if (st->indio_dev->scan_count) + if (adis16300_spi_read_burst(&st->indio_dev->dev, st->rx) >= 0) + for (; i < st->indio_dev->scan_count; i++) { + data[i] = combine_8_to_16(st->rx[i*2+1], + st->rx[i*2]); + } + + /* Guaranteed to be aligned with 8 byte boundary */ + if (st->indio_dev->scan_timestamp) + *((s64 *)(data + ((i + 3)/4)*4)) = st->last_timestamp; + + st->indio_dev->ring->access.store_to(st->indio_dev->ring, + (u8 *)data, + st->last_timestamp); + + iio_trigger_notify_done(st->indio_dev->trig); + kfree(data); + + return; +} +/* in these circumstances is it better to go with unaligned packing and + * deal with the cost?*/ +static int adis16300_data_rdy_ring_preenable(struct iio_dev *indio_dev) +{ + size_t size; + dev_dbg(&indio_dev->dev, "%s\n", __func__); + /* Check if there are any scan elements enabled, if not fail*/ + if (!(indio_dev->scan_count || indio_dev->scan_timestamp)) + return -EINVAL; + + if (indio_dev->ring->access.set_bpd) { + if (indio_dev->scan_timestamp) + if (indio_dev->scan_count) /* Timestamp and data */ + size = 4*sizeof(s64); + else /* Timestamp only */ + size = sizeof(s64); + else /* Data only */ + size = indio_dev->scan_count*sizeof(s16); + indio_dev->ring->access.set_bpd(indio_dev->ring, size); + } + + return 0; +} + +static int adis16300_data_rdy_ring_postenable(struct iio_dev *indio_dev) +{ + return indio_dev->trig + ? iio_trigger_attach_poll_func(indio_dev->trig, + indio_dev->pollfunc) + : 0; +} + +static int adis16300_data_rdy_ring_predisable(struct iio_dev *indio_dev) +{ + return indio_dev->trig + ? iio_trigger_dettach_poll_func(indio_dev->trig, + indio_dev->pollfunc) + : 0; +} + +void adis16300_unconfigure_ring(struct iio_dev *indio_dev) +{ + kfree(indio_dev->pollfunc); + iio_sw_rb_free(indio_dev->ring); +} + +int adis16300_configure_ring(struct iio_dev *indio_dev) +{ + int ret = 0; + struct adis16300_state *st = indio_dev->dev_data; + struct iio_ring_buffer *ring; + INIT_WORK(&st->work_trigger_to_ring, adis16300_trigger_bh_to_ring); + /* Set default scan mode */ + + iio_scan_mask_set(indio_dev, iio_scan_el_supply.number); + iio_scan_mask_set(indio_dev, iio_scan_el_gyro_x.number); + iio_scan_mask_set(indio_dev, iio_scan_el_accel_x.number); + iio_scan_mask_set(indio_dev, iio_scan_el_accel_y.number); + iio_scan_mask_set(indio_dev, iio_scan_el_accel_z.number); + iio_scan_mask_set(indio_dev, iio_scan_el_temp.number); + iio_scan_mask_set(indio_dev, iio_scan_el_adc_0.number); + iio_scan_mask_set(indio_dev, iio_scan_el_incli_x.number); + iio_scan_mask_set(indio_dev, iio_scan_el_incli_y.number); + indio_dev->scan_timestamp = true; + + indio_dev->scan_el_attrs = &adis16300_scan_el_group; + + ring = iio_sw_rb_allocate(indio_dev); + if (!ring) { + ret = -ENOMEM; + return ret; + } + indio_dev->ring = ring; + /* Effectively select the ring buffer implementation */ + iio_ring_sw_register_funcs(&ring->access); + ring->preenable = &adis16300_data_rdy_ring_preenable; + ring->postenable = &adis16300_data_rdy_ring_postenable; + ring->predisable = &adis16300_data_rdy_ring_predisable; + ring->owner = THIS_MODULE; + + indio_dev->pollfunc = kzalloc(sizeof(*indio_dev->pollfunc), GFP_KERNEL); + if (indio_dev->pollfunc == NULL) { + ret = -ENOMEM; + goto error_iio_sw_rb_free;; + } + indio_dev->pollfunc->poll_func_main = &adis16300_poll_func_th; + indio_dev->pollfunc->private_data = indio_dev; + indio_dev->modes |= INDIO_RING_TRIGGERED; + return 0; + +error_iio_sw_rb_free: + iio_sw_rb_free(indio_dev->ring); + return ret; +} + +int adis16300_initialize_ring(struct iio_ring_buffer *ring) +{ + return iio_ring_buffer_register(ring, 0); +} + +void adis16300_uninitialize_ring(struct iio_ring_buffer *ring) +{ + iio_ring_buffer_unregister(ring); +} diff --git a/drivers/staging/iio/imu/adis16300_trigger.c b/drivers/staging/iio/imu/adis16300_trigger.c new file mode 100644 index 000000000000..54edb20bf119 --- /dev/null +++ b/drivers/staging/iio/imu/adis16300_trigger.c @@ -0,0 +1,127 @@ +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/mutex.h> +#include <linux/device.h> +#include <linux/kernel.h> +#include <linux/sysfs.h> +#include <linux/list.h> +#include <linux/spi/spi.h> + +#include "../iio.h" +#include "../sysfs.h" +#include "../trigger.h" +#include "adis16300.h" + +/** + * adis16300_data_rdy_trig_poll() the event handler for the data rdy trig + **/ +static int adis16300_data_rdy_trig_poll(struct iio_dev *dev_info, + int index, + s64 timestamp, + int no_test) +{ + struct adis16300_state *st = iio_dev_get_devdata(dev_info); + struct iio_trigger *trig = st->trig; + + trig->timestamp = timestamp; + iio_trigger_poll(trig); + + return IRQ_HANDLED; +} + +IIO_EVENT_SH(data_rdy_trig, &adis16300_data_rdy_trig_poll); + +static DEVICE_ATTR(name, S_IRUGO, iio_trigger_read_name, NULL); + +static struct attribute *adis16300_trigger_attrs[] = { + &dev_attr_name.attr, + NULL, +}; + +static const struct attribute_group adis16300_trigger_attr_group = { + .attrs = adis16300_trigger_attrs, +}; + +/** + * adis16300_data_rdy_trigger_set_state() set datardy interrupt state + **/ +static int adis16300_data_rdy_trigger_set_state(struct iio_trigger *trig, + bool state) +{ + struct adis16300_state *st = trig->private_data; + struct iio_dev *indio_dev = st->indio_dev; + int ret = 0; + + dev_dbg(&indio_dev->dev, "%s (%d)\n", __func__, state); + ret = adis16300_set_irq(&st->indio_dev->dev, state); + if (state == false) { + iio_remove_event_from_list(&iio_event_data_rdy_trig, + &indio_dev->interrupts[0] + ->ev_list); + /* possible quirk with handler currently worked around + by ensuring the work queue is empty */ + flush_scheduled_work(); + } else { + iio_add_event_to_list(&iio_event_data_rdy_trig, + &indio_dev->interrupts[0]->ev_list); + } + return ret; +} + +/** + * adis16300_trig_try_reen() try renabling irq for data rdy trigger + * @trig: the datardy trigger + **/ +static int adis16300_trig_try_reen(struct iio_trigger *trig) +{ + struct adis16300_state *st = trig->private_data; + enable_irq(st->us->irq); + /* irq reenabled so success! */ + return 0; +} + +int adis16300_probe_trigger(struct iio_dev *indio_dev) +{ + int ret; + struct adis16300_state *st = indio_dev->dev_data; + + st->trig = iio_allocate_trigger(); + st->trig->name = kmalloc(IIO_TRIGGER_NAME_LENGTH, GFP_KERNEL); + if (!st->trig->name) { + ret = -ENOMEM; + goto error_free_trig; + } + snprintf((char *)st->trig->name, + IIO_TRIGGER_NAME_LENGTH, + "adis16300-dev%d", indio_dev->id); + st->trig->dev.parent = &st->us->dev; + st->trig->owner = THIS_MODULE; + st->trig->private_data = st; + st->trig->set_trigger_state = &adis16300_data_rdy_trigger_set_state; + st->trig->try_reenable = &adis16300_trig_try_reen; + st->trig->control_attrs = &adis16300_trigger_attr_group; + ret = iio_trigger_register(st->trig); + + /* select default trigger */ + indio_dev->trig = st->trig; + if (ret) + goto error_free_trig_name; + + return 0; + +error_free_trig_name: + kfree(st->trig->name); +error_free_trig: + iio_free_trigger(st->trig); + + return ret; +} + +void adis16300_remove_trigger(struct iio_dev *indio_dev) +{ + struct adis16300_state *state = indio_dev->dev_data; + + iio_trigger_unregister(state->trig); + kfree(state->trig->name); + iio_free_trigger(state->trig); +} diff --git a/drivers/staging/iio/imu/adis16350.h b/drivers/staging/iio/imu/adis16350.h new file mode 100644 index 000000000000..334b18ace38e --- /dev/null +++ b/drivers/staging/iio/imu/adis16350.h @@ -0,0 +1,193 @@ +#ifndef SPI_ADIS16350_H_ +#define SPI_ADIS16350_H_ + +#define ADIS16350_STARTUP_DELAY 220 /* ms */ + +#define ADIS16350_READ_REG(a) a +#define ADIS16350_WRITE_REG(a) ((a) | 0x80) + +#define ADIS16350_FLASH_CNT 0x00 /* Flash memory write count */ +#define ADIS16350_SUPPLY_OUT 0x02 /* Power supply measurement */ +#define ADIS16350_XGYRO_OUT 0x04 /* X-axis gyroscope output */ +#define ADIS16350_YGYRO_OUT 0x06 /* Y-axis gyroscope output */ +#define ADIS16350_ZGYRO_OUT 0x08 /* Z-axis gyroscope output */ +#define ADIS16350_XACCL_OUT 0x0A /* X-axis accelerometer output */ +#define ADIS16350_YACCL_OUT 0x0C /* Y-axis accelerometer output */ +#define ADIS16350_ZACCL_OUT 0x0E /* Z-axis accelerometer output */ +#define ADIS16350_XTEMP_OUT 0x10 /* X-axis gyroscope temperature measurement */ +#define ADIS16350_YTEMP_OUT 0x12 /* Y-axis gyroscope temperature measurement */ +#define ADIS16350_ZTEMP_OUT 0x14 /* Z-axis gyroscope temperature measurement */ +#define ADIS16350_AUX_ADC 0x16 /* Auxiliary ADC measurement */ + +/* Calibration parameters */ +#define ADIS16350_XGYRO_OFF 0x1A /* X-axis gyroscope bias offset factor */ +#define ADIS16350_YGYRO_OFF 0x1C /* Y-axis gyroscope bias offset factor */ +#define ADIS16350_ZGYRO_OFF 0x1E /* Z-axis gyroscope bias offset factor */ +#define ADIS16350_XACCL_OFF 0x20 /* X-axis acceleration bias offset factor */ +#define ADIS16350_YACCL_OFF 0x22 /* Y-axis acceleration bias offset factor */ +#define ADIS16350_ZACCL_OFF 0x24 /* Z-axis acceleration bias offset factor */ + +#define ADIS16350_GPIO_CTRL 0x32 /* Auxiliary digital input/output control */ +#define ADIS16350_MSC_CTRL 0x34 /* Miscellaneous control */ +#define ADIS16350_SMPL_PRD 0x36 /* Internal sample period (rate) control */ +#define ADIS16350_SENS_AVG 0x38 /* Dynamic range and digital filter control */ +#define ADIS16350_SLP_CNT 0x3A /* Sleep mode control */ +#define ADIS16350_DIAG_STAT 0x3C /* System status */ + +/* Alarm functions */ +#define ADIS16350_GLOB_CMD 0x3E /* System command */ +#define ADIS16350_ALM_MAG1 0x26 /* Alarm 1 amplitude threshold */ +#define ADIS16350_ALM_MAG2 0x28 /* Alarm 2 amplitude threshold */ +#define ADIS16350_ALM_SMPL1 0x2A /* Alarm 1 sample size */ +#define ADIS16350_ALM_SMPL2 0x2C /* Alarm 2 sample size */ +#define ADIS16350_ALM_CTRL 0x2E /* Alarm control */ +#define ADIS16350_AUX_DAC 0x30 /* Auxiliary DAC data */ + +#define ADIS16350_ERROR_ACTIVE (1<<14) +#define ADIS16350_NEW_DATA (1<<15) + +/* MSC_CTRL */ +#define ADIS16350_MSC_CTRL_MEM_TEST (1<<11) +#define ADIS16350_MSC_CTRL_INT_SELF_TEST (1<<10) +#define ADIS16350_MSC_CTRL_NEG_SELF_TEST (1<<9) +#define ADIS16350_MSC_CTRL_POS_SELF_TEST (1<<8) +#define ADIS16350_MSC_CTRL_GYRO_BIAS (1<<7) +#define ADIS16350_MSC_CTRL_ACCL_ALIGN (1<<6) +#define ADIS16350_MSC_CTRL_DATA_RDY_EN (1<<2) +#define ADIS16350_MSC_CTRL_DATA_RDY_POL_HIGH (1<<1) +#define ADIS16350_MSC_CTRL_DATA_RDY_DIO2 (1<<0) + +/* SMPL_PRD */ +#define ADIS16350_SMPL_PRD_TIME_BASE (1<<7) +#define ADIS16350_SMPL_PRD_DIV_MASK 0x7F + +/* DIAG_STAT */ +#define ADIS16350_DIAG_STAT_ZACCL_FAIL (1<<15) +#define ADIS16350_DIAG_STAT_YACCL_FAIL (1<<14) +#define ADIS16350_DIAG_STAT_XACCL_FAIL (1<<13) +#define ADIS16350_DIAG_STAT_XGYRO_FAIL (1<<12) +#define ADIS16350_DIAG_STAT_YGYRO_FAIL (1<<11) +#define ADIS16350_DIAG_STAT_ZGYRO_FAIL (1<<10) +#define ADIS16350_DIAG_STAT_ALARM2 (1<<9) +#define ADIS16350_DIAG_STAT_ALARM1 (1<<8) +#define ADIS16350_DIAG_STAT_FLASH_CHK (1<<6) +#define ADIS16350_DIAG_STAT_SELF_TEST (1<<5) +#define ADIS16350_DIAG_STAT_OVERFLOW (1<<4) +#define ADIS16350_DIAG_STAT_SPI_FAIL (1<<3) +#define ADIS16350_DIAG_STAT_FLASH_UPT (1<<2) +#define ADIS16350_DIAG_STAT_POWER_HIGH (1<<1) +#define ADIS16350_DIAG_STAT_POWER_LOW (1<<0) + +/* GLOB_CMD */ +#define ADIS16350_GLOB_CMD_SW_RESET (1<<7) +#define ADIS16350_GLOB_CMD_P_AUTO_NULL (1<<4) +#define ADIS16350_GLOB_CMD_FLASH_UPD (1<<3) +#define ADIS16350_GLOB_CMD_DAC_LATCH (1<<2) +#define ADIS16350_GLOB_CMD_FAC_CALIB (1<<1) +#define ADIS16350_GLOB_CMD_AUTO_NULL (1<<0) + +/* SLP_CNT */ +#define ADIS16350_SLP_CNT_POWER_OFF (1<<8) + +#define ADIS16350_MAX_TX 24 +#define ADIS16350_MAX_RX 24 + +#define ADIS16350_SPI_SLOW (u32)(300 * 1000) +#define ADIS16350_SPI_BURST (u32)(1000 * 1000) +#define ADIS16350_SPI_FAST (u32)(2000 * 1000) + +/** + * struct adis16350_state - device instance specific data + * @us: actual spi_device + * @work_trigger_to_ring: bh for triggered event handling + * @work_cont_thresh: CLEAN + * @inter: used to check if new interrupt has been triggered + * @last_timestamp: passing timestamp from th to bh of interrupt handler + * @indio_dev: industrial I/O device structure + * @trig: data ready trigger registered with iio + * @tx: transmit buffer + * @rx: recieve buffer + * @buf_lock: mutex to protect tx and rx + **/ +struct adis16350_state { + struct spi_device *us; + struct work_struct work_trigger_to_ring; + struct iio_work_cont work_cont_data_rdy; + s64 last_timestamp; + struct iio_dev *indio_dev; + struct iio_trigger *trig; + u8 *tx; + u8 *rx; + struct mutex buf_lock; +}; + +int adis16350_set_irq(struct device *dev, bool enable); + +#ifdef CONFIG_IIO_RING_BUFFER + +enum adis16350_scan { + ADIS16350_SCAN_SUPPLY, + ADIS16350_SCAN_GYRO_X, + ADIS16350_SCAN_GYRO_Y, + ADIS16350_SCAN_GYRO_Z, + ADIS16350_SCAN_ACC_X, + ADIS16350_SCAN_ACC_Y, + ADIS16350_SCAN_ACC_Z, + ADIS16350_SCAN_TEMP_X, + ADIS16350_SCAN_TEMP_Y, + ADIS16350_SCAN_TEMP_Z, + ADIS16350_SCAN_ADC_0 +}; + +void adis16350_remove_trigger(struct iio_dev *indio_dev); +int adis16350_probe_trigger(struct iio_dev *indio_dev); + +ssize_t adis16350_read_data_from_ring(struct device *dev, + struct device_attribute *attr, + char *buf); + + +int adis16350_configure_ring(struct iio_dev *indio_dev); +void adis16350_unconfigure_ring(struct iio_dev *indio_dev); + +int adis16350_initialize_ring(struct iio_ring_buffer *ring); +void adis16350_uninitialize_ring(struct iio_ring_buffer *ring); +#else /* CONFIG_IIO_RING_BUFFER */ + +static inline void adis16350_remove_trigger(struct iio_dev *indio_dev) +{ +} + +static inline int adis16350_probe_trigger(struct iio_dev *indio_dev) +{ + return 0; +} + +static inline ssize_t +adis16350_read_data_from_ring(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return 0; +} + +static int adis16350_configure_ring(struct iio_dev *indio_dev) +{ + return 0; +} + +static inline void adis16350_unconfigure_ring(struct iio_dev *indio_dev) +{ +} + +static inline int adis16350_initialize_ring(struct iio_ring_buffer *ring) +{ + return 0; +} + +static inline void adis16350_uninitialize_ring(struct iio_ring_buffer *ring) +{ +} + +#endif /* CONFIG_IIO_RING_BUFFER */ +#endif /* SPI_ADIS16350_H_ */ diff --git a/drivers/staging/iio/imu/adis16350_core.c b/drivers/staging/iio/imu/adis16350_core.c new file mode 100644 index 000000000000..0edde73ce5c2 --- /dev/null +++ b/drivers/staging/iio/imu/adis16350_core.c @@ -0,0 +1,736 @@ +/* + * ADIS16350/54/55/60/62/64/65 high precision tri-axis inertial sensor + * + * Copyright 2010 Analog Devices Inc. + * + * Licensed under the GPL-2 or later. + */ + +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/gpio.h> +#include <linux/delay.h> +#include <linux/mutex.h> +#include <linux/device.h> +#include <linux/kernel.h> +#include <linux/spi/spi.h> + +#include <linux/sysfs.h> +#include <linux/list.h> + +#include "../iio.h" +#include "../sysfs.h" +#include "../accel/accel.h" +#include "../adc/adc.h" +#include "../gyro/gyro.h" + +#include "adis16350.h" + +#define DRIVER_NAME "adis16350" + +static int adis16350_check_status(struct device *dev); + +/** + * adis16350_spi_write_reg_8() - write single byte to a register + * @dev: device associated with child of actual device (iio_dev or iio_trig) + * @reg_address: the address of the register to be written + * @val: the value to write + **/ +static int adis16350_spi_write_reg_8(struct device *dev, + u8 reg_address, + u8 val) +{ + int ret; + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct adis16350_state *st = iio_dev_get_devdata(indio_dev); + + mutex_lock(&st->buf_lock); + st->tx[0] = ADIS16350_WRITE_REG(reg_address); + st->tx[1] = val; + + ret = spi_write(st->us, st->tx, 2); + mutex_unlock(&st->buf_lock); + + return ret; +} + +/** + * adis16350_spi_write_reg_16() - write 2 bytes to a pair of registers + * @dev: device associated with child of actual device (iio_dev or iio_trig) + * @reg_address: the address of the lower of the two registers. Second register + * is assumed to have address one greater. + * @val: value to be written + **/ +static int adis16350_spi_write_reg_16(struct device *dev, + u8 lower_reg_address, + u16 value) +{ + int ret; + struct spi_message msg; + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct adis16350_state *st = iio_dev_get_devdata(indio_dev); + struct spi_transfer xfers[] = { + { + .tx_buf = st->tx, + .bits_per_word = 8, + .len = 2, + .cs_change = 1, + .delay_usecs = 25, + }, { + .tx_buf = st->tx + 2, + .bits_per_word = 8, + .len = 2, + .cs_change = 1, + .delay_usecs = 25, + }, + }; + + mutex_lock(&st->buf_lock); + st->tx[0] = ADIS16350_WRITE_REG(lower_reg_address); + st->tx[1] = value & 0xFF; + st->tx[2] = ADIS16350_WRITE_REG(lower_reg_address + 1); + st->tx[3] = (value >> 8) & 0xFF; + + spi_message_init(&msg); + spi_message_add_tail(&xfers[0], &msg); + spi_message_add_tail(&xfers[1], &msg); + ret = spi_sync(st->us, &msg); + mutex_unlock(&st->buf_lock); + + return ret; +} + +/** + * adis16350_spi_read_reg_16() - read 2 bytes from a 16-bit register + * @dev: device associated with child of actual device (iio_dev or iio_trig) + * @reg_address: the address of the lower of the two registers. Second register + * is assumed to have address one greater. + * @val: somewhere to pass back the value read + **/ +static int adis16350_spi_read_reg_16(struct device *dev, + u8 lower_reg_address, + u16 *val) +{ + struct spi_message msg; + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct adis16350_state *st = iio_dev_get_devdata(indio_dev); + int ret; + struct spi_transfer xfers[] = { + { + .tx_buf = st->tx, + .bits_per_word = 8, + .len = 2, + .cs_change = 1, + .delay_usecs = 25, + }, { + .rx_buf = st->rx, + .bits_per_word = 8, + .len = 2, + .cs_change = 1, + .delay_usecs = 25, + }, + }; + + mutex_lock(&st->buf_lock); + st->tx[0] = ADIS16350_READ_REG(lower_reg_address); + st->tx[1] = 0; + st->tx[2] = 0; + st->tx[3] = 0; + + spi_message_init(&msg); + spi_message_add_tail(&xfers[0], &msg); + spi_message_add_tail(&xfers[1], &msg); + ret = spi_sync(st->us, &msg); + if (ret) { + dev_err(&st->us->dev, + "problem when reading 16 bit register 0x%02X", + lower_reg_address); + goto error_ret; + } + *val = (st->rx[0] << 8) | st->rx[1]; + +error_ret: + mutex_unlock(&st->buf_lock); + return ret; +} + + +static ssize_t adis16350_spi_read_signed(struct device *dev, + struct device_attribute *attr, + char *buf, + unsigned bits) +{ + int ret; + s16 val = 0; + unsigned shift = 16 - bits; + struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); + + ret = adis16350_spi_read_reg_16(dev, this_attr->address, (u16 *)&val); + if (ret) + return ret; + + if (val & ADIS16350_ERROR_ACTIVE) + adis16350_check_status(dev); + val = ((s16)(val << shift) >> shift); + return sprintf(buf, "%d\n", val); +} + +static ssize_t adis16350_read_12bit_unsigned(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int ret; + u16 val = 0; + struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); + + ret = adis16350_spi_read_reg_16(dev, this_attr->address, &val); + if (ret) + return ret; + + if (val & ADIS16350_ERROR_ACTIVE) + adis16350_check_status(dev); + + return sprintf(buf, "%u\n", val & 0x0FFF); +} + +static ssize_t adis16350_read_14bit_signed(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + ssize_t ret; + + /* Take the iio_dev status lock */ + mutex_lock(&indio_dev->mlock); + ret = adis16350_spi_read_signed(dev, attr, buf, 14); + mutex_unlock(&indio_dev->mlock); + + return ret; +} + +static ssize_t adis16350_read_12bit_signed(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + ssize_t ret; + + /* Take the iio_dev status lock */ + mutex_lock(&indio_dev->mlock); + ret = adis16350_spi_read_signed(dev, attr, buf, 12); + mutex_unlock(&indio_dev->mlock); + + return ret; +} + +static ssize_t adis16350_write_16bit(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t len) +{ + struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); + int ret; + long val; + + ret = strict_strtol(buf, 10, &val); + if (ret) + goto error_ret; + ret = adis16350_spi_write_reg_16(dev, this_attr->address, val); + +error_ret: + return ret ? ret : len; +} + +static ssize_t adis16350_read_frequency(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int ret, len = 0; + u16 t; + int sps; + ret = adis16350_spi_read_reg_16(dev, + ADIS16350_SMPL_PRD, + &t); + if (ret) + return ret; + sps = (t & ADIS16350_SMPL_PRD_TIME_BASE) ? 53 : 1638; + sps /= (t & ADIS16350_SMPL_PRD_DIV_MASK) + 1; + len = sprintf(buf, "%d SPS\n", sps); + return len; +} + +static ssize_t adis16350_write_frequency(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t len) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct adis16350_state *st = iio_dev_get_devdata(indio_dev); + long val; + int ret; + u8 t; + + ret = strict_strtol(buf, 10, &val); + if (ret) + return ret; + + mutex_lock(&indio_dev->mlock); + + t = (1638 / val); + if (t > 0) + t--; + t &= ADIS16350_SMPL_PRD_DIV_MASK; + if ((t & ADIS16350_SMPL_PRD_DIV_MASK) >= 0x0A) + st->us->max_speed_hz = ADIS16350_SPI_SLOW; + else + st->us->max_speed_hz = ADIS16350_SPI_FAST; + + ret = adis16350_spi_write_reg_8(dev, + ADIS16350_SMPL_PRD, + t); + + mutex_unlock(&indio_dev->mlock); + + return ret ? ret : len; +} + +static int adis16350_reset(struct device *dev) +{ + int ret; + ret = adis16350_spi_write_reg_8(dev, + ADIS16350_GLOB_CMD, + ADIS16350_GLOB_CMD_SW_RESET); + if (ret) + dev_err(dev, "problem resetting device"); + + return ret; +} + +static ssize_t adis16350_write_reset(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + if (len < 1) + return -1; + switch (buf[0]) { + case '1': + case 'y': + case 'Y': + return adis16350_reset(dev); + } + return -1; +} + +int adis16350_set_irq(struct device *dev, bool enable) +{ + int ret; + u16 msc; + ret = adis16350_spi_read_reg_16(dev, ADIS16350_MSC_CTRL, &msc); + if (ret) + goto error_ret; + + msc |= ADIS16350_MSC_CTRL_DATA_RDY_POL_HIGH; + msc &= ~ADIS16350_MSC_CTRL_DATA_RDY_DIO2; + + if (enable) + msc |= ADIS16350_MSC_CTRL_DATA_RDY_EN; + else + msc &= ~ADIS16350_MSC_CTRL_DATA_RDY_EN; + + ret = adis16350_spi_write_reg_16(dev, ADIS16350_MSC_CTRL, msc); + if (ret) + goto error_ret; + +error_ret: + return ret; +} + +/* Power down the device */ +static int adis16350_stop_device(struct device *dev) +{ + int ret; + u16 val = ADIS16350_SLP_CNT_POWER_OFF; + + ret = adis16350_spi_write_reg_16(dev, ADIS16350_SLP_CNT, val); + if (ret) + dev_err(dev, "problem with turning device off: SLP_CNT"); + + return ret; +} + +static int adis16350_self_test(struct device *dev) +{ + int ret; + ret = adis16350_spi_write_reg_16(dev, + ADIS16350_MSC_CTRL, + ADIS16350_MSC_CTRL_MEM_TEST); + if (ret) { + dev_err(dev, "problem starting self test"); + goto err_ret; + } + + adis16350_check_status(dev); + +err_ret: + return ret; +} + +static int adis16350_check_status(struct device *dev) +{ + u16 status; + int ret; + + ret = adis16350_spi_read_reg_16(dev, ADIS16350_DIAG_STAT, &status); + + if (ret < 0) { + dev_err(dev, "Reading status failed\n"); + goto error_ret; + } + ret = status; + if (status & ADIS16350_DIAG_STAT_ZACCL_FAIL) + dev_err(dev, "Z-axis accelerometer self-test failure\n"); + if (status & ADIS16350_DIAG_STAT_YACCL_FAIL) + dev_err(dev, "Y-axis accelerometer self-test failure\n"); + if (status & ADIS16350_DIAG_STAT_XACCL_FAIL) + dev_err(dev, "X-axis accelerometer self-test failure\n"); + if (status & ADIS16350_DIAG_STAT_XGYRO_FAIL) + dev_err(dev, "X-axis gyroscope self-test failure\n"); + if (status & ADIS16350_DIAG_STAT_YGYRO_FAIL) + dev_err(dev, "Y-axis gyroscope self-test failure\n"); + if (status & ADIS16350_DIAG_STAT_ZGYRO_FAIL) + dev_err(dev, "Z-axis gyroscope self-test failure\n"); + if (status & ADIS16350_DIAG_STAT_ALARM2) + dev_err(dev, "Alarm 2 active\n"); + if (status & ADIS16350_DIAG_STAT_ALARM1) + dev_err(dev, "Alarm 1 active\n"); + if (status & ADIS16350_DIAG_STAT_FLASH_CHK) + dev_err(dev, "Flash checksum error\n"); + if (status & ADIS16350_DIAG_STAT_SELF_TEST) + dev_err(dev, "Self test error\n"); + if (status & ADIS16350_DIAG_STAT_OVERFLOW) + dev_err(dev, "Sensor overrange\n"); + if (status & ADIS16350_DIAG_STAT_SPI_FAIL) + dev_err(dev, "SPI failure\n"); + if (status & ADIS16350_DIAG_STAT_FLASH_UPT) + dev_err(dev, "Flash update failed\n"); + if (status & ADIS16350_DIAG_STAT_POWER_HIGH) + dev_err(dev, "Power supply above 5.25V\n"); + if (status & ADIS16350_DIAG_STAT_POWER_LOW) + dev_err(dev, "Power supply below 4.75V\n"); + +error_ret: + return ret; +} + +static int adis16350_initial_setup(struct adis16350_state *st) +{ + int ret; + u16 smp_prd; + struct device *dev = &st->indio_dev->dev; + + /* use low spi speed for init */ + st->us->max_speed_hz = ADIS16350_SPI_SLOW; + st->us->mode = SPI_MODE_3; + spi_setup(st->us); + + /* Disable IRQ */ + ret = adis16350_set_irq(dev, false); + if (ret) { + dev_err(dev, "disable irq failed"); + goto err_ret; + } + + /* Do self test */ + ret = adis16350_self_test(dev); + if (ret) { + dev_err(dev, "self test failure"); + goto err_ret; + } + + /* Read status register to check the result */ + ret = adis16350_check_status(dev); + if (ret) { + adis16350_reset(dev); + dev_err(dev, "device not playing ball -> reset"); + msleep(ADIS16350_STARTUP_DELAY); + ret = adis16350_check_status(dev); + if (ret) { + dev_err(dev, "giving up"); + goto err_ret; + } + } + + printk(KERN_INFO DRIVER_NAME ": at CS%d (irq %d)\n", + st->us->chip_select, st->us->irq); + + /* use high spi speed if possible */ + ret = adis16350_spi_read_reg_16(dev, ADIS16350_SMPL_PRD, &smp_prd); + if (!ret && (smp_prd & ADIS16350_SMPL_PRD_DIV_MASK) < 0x0A) { + st->us->max_speed_hz = ADIS16350_SPI_SLOW; + spi_setup(st->us); + } + +err_ret: + return ret; +} + +static IIO_DEV_ATTR_ACCEL_X_OFFSET(S_IWUSR | S_IRUGO, + adis16350_read_12bit_signed, + adis16350_write_16bit, + ADIS16350_XACCL_OFF); + +static IIO_DEV_ATTR_ACCEL_Y_OFFSET(S_IWUSR | S_IRUGO, + adis16350_read_12bit_signed, + adis16350_write_16bit, + ADIS16350_YACCL_OFF); + +static IIO_DEV_ATTR_ACCEL_Z_OFFSET(S_IWUSR | S_IRUGO, + adis16350_read_12bit_signed, + adis16350_write_16bit, + ADIS16350_ZACCL_OFF); + +static IIO_DEV_ATTR_IN_NAMED_RAW(supply, adis16350_read_12bit_unsigned, + ADIS16350_SUPPLY_OUT); +static IIO_CONST_ATTR(in_supply_scale, "0.002418"); + +static IIO_DEV_ATTR_GYRO_X(adis16350_read_14bit_signed, + ADIS16350_XGYRO_OUT); +static IIO_DEV_ATTR_GYRO_Y(adis16350_read_14bit_signed, + ADIS16350_YGYRO_OUT); +static IIO_DEV_ATTR_GYRO_Z(adis16350_read_14bit_signed, + ADIS16350_ZGYRO_OUT); +static IIO_CONST_ATTR(gyro_scale, "0.05"); + +static IIO_DEV_ATTR_ACCEL_X(adis16350_read_14bit_signed, + ADIS16350_XACCL_OUT); +static IIO_DEV_ATTR_ACCEL_Y(adis16350_read_14bit_signed, + ADIS16350_YACCL_OUT); +static IIO_DEV_ATTR_ACCEL_Z(adis16350_read_14bit_signed, + ADIS16350_ZACCL_OUT); +static IIO_CONST_ATTR(accel_scale, "0.00333"); + +static IIO_DEVICE_ATTR(temp_x_raw, S_IRUGO, adis16350_read_12bit_signed, + NULL, ADIS16350_XTEMP_OUT); +static IIO_DEVICE_ATTR(temp_y_raw, S_IRUGO, adis16350_read_12bit_signed, + NULL, ADIS16350_YTEMP_OUT); +static IIO_DEVICE_ATTR(temp_z_raw, S_IRUGO, adis16350_read_12bit_signed, + NULL, ADIS16350_ZTEMP_OUT); +static IIO_CONST_ATTR(temp_scale, "0.0005"); + +static IIO_DEV_ATTR_IN_RAW(0, adis16350_read_12bit_unsigned, + ADIS16350_AUX_ADC); +static IIO_CONST_ATTR(in0_scale, "0.000806"); + +static IIO_DEV_ATTR_SAMP_FREQ(S_IWUSR | S_IRUGO, + adis16350_read_frequency, + adis16350_write_frequency); + +static IIO_DEVICE_ATTR(reset, S_IWUSR, NULL, + adis16350_write_reset, 0); + +static IIO_CONST_ATTR_AVAIL_SAMP_FREQ("409 546 819 1638"); + +static IIO_CONST_ATTR(name, "adis16350"); + +static struct attribute *adis16350_attributes[] = { + &iio_dev_attr_accel_x_offset.dev_attr.attr, + &iio_dev_attr_accel_y_offset.dev_attr.attr, + &iio_dev_attr_accel_z_offset.dev_attr.attr, + &iio_dev_attr_in_supply_raw.dev_attr.attr, + &iio_const_attr_in_supply_scale.dev_attr.attr, + &iio_dev_attr_gyro_x_raw.dev_attr.attr, + &iio_dev_attr_gyro_y_raw.dev_attr.attr, + &iio_dev_attr_gyro_z_raw.dev_attr.attr, + &iio_const_attr_gyro_scale.dev_attr.attr, + &iio_dev_attr_accel_x_raw.dev_attr.attr, + &iio_dev_attr_accel_y_raw.dev_attr.attr, + &iio_dev_attr_accel_z_raw.dev_attr.attr, + &iio_const_attr_accel_scale.dev_attr.attr, + &iio_dev_attr_temp_x_raw.dev_attr.attr, + &iio_dev_attr_temp_y_raw.dev_attr.attr, + &iio_dev_attr_temp_z_raw.dev_attr.attr, + &iio_const_attr_temp_scale.dev_attr.attr, + &iio_dev_attr_in0_raw.dev_attr.attr, + &iio_const_attr_in0_scale.dev_attr.attr, + &iio_dev_attr_sampling_frequency.dev_attr.attr, + &iio_const_attr_available_sampling_frequency.dev_attr.attr, + &iio_dev_attr_reset.dev_attr.attr, + &iio_const_attr_name.dev_attr.attr, + NULL +}; + +static const struct attribute_group adis16350_attribute_group = { + .attrs = adis16350_attributes, +}; + +static struct attribute *adis16350_event_attributes[] = { + NULL, +}; + +static struct attribute_group adis16350_event_attribute_group = { + .attrs = adis16350_event_attributes, +}; + +static int __devinit adis16350_probe(struct spi_device *spi) +{ + int ret, regdone = 0; + struct adis16350_state *st = kzalloc(sizeof *st, GFP_KERNEL); + if (!st) { + ret = -ENOMEM; + goto error_ret; + } + /* this is only used for removal purposes */ + spi_set_drvdata(spi, st); + + /* Allocate the comms buffers */ + st->rx = kzalloc(sizeof(*st->rx)*ADIS16350_MAX_RX, GFP_KERNEL); + if (st->rx == NULL) { + ret = -ENOMEM; + goto error_free_st; + } + st->tx = kzalloc(sizeof(*st->tx)*ADIS16350_MAX_TX, GFP_KERNEL); + if (st->tx == NULL) { + ret = -ENOMEM; + goto error_free_rx; + } + st->us = spi; + mutex_init(&st->buf_lock); + /* setup the industrialio driver allocated elements */ + st->indio_dev = iio_allocate_device(); + if (st->indio_dev == NULL) { + ret = -ENOMEM; + goto error_free_tx; + } + + st->indio_dev->dev.parent = &spi->dev; + st->indio_dev->num_interrupt_lines = 1; + st->indio_dev->event_attrs = &adis16350_event_attribute_group; + st->indio_dev->attrs = &adis16350_attribute_group; + st->indio_dev->dev_data = (void *)(st); + st->indio_dev->driver_module = THIS_MODULE; + st->indio_dev->modes = INDIO_DIRECT_MODE; + + ret = adis16350_configure_ring(st->indio_dev); + if (ret) + goto error_free_dev; + + ret = iio_device_register(st->indio_dev); + if (ret) + goto error_unreg_ring_funcs; + regdone = 1; + + ret = adis16350_initialize_ring(st->indio_dev->ring); + if (ret) { + printk(KERN_ERR "failed to initialize the ring\n"); + goto error_unreg_ring_funcs; + } + + if (spi->irq) { + ret = iio_register_interrupt_line(spi->irq, + st->indio_dev, + 0, + IRQF_TRIGGER_RISING, + "adis16350"); + if (ret) + goto error_uninitialize_ring; + + ret = adis16350_probe_trigger(st->indio_dev); + if (ret) + goto error_unregister_line; + } + + /* Get the device into a sane initial state */ + ret = adis16350_initial_setup(st); + if (ret) + goto error_remove_trigger; + return 0; + +error_remove_trigger: + adis16350_remove_trigger(st->indio_dev); +error_unregister_line: + if (spi->irq) + iio_unregister_interrupt_line(st->indio_dev, 0); +error_uninitialize_ring: + adis16350_uninitialize_ring(st->indio_dev->ring); +error_unreg_ring_funcs: + adis16350_unconfigure_ring(st->indio_dev); +error_free_dev: + if (regdone) + iio_device_unregister(st->indio_dev); + else + iio_free_device(st->indio_dev); +error_free_tx: + kfree(st->tx); +error_free_rx: + kfree(st->rx); +error_free_st: + kfree(st); +error_ret: + return ret; +} + +static int adis16350_remove(struct spi_device *spi) +{ + int ret; + struct adis16350_state *st = spi_get_drvdata(spi); + struct iio_dev *indio_dev = st->indio_dev; + + ret = adis16350_stop_device(&(indio_dev->dev)); + if (ret) + goto err_ret; + + flush_scheduled_work(); + + adis16350_remove_trigger(indio_dev); + if (spi->irq) + iio_unregister_interrupt_line(indio_dev, 0); + + adis16350_uninitialize_ring(indio_dev->ring); + iio_device_unregister(indio_dev); + adis16350_unconfigure_ring(indio_dev); + kfree(st->tx); + kfree(st->rx); + kfree(st); + + return 0; + +err_ret: + return ret; +} + +static const struct spi_device_id adis16350_id[] = { + {"adis16350", 0}, + {"adis16354", 0}, + {"adis16355", 0}, + {"adis16360", 0}, + {"adis16362", 0}, + {"adis16364", 0}, + {"adis16365", 0}, + {} +}; + +static struct spi_driver adis16350_driver = { + .driver = { + .name = "adis16350", + .owner = THIS_MODULE, + }, + .probe = adis16350_probe, + .remove = __devexit_p(adis16350_remove), + .id_table = adis16350_id, +}; + +static __init int adis16350_init(void) +{ + return spi_register_driver(&adis16350_driver); +} +module_init(adis16350_init); + +static __exit void adis16350_exit(void) +{ + spi_unregister_driver(&adis16350_driver); +} +module_exit(adis16350_exit); + +MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>"); +MODULE_DESCRIPTION("Analog Devices ADIS16350/54/55/60/62/64/65 IMU SPI driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/iio/imu/adis16350_ring.c b/drivers/staging/iio/imu/adis16350_ring.c new file mode 100644 index 000000000000..5e9716ea7c77 --- /dev/null +++ b/drivers/staging/iio/imu/adis16350_ring.c @@ -0,0 +1,286 @@ +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/gpio.h> +#include <linux/workqueue.h> +#include <linux/mutex.h> +#include <linux/device.h> +#include <linux/kernel.h> +#include <linux/spi/spi.h> +#include <linux/sysfs.h> +#include <linux/list.h> + +#include "../iio.h" +#include "../sysfs.h" +#include "../ring_sw.h" +#include "../accel/accel.h" +#include "../trigger.h" +#include "adis16350.h" + +/** + * combine_8_to_16() utility function to munge to u8s into u16 + **/ +static inline u16 combine_8_to_16(u8 lower, u8 upper) +{ + u16 _lower = lower; + u16 _upper = upper; + return _lower | (_upper << 8); +} + +static IIO_SCAN_EL_C(supply, ADIS16350_SCAN_SUPPLY, IIO_UNSIGNED(12), + ADIS16350_SUPPLY_OUT, NULL); + +static IIO_SCAN_EL_C(gyro_x, ADIS16350_SCAN_GYRO_X, IIO_SIGNED(14), + ADIS16350_XGYRO_OUT, NULL); +static IIO_SCAN_EL_C(gyro_y, ADIS16350_SCAN_GYRO_Y, IIO_SIGNED(14), + ADIS16350_YGYRO_OUT, NULL); +static IIO_SCAN_EL_C(gyro_z, ADIS16350_SCAN_GYRO_Z, IIO_SIGNED(14), + ADIS16350_ZGYRO_OUT, NULL); + +static IIO_SCAN_EL_C(accel_x, ADIS16350_SCAN_ACC_X, IIO_SIGNED(14), + ADIS16350_XACCL_OUT, NULL); +static IIO_SCAN_EL_C(accel_y, ADIS16350_SCAN_ACC_Y, IIO_SIGNED(14), + ADIS16350_YACCL_OUT, NULL); +static IIO_SCAN_EL_C(accel_z, ADIS16350_SCAN_ACC_Z, IIO_SIGNED(14), + ADIS16350_ZACCL_OUT, NULL); + +static IIO_SCAN_EL_C(temp_x, ADIS16350_SCAN_TEMP_X, IIO_SIGNED(12), + ADIS16350_XTEMP_OUT, NULL); +static IIO_SCAN_EL_C(temp_y, ADIS16350_SCAN_TEMP_Y, IIO_SIGNED(12), + ADIS16350_YTEMP_OUT, NULL); +static IIO_SCAN_EL_C(temp_z, ADIS16350_SCAN_TEMP_Z, IIO_SIGNED(12), + ADIS16350_ZTEMP_OUT, NULL); + +static IIO_SCAN_EL_C(adc_0, ADIS16350_SCAN_ADC_0, IIO_UNSIGNED(12), + ADIS16350_AUX_ADC, NULL); + +static IIO_SCAN_EL_TIMESTAMP(11); + +static struct attribute *adis16350_scan_el_attrs[] = { + &iio_scan_el_supply.dev_attr.attr, + &iio_scan_el_gyro_x.dev_attr.attr, + &iio_scan_el_gyro_y.dev_attr.attr, + &iio_scan_el_gyro_z.dev_attr.attr, + &iio_scan_el_accel_x.dev_attr.attr, + &iio_scan_el_accel_y.dev_attr.attr, + &iio_scan_el_accel_z.dev_attr.attr, + &iio_scan_el_temp_x.dev_attr.attr, + &iio_scan_el_temp_y.dev_attr.attr, + &iio_scan_el_temp_z.dev_attr.attr, + &iio_scan_el_adc_0.dev_attr.attr, + &iio_scan_el_timestamp.dev_attr.attr, + NULL, +}; + +static struct attribute_group adis16350_scan_el_group = { + .attrs = adis16350_scan_el_attrs, + .name = "scan_elements", +}; + +/** + * adis16350_poll_func_th() top half interrupt handler called by trigger + * @private_data: iio_dev + **/ +static void adis16350_poll_func_th(struct iio_dev *indio_dev) +{ + struct adis16350_state *st = iio_dev_get_devdata(indio_dev); + st->last_timestamp = indio_dev->trig->timestamp; + schedule_work(&st->work_trigger_to_ring); +} + +/** + * adis16350_spi_read_burst() - read all data registers + * @dev: device associated with child of actual device (iio_dev or iio_trig) + * @rx: somewhere to pass back the value read (min size is 24 bytes) + **/ +static int adis16350_spi_read_burst(struct device *dev, u8 *rx) +{ + struct spi_message msg; + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct adis16350_state *st = iio_dev_get_devdata(indio_dev); + u32 old_speed_hz = st->us->max_speed_hz; + int ret; + + struct spi_transfer xfers[] = { + { + .tx_buf = st->tx, + .bits_per_word = 8, + .len = 2, + .cs_change = 0, + }, { + .rx_buf = rx, + .bits_per_word = 8, + .len = 22, + .cs_change = 0, + }, + }; + + mutex_lock(&st->buf_lock); + st->tx[0] = ADIS16350_READ_REG(ADIS16350_GLOB_CMD); + st->tx[1] = 0; + + spi_message_init(&msg); + spi_message_add_tail(&xfers[0], &msg); + spi_message_add_tail(&xfers[1], &msg); + + st->us->max_speed_hz = ADIS16350_SPI_BURST; + spi_setup(st->us); + + ret = spi_sync(st->us, &msg); + if (ret) + dev_err(&st->us->dev, "problem when burst reading"); + + st->us->max_speed_hz = old_speed_hz; + spi_setup(st->us); + mutex_unlock(&st->buf_lock); + return ret; +} + +/* Whilst this makes a lot of calls to iio_sw_ring functions - it is to device + * specific to be rolled into the core. + */ +static void adis16350_trigger_bh_to_ring(struct work_struct *work_s) +{ + struct adis16350_state *st + = container_of(work_s, struct adis16350_state, + work_trigger_to_ring); + + int i = 0; + s16 *data; + size_t datasize = st->indio_dev + ->ring->access.get_bpd(st->indio_dev->ring); + + data = kmalloc(datasize , GFP_KERNEL); + if (data == NULL) { + dev_err(&st->us->dev, "memory alloc failed in ring bh"); + return; + } + + if (st->indio_dev->scan_count) + if (adis16350_spi_read_burst(&st->indio_dev->dev, st->rx) >= 0) + for (; i < st->indio_dev->scan_count; i++) { + data[i] = combine_8_to_16(st->rx[i*2+1], + st->rx[i*2]); + } + + /* Guaranteed to be aligned with 8 byte boundary */ + if (st->indio_dev->scan_timestamp) + *((s64 *)(data + ((i + 3)/4)*4)) = st->last_timestamp; + + st->indio_dev->ring->access.store_to(st->indio_dev->ring, + (u8 *)data, + st->last_timestamp); + + iio_trigger_notify_done(st->indio_dev->trig); + kfree(data); + + return; +} + +static int adis16350_data_rdy_ring_preenable(struct iio_dev *indio_dev) +{ + size_t size; + dev_dbg(&indio_dev->dev, "%s\n", __func__); + /* Check if there are any scan elements enabled, if not fail*/ + if (!(indio_dev->scan_count || indio_dev->scan_timestamp)) + return -EINVAL; + + if (indio_dev->ring->access.set_bpd) { + if (indio_dev->scan_timestamp) + if (indio_dev->scan_count) + /* Timestamp (aligned sizeof(s64) and data */ + size = (((indio_dev->scan_count * sizeof(s16)) + + sizeof(s64) - 1) + & ~(sizeof(s64) - 1)) + + sizeof(s64); + else /* Timestamp only */ + size = sizeof(s64); + else /* Data only */ + size = indio_dev->scan_count*sizeof(s16); + indio_dev->ring->access.set_bpd(indio_dev->ring, size); + } + + return 0; +} + +static int adis16350_data_rdy_ring_postenable(struct iio_dev *indio_dev) +{ + return indio_dev->trig + ? iio_trigger_attach_poll_func(indio_dev->trig, + indio_dev->pollfunc) + : 0; +} + +static int adis16350_data_rdy_ring_predisable(struct iio_dev *indio_dev) +{ + return indio_dev->trig + ? iio_trigger_dettach_poll_func(indio_dev->trig, + indio_dev->pollfunc) + : 0; +} + +void adis16350_unconfigure_ring(struct iio_dev *indio_dev) +{ + kfree(indio_dev->pollfunc); + iio_sw_rb_free(indio_dev->ring); +} + +int adis16350_configure_ring(struct iio_dev *indio_dev) +{ + int ret = 0; + struct adis16350_state *st = indio_dev->dev_data; + struct iio_ring_buffer *ring; + INIT_WORK(&st->work_trigger_to_ring, adis16350_trigger_bh_to_ring); + /* Set default scan mode */ + + iio_scan_mask_set(indio_dev, iio_scan_el_supply.number); + iio_scan_mask_set(indio_dev, iio_scan_el_gyro_x.number); + iio_scan_mask_set(indio_dev, iio_scan_el_gyro_y.number); + iio_scan_mask_set(indio_dev, iio_scan_el_gyro_z.number); + iio_scan_mask_set(indio_dev, iio_scan_el_accel_x.number); + iio_scan_mask_set(indio_dev, iio_scan_el_accel_y.number); + iio_scan_mask_set(indio_dev, iio_scan_el_accel_z.number); + iio_scan_mask_set(indio_dev, iio_scan_el_temp_x.number); + iio_scan_mask_set(indio_dev, iio_scan_el_temp_y.number); + iio_scan_mask_set(indio_dev, iio_scan_el_temp_z.number); + iio_scan_mask_set(indio_dev, iio_scan_el_adc_0.number); + indio_dev->scan_timestamp = true; + + indio_dev->scan_el_attrs = &adis16350_scan_el_group; + + ring = iio_sw_rb_allocate(indio_dev); + if (!ring) { + ret = -ENOMEM; + return ret; + } + indio_dev->ring = ring; + /* Effectively select the ring buffer implementation */ + iio_ring_sw_register_funcs(&ring->access); + ring->preenable = &adis16350_data_rdy_ring_preenable; + ring->postenable = &adis16350_data_rdy_ring_postenable; + ring->predisable = &adis16350_data_rdy_ring_predisable; + ring->owner = THIS_MODULE; + + indio_dev->pollfunc = kzalloc(sizeof(*indio_dev->pollfunc), GFP_KERNEL); + if (indio_dev->pollfunc == NULL) { + ret = -ENOMEM; + goto error_iio_sw_rb_free;; + } + indio_dev->pollfunc->poll_func_main = &adis16350_poll_func_th; + indio_dev->pollfunc->private_data = indio_dev; + indio_dev->modes |= INDIO_RING_TRIGGERED; + return 0; + +error_iio_sw_rb_free: + iio_sw_rb_free(indio_dev->ring); + return ret; +} + +int adis16350_initialize_ring(struct iio_ring_buffer *ring) +{ + return iio_ring_buffer_register(ring, 0); +} + +void adis16350_uninitialize_ring(struct iio_ring_buffer *ring) +{ + iio_ring_buffer_unregister(ring); +} diff --git a/drivers/staging/iio/imu/adis16350_trigger.c b/drivers/staging/iio/imu/adis16350_trigger.c new file mode 100644 index 000000000000..1ffa75d05fac --- /dev/null +++ b/drivers/staging/iio/imu/adis16350_trigger.c @@ -0,0 +1,127 @@ +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/mutex.h> +#include <linux/device.h> +#include <linux/kernel.h> +#include <linux/sysfs.h> +#include <linux/list.h> +#include <linux/spi/spi.h> + +#include "../iio.h" +#include "../sysfs.h" +#include "../trigger.h" +#include "adis16350.h" + +/** + * adis16350_data_rdy_trig_poll() the event handler for the data rdy trig + **/ +static int adis16350_data_rdy_trig_poll(struct iio_dev *dev_info, + int index, + s64 timestamp, + int no_test) +{ + struct adis16350_state *st = iio_dev_get_devdata(dev_info); + struct iio_trigger *trig = st->trig; + + trig->timestamp = timestamp; + iio_trigger_poll(trig); + + return IRQ_HANDLED; +} + +IIO_EVENT_SH(data_rdy_trig, &adis16350_data_rdy_trig_poll); + +static DEVICE_ATTR(name, S_IRUGO, iio_trigger_read_name, NULL); + +static struct attribute *adis16350_trigger_attrs[] = { + &dev_attr_name.attr, + NULL, +}; + +static const struct attribute_group adis16350_trigger_attr_group = { + .attrs = adis16350_trigger_attrs, +}; + +/** + * adis16350_data_rdy_trigger_set_state() set datardy interrupt state + **/ +static int adis16350_data_rdy_trigger_set_state(struct iio_trigger *trig, + bool state) +{ + struct adis16350_state *st = trig->private_data; + struct iio_dev *indio_dev = st->indio_dev; + int ret = 0; + + dev_dbg(&indio_dev->dev, "%s (%d)\n", __func__, state); + ret = adis16350_set_irq(&st->indio_dev->dev, state); + if (state == false) { + iio_remove_event_from_list(&iio_event_data_rdy_trig, + &indio_dev->interrupts[0] + ->ev_list); + /* possible quirk with handler currently worked around + by ensuring the work queue is empty */ + flush_scheduled_work(); + } else { + iio_add_event_to_list(&iio_event_data_rdy_trig, + &indio_dev->interrupts[0]->ev_list); + } + return ret; +} + +/** + * adis16350_trig_try_reen() try renabling irq for data rdy trigger + * @trig: the datardy trigger + **/ +static int adis16350_trig_try_reen(struct iio_trigger *trig) +{ + struct adis16350_state *st = trig->private_data; + enable_irq(st->us->irq); + /* irq reenabled so success! */ + return 0; +} + +int adis16350_probe_trigger(struct iio_dev *indio_dev) +{ + int ret; + struct adis16350_state *st = indio_dev->dev_data; + + st->trig = iio_allocate_trigger(); + st->trig->name = kmalloc(IIO_TRIGGER_NAME_LENGTH, GFP_KERNEL); + if (!st->trig->name) { + ret = -ENOMEM; + goto error_free_trig; + } + snprintf((char *)st->trig->name, + IIO_TRIGGER_NAME_LENGTH, + "adis16350-dev%d", indio_dev->id); + st->trig->dev.parent = &st->us->dev; + st->trig->owner = THIS_MODULE; + st->trig->private_data = st; + st->trig->set_trigger_state = &adis16350_data_rdy_trigger_set_state; + st->trig->try_reenable = &adis16350_trig_try_reen; + st->trig->control_attrs = &adis16350_trigger_attr_group; + ret = iio_trigger_register(st->trig); + + /* select default trigger */ + indio_dev->trig = st->trig; + if (ret) + goto error_free_trig_name; + + return 0; + +error_free_trig_name: + kfree(st->trig->name); +error_free_trig: + iio_free_trigger(st->trig); + + return ret; +} + +void adis16350_remove_trigger(struct iio_dev *indio_dev) +{ + struct adis16350_state *state = indio_dev->dev_data; + + iio_trigger_unregister(state->trig); + kfree(state->trig->name); + iio_free_trigger(state->trig); +} diff --git a/drivers/staging/iio/imu/adis16400.h b/drivers/staging/iio/imu/adis16400.h new file mode 100644 index 000000000000..5a69a7ab91ce --- /dev/null +++ b/drivers/staging/iio/imu/adis16400.h @@ -0,0 +1,229 @@ +/* + * adis16400.h support Analog Devices ADIS16400 + * 3d 18g accelerometers, + * 3d gyroscopes, + * 3d 2.5gauss magnetometers via SPI + * + * Copyright (c) 2009 Manuel Stahl <manuel.stahl@iis.fraunhofer.de> + * Copyright (c) 2007 Jonathan Cameron <jic23@cam.ac.uk> + * + * Loosely based upon lis3l02dq.h + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef SPI_ADIS16400_H_ +#define SPI_ADIS16400_H_ + +#define ADIS16400_STARTUP_DELAY 220 /* ms */ + +#define ADIS16400_READ_REG(a) a +#define ADIS16400_WRITE_REG(a) ((a) | 0x80) + +#define ADIS16400_FLASH_CNT 0x00 /* Flash memory write count */ +#define ADIS16400_SUPPLY_OUT 0x02 /* Power supply measurement */ +#define ADIS16400_XGYRO_OUT 0x04 /* X-axis gyroscope output */ +#define ADIS16400_YGYRO_OUT 0x06 /* Y-axis gyroscope output */ +#define ADIS16400_ZGYRO_OUT 0x08 /* Z-axis gyroscope output */ +#define ADIS16400_XACCL_OUT 0x0A /* X-axis accelerometer output */ +#define ADIS16400_YACCL_OUT 0x0C /* Y-axis accelerometer output */ +#define ADIS16400_ZACCL_OUT 0x0E /* Z-axis accelerometer output */ +#define ADIS16400_XMAGN_OUT 0x10 /* X-axis magnetometer measurement */ +#define ADIS16400_YMAGN_OUT 0x12 /* Y-axis magnetometer measurement */ +#define ADIS16400_ZMAGN_OUT 0x14 /* Z-axis magnetometer measurement */ +#define ADIS16400_TEMP_OUT 0x16 /* Temperature output */ +#define ADIS16400_AUX_ADC 0x18 /* Auxiliary ADC measurement */ + +/* Calibration parameters */ +#define ADIS16400_XGYRO_OFF 0x1A /* X-axis gyroscope bias offset factor */ +#define ADIS16400_YGYRO_OFF 0x1C /* Y-axis gyroscope bias offset factor */ +#define ADIS16400_ZGYRO_OFF 0x1E /* Z-axis gyroscope bias offset factor */ +#define ADIS16400_XACCL_OFF 0x20 /* X-axis acceleration bias offset factor */ +#define ADIS16400_YACCL_OFF 0x22 /* Y-axis acceleration bias offset factor */ +#define ADIS16400_ZACCL_OFF 0x24 /* Z-axis acceleration bias offset factor */ +#define ADIS16400_XMAGN_HIF 0x26 /* X-axis magnetometer, hard-iron factor */ +#define ADIS16400_YMAGN_HIF 0x28 /* Y-axis magnetometer, hard-iron factor */ +#define ADIS16400_ZMAGN_HIF 0x2A /* Z-axis magnetometer, hard-iron factor */ +#define ADIS16400_XMAGN_SIF 0x2C /* X-axis magnetometer, soft-iron factor */ +#define ADIS16400_YMAGN_SIF 0x2E /* Y-axis magnetometer, soft-iron factor */ +#define ADIS16400_ZMAGN_SIF 0x30 /* Z-axis magnetometer, soft-iron factor */ + +#define ADIS16400_GPIO_CTRL 0x32 /* Auxiliary digital input/output control */ +#define ADIS16400_MSC_CTRL 0x34 /* Miscellaneous control */ +#define ADIS16400_SMPL_PRD 0x36 /* Internal sample period (rate) control */ +#define ADIS16400_SENS_AVG 0x38 /* Dynamic range and digital filter control */ +#define ADIS16400_SLP_CNT 0x3A /* Sleep mode control */ +#define ADIS16400_DIAG_STAT 0x3C /* System status */ + +/* Alarm functions */ +#define ADIS16400_GLOB_CMD 0x3E /* System command */ +#define ADIS16400_ALM_MAG1 0x40 /* Alarm 1 amplitude threshold */ +#define ADIS16400_ALM_MAG2 0x42 /* Alarm 2 amplitude threshold */ +#define ADIS16400_ALM_SMPL1 0x44 /* Alarm 1 sample size */ +#define ADIS16400_ALM_SMPL2 0x46 /* Alarm 2 sample size */ +#define ADIS16400_ALM_CTRL 0x48 /* Alarm control */ +#define ADIS16400_AUX_DAC 0x4A /* Auxiliary DAC data */ + +#define ADIS16400_PRODUCT_ID 0x56 /* Product identifier */ +#define ADIS16400_PRODUCT_ID_DEFAULT 0x4015 /* Datasheet says 0x4105, I get 0x4015 */ + +#define ADIS16400_ERROR_ACTIVE (1<<14) +#define ADIS16400_NEW_DATA (1<<14) + +/* MSC_CTRL */ +#define ADIS16400_MSC_CTRL_MEM_TEST (1<<11) +#define ADIS16400_MSC_CTRL_INT_SELF_TEST (1<<10) +#define ADIS16400_MSC_CTRL_NEG_SELF_TEST (1<<9) +#define ADIS16400_MSC_CTRL_POS_SELF_TEST (1<<8) +#define ADIS16400_MSC_CTRL_GYRO_BIAS (1<<7) +#define ADIS16400_MSC_CTRL_ACCL_ALIGN (1<<6) +#define ADIS16400_MSC_CTRL_DATA_RDY_EN (1<<2) +#define ADIS16400_MSC_CTRL_DATA_RDY_POL_HIGH (1<<1) +#define ADIS16400_MSC_CTRL_DATA_RDY_DIO2 (1<<0) + +/* SMPL_PRD */ +#define ADIS16400_SMPL_PRD_TIME_BASE (1<<7) +#define ADIS16400_SMPL_PRD_DIV_MASK 0x7F + +/* DIAG_STAT */ +#define ADIS16400_DIAG_STAT_ZACCL_FAIL (1<<15) +#define ADIS16400_DIAG_STAT_YACCL_FAIL (1<<14) +#define ADIS16400_DIAG_STAT_XACCL_FAIL (1<<13) +#define ADIS16400_DIAG_STAT_XGYRO_FAIL (1<<12) +#define ADIS16400_DIAG_STAT_YGYRO_FAIL (1<<11) +#define ADIS16400_DIAG_STAT_ZGYRO_FAIL (1<<10) +#define ADIS16400_DIAG_STAT_ALARM2 (1<<9) +#define ADIS16400_DIAG_STAT_ALARM1 (1<<8) +#define ADIS16400_DIAG_STAT_FLASH_CHK (1<<6) +#define ADIS16400_DIAG_STAT_SELF_TEST (1<<5) +#define ADIS16400_DIAG_STAT_OVERFLOW (1<<4) +#define ADIS16400_DIAG_STAT_SPI_FAIL (1<<3) +#define ADIS16400_DIAG_STAT_FLASH_UPT (1<<2) +#define ADIS16400_DIAG_STAT_POWER_HIGH (1<<1) +#define ADIS16400_DIAG_STAT_POWER_LOW (1<<0) + +/* GLOB_CMD */ +#define ADIS16400_GLOB_CMD_SW_RESET (1<<7) +#define ADIS16400_GLOB_CMD_P_AUTO_NULL (1<<4) +#define ADIS16400_GLOB_CMD_FLASH_UPD (1<<3) +#define ADIS16400_GLOB_CMD_DAC_LATCH (1<<2) +#define ADIS16400_GLOB_CMD_FAC_CALIB (1<<1) +#define ADIS16400_GLOB_CMD_AUTO_NULL (1<<0) + +/* SLP_CNT */ +#define ADIS16400_SLP_CNT_POWER_OFF (1<<8) + +#define ADIS16400_MAX_TX 24 +#define ADIS16400_MAX_RX 24 + +#define ADIS16400_SPI_SLOW (u32)(300 * 1000) +#define ADIS16400_SPI_BURST (u32)(1000 * 1000) +#define ADIS16400_SPI_FAST (u32)(2000 * 1000) + +/** + * struct adis16400_state - device instance specific data + * @us: actual spi_device + * @work_trigger_to_ring: bh for triggered event handling + * @work_cont_thresh: CLEAN + * @inter: used to check if new interrupt has been triggered + * @last_timestamp: passing timestamp from th to bh of interrupt handler + * @indio_dev: industrial I/O device structure + * @trig: data ready trigger registered with iio + * @tx: transmit buffer + * @rx: recieve buffer + * @buf_lock: mutex to protect tx and rx + **/ +struct adis16400_state { + struct spi_device *us; + struct work_struct work_trigger_to_ring; + struct iio_work_cont work_cont_thresh; + s64 last_timestamp; + struct iio_dev *indio_dev; + struct iio_trigger *trig; + u8 *tx; + u8 *rx; + struct mutex buf_lock; +}; + +int adis16400_spi_read_burst(struct device *dev, u8 *rx); + +int adis16400_set_irq(struct device *dev, bool enable); + +int adis16400_reset(struct device *dev); + +int adis16400_check_status(struct device *dev); + +#ifdef CONFIG_IIO_RING_BUFFER +/* At the moment triggers are only used for ring buffer + * filling. This may change! + */ + +enum adis16400_scan { + ADIS16400_SCAN_SUPPLY, + ADIS16400_SCAN_GYRO_X, + ADIS16400_SCAN_GYRO_Y, + ADIS16400_SCAN_GYRO_Z, + ADIS16400_SCAN_ACC_X, + ADIS16400_SCAN_ACC_Y, + ADIS16400_SCAN_ACC_Z, + ADIS16400_SCAN_MAGN_X, + ADIS16400_SCAN_MAGN_Y, + ADIS16400_SCAN_MAGN_Z, + ADIS16400_SCAN_TEMP, + ADIS16400_SCAN_ADC_0 +}; + +void adis16400_remove_trigger(struct iio_dev *indio_dev); +int adis16400_probe_trigger(struct iio_dev *indio_dev); + +ssize_t adis16400_read_data_from_ring(struct device *dev, + struct device_attribute *attr, + char *buf); + + +int adis16400_configure_ring(struct iio_dev *indio_dev); +void adis16400_unconfigure_ring(struct iio_dev *indio_dev); + +int adis16400_initialize_ring(struct iio_ring_buffer *ring); +void adis16400_uninitialize_ring(struct iio_ring_buffer *ring); +#else /* CONFIG_IIO_RING_BUFFER */ + +static inline void adis16400_remove_trigger(struct iio_dev *indio_dev) +{ +} + +static inline int adis16400_probe_trigger(struct iio_dev *indio_dev) +{ + return 0; +} + +static inline ssize_t +adis16400_read_data_from_ring(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return 0; +} + +static int adis16400_configure_ring(struct iio_dev *indio_dev) +{ + return 0; +} + +static inline void adis16400_unconfigure_ring(struct iio_dev *indio_dev) +{ +} + +static inline int adis16400_initialize_ring(struct iio_ring_buffer *ring) +{ + return 0; +} + +static inline void adis16400_uninitialize_ring(struct iio_ring_buffer *ring) +{ +} + +#endif /* CONFIG_IIO_RING_BUFFER */ +#endif /* SPI_ADIS16400_H_ */ diff --git a/drivers/staging/iio/imu/adis16400_core.c b/drivers/staging/iio/imu/adis16400_core.c new file mode 100644 index 000000000000..e69e2ce47da3 --- /dev/null +++ b/drivers/staging/iio/imu/adis16400_core.c @@ -0,0 +1,800 @@ +/* + * adis16400.c support Analog Devices ADIS16400/5 + * 3d 2g Linear Accelerometers, + * 3d Gyroscopes, + * 3d Magnetometers via SPI + * + * Copyright (c) 2009 Manuel Stahl <manuel.stahl@iis.fraunhofer.de> + * Copyright (c) 2007 Jonathan Cameron <jic23@cam.ac.uk> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/gpio.h> +#include <linux/delay.h> +#include <linux/mutex.h> +#include <linux/device.h> +#include <linux/kernel.h> +#include <linux/spi/spi.h> + +#include <linux/sysfs.h> +#include <linux/list.h> + +#include "../iio.h" +#include "../sysfs.h" +#include "../accel/accel.h" +#include "../adc/adc.h" +#include "../gyro/gyro.h" +#include "../magnetometer/magnet.h" + +#include "adis16400.h" + +#define DRIVER_NAME "adis16400" + +/* At the moment the spi framework doesn't allow global setting of cs_change. + * It's in the likely to be added comment at the top of spi.h. + * This means that use cannot be made of spi_write etc. + */ + +/** + * adis16400_spi_write_reg_8() - write single byte to a register + * @dev: device associated with child of actual device (iio_dev or iio_trig) + * @reg_address: the address of the register to be written + * @val: the value to write + **/ +static int adis16400_spi_write_reg_8(struct device *dev, + u8 reg_address, + u8 val) +{ + int ret; + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct adis16400_state *st = iio_dev_get_devdata(indio_dev); + + mutex_lock(&st->buf_lock); + st->tx[0] = ADIS16400_WRITE_REG(reg_address); + st->tx[1] = val; + + ret = spi_write(st->us, st->tx, 2); + mutex_unlock(&st->buf_lock); + + return ret; +} + +/** + * adis16400_spi_write_reg_16() - write 2 bytes to a pair of registers + * @dev: device associated with child of actual device (iio_dev or iio_trig) + * @reg_address: the address of the lower of the two registers. Second register + * is assumed to have address one greater. + * @val: value to be written + **/ +static int adis16400_spi_write_reg_16(struct device *dev, + u8 lower_reg_address, + u16 value) +{ + int ret; + struct spi_message msg; + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct adis16400_state *st = iio_dev_get_devdata(indio_dev); + struct spi_transfer xfers[] = { + { + .tx_buf = st->tx, + .bits_per_word = 8, + .len = 2, + .cs_change = 1, + }, { + .tx_buf = st->tx + 2, + .bits_per_word = 8, + .len = 2, + .cs_change = 1, + }, + }; + + mutex_lock(&st->buf_lock); + st->tx[0] = ADIS16400_WRITE_REG(lower_reg_address); + st->tx[1] = value & 0xFF; + st->tx[2] = ADIS16400_WRITE_REG(lower_reg_address + 1); + st->tx[3] = (value >> 8) & 0xFF; + + spi_message_init(&msg); + spi_message_add_tail(&xfers[0], &msg); + spi_message_add_tail(&xfers[1], &msg); + ret = spi_sync(st->us, &msg); + mutex_unlock(&st->buf_lock); + + return ret; +} + +/** + * adis16400_spi_read_reg_16() - read 2 bytes from a 16-bit register + * @dev: device associated with child of actual device (iio_dev or iio_trig) + * @reg_address: the address of the lower of the two registers. Second register + * is assumed to have address one greater. + * @val: somewhere to pass back the value read + **/ +static int adis16400_spi_read_reg_16(struct device *dev, + u8 lower_reg_address, + u16 *val) +{ + struct spi_message msg; + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct adis16400_state *st = iio_dev_get_devdata(indio_dev); + int ret; + struct spi_transfer xfers[] = { + { + .tx_buf = st->tx, + .bits_per_word = 8, + .len = 2, + .cs_change = 1, + }, { + .rx_buf = st->rx, + .bits_per_word = 8, + .len = 2, + .cs_change = 1, + }, + }; + + mutex_lock(&st->buf_lock); + st->tx[0] = ADIS16400_READ_REG(lower_reg_address); + st->tx[1] = 0; + st->tx[2] = 0; + st->tx[3] = 0; + + spi_message_init(&msg); + spi_message_add_tail(&xfers[0], &msg); + spi_message_add_tail(&xfers[1], &msg); + ret = spi_sync(st->us, &msg); + if (ret) { + dev_err(&st->us->dev, + "problem when reading 16 bit register 0x%02X", + lower_reg_address); + goto error_ret; + } + *val = (st->rx[0] << 8) | st->rx[1]; + +error_ret: + mutex_unlock(&st->buf_lock); + return ret; +} + +/** + * adis16400_spi_read_burst() - read all data registers + * @dev: device associated with child of actual device (iio_dev or iio_trig) + * @rx: somewhere to pass back the value read (min size is 24 bytes) + **/ +int adis16400_spi_read_burst(struct device *dev, u8 *rx) +{ + struct spi_message msg; + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct adis16400_state *st = iio_dev_get_devdata(indio_dev); + u32 old_speed_hz = st->us->max_speed_hz; + int ret; + + struct spi_transfer xfers[] = { + { + .tx_buf = st->tx, + .bits_per_word = 8, + .len = 2, + .cs_change = 0, + }, { + .rx_buf = rx, + .bits_per_word = 8, + .len = 24, + .cs_change = 1, + }, + }; + + mutex_lock(&st->buf_lock); + st->tx[0] = ADIS16400_READ_REG(ADIS16400_GLOB_CMD); + st->tx[1] = 0; + + spi_message_init(&msg); + spi_message_add_tail(&xfers[0], &msg); + spi_message_add_tail(&xfers[1], &msg); + + st->us->max_speed_hz = min(ADIS16400_SPI_BURST, old_speed_hz); + spi_setup(st->us); + + ret = spi_sync(st->us, &msg); + if (ret) + dev_err(&st->us->dev, "problem when burst reading"); + + st->us->max_speed_hz = old_speed_hz; + spi_setup(st->us); + mutex_unlock(&st->buf_lock); + return ret; +} + +static ssize_t adis16400_spi_read_signed(struct device *dev, + struct device_attribute *attr, + char *buf, + unsigned bits) +{ + int ret; + s16 val = 0; + unsigned shift = 16 - bits; + struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); + + ret = adis16400_spi_read_reg_16(dev, this_attr->address, (u16 *)&val); + if (ret) + return ret; + + if (val & ADIS16400_ERROR_ACTIVE) + adis16400_check_status(dev); + val = ((s16)(val << shift) >> shift); + return sprintf(buf, "%d\n", val); +} + +static ssize_t adis16400_read_12bit_unsigned(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int ret; + u16 val = 0; + struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); + + ret = adis16400_spi_read_reg_16(dev, this_attr->address, &val); + if (ret) + return ret; + + if (val & ADIS16400_ERROR_ACTIVE) + adis16400_check_status(dev); + + return sprintf(buf, "%u\n", val & 0x0FFF); +} + +static ssize_t adis16400_read_14bit_signed(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + ssize_t ret; + + /* Take the iio_dev status lock */ + mutex_lock(&indio_dev->mlock); + ret = adis16400_spi_read_signed(dev, attr, buf, 14); + mutex_unlock(&indio_dev->mlock); + + return ret; +} + +static ssize_t adis16400_read_12bit_signed(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + ssize_t ret; + + /* Take the iio_dev status lock */ + mutex_lock(&indio_dev->mlock); + ret = adis16400_spi_read_signed(dev, attr, buf, 12); + mutex_unlock(&indio_dev->mlock); + + return ret; +} + + +static ssize_t adis16400_write_16bit(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t len) +{ + struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); + int ret; + long val; + + ret = strict_strtol(buf, 10, &val); + if (ret) + goto error_ret; + ret = adis16400_spi_write_reg_16(dev, this_attr->address, val); + +error_ret: + return ret ? ret : len; +} + +static ssize_t adis16400_read_frequency(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int ret, len = 0; + u16 t; + int sps; + ret = adis16400_spi_read_reg_16(dev, + ADIS16400_SMPL_PRD, + &t); + if (ret) + return ret; + sps = (t & ADIS16400_SMPL_PRD_TIME_BASE) ? 53 : 1638; + sps /= (t & ADIS16400_SMPL_PRD_DIV_MASK) + 1; + len = sprintf(buf, "%d SPS\n", sps); + return len; +} + +static ssize_t adis16400_write_frequency(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t len) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct adis16400_state *st = iio_dev_get_devdata(indio_dev); + long val; + int ret; + u8 t; + + ret = strict_strtol(buf, 10, &val); + if (ret) + return ret; + + mutex_lock(&indio_dev->mlock); + + t = (1638 / val); + if (t > 0) + t--; + t &= ADIS16400_SMPL_PRD_DIV_MASK; + if ((t & ADIS16400_SMPL_PRD_DIV_MASK) >= 0x0A) + st->us->max_speed_hz = ADIS16400_SPI_SLOW; + else + st->us->max_speed_hz = ADIS16400_SPI_FAST; + + ret = adis16400_spi_write_reg_8(dev, + ADIS16400_SMPL_PRD, + t); + + mutex_unlock(&indio_dev->mlock); + + return ret ? ret : len; +} + +static ssize_t adis16400_write_reset(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + if (len < 1) + return -1; + switch (buf[0]) { + case '1': + case 'y': + case 'Y': + return adis16400_reset(dev); + } + return -1; +} + + + +int adis16400_set_irq(struct device *dev, bool enable) +{ + int ret; + u16 msc; + ret = adis16400_spi_read_reg_16(dev, ADIS16400_MSC_CTRL, &msc); + if (ret) + goto error_ret; + + msc |= ADIS16400_MSC_CTRL_DATA_RDY_POL_HIGH; + if (enable) + msc |= ADIS16400_MSC_CTRL_DATA_RDY_EN; + else + msc &= ~ADIS16400_MSC_CTRL_DATA_RDY_EN; + + ret = adis16400_spi_write_reg_16(dev, ADIS16400_MSC_CTRL, msc); + if (ret) + goto error_ret; + +error_ret: + return ret; +} + +int adis16400_reset(struct device *dev) +{ + int ret; + ret = adis16400_spi_write_reg_8(dev, + ADIS16400_GLOB_CMD, + ADIS16400_GLOB_CMD_SW_RESET); + if (ret) + dev_err(dev, "problem resetting device"); + + return ret; +} + +/* Power down the device */ +static int adis16400_stop_device(struct device *dev) +{ + int ret; + u16 val = ADIS16400_SLP_CNT_POWER_OFF; + + ret = adis16400_spi_write_reg_16(dev, ADIS16400_SLP_CNT, val); + if (ret) + dev_err(dev, "problem with turning device off: SLP_CNT"); + + return ret; +} + +static int adis16400_self_test(struct device *dev) +{ + int ret; + ret = adis16400_spi_write_reg_16(dev, + ADIS16400_MSC_CTRL, + ADIS16400_MSC_CTRL_MEM_TEST); + if (ret) { + dev_err(dev, "problem starting self test"); + goto err_ret; + } + + adis16400_check_status(dev); + +err_ret: + return ret; +} + +int adis16400_check_status(struct device *dev) +{ + u16 status; + int ret; + + ret = adis16400_spi_read_reg_16(dev, ADIS16400_DIAG_STAT, &status); + + if (ret < 0) { + dev_err(dev, "Reading status failed\n"); + goto error_ret; + } + ret = status; + if (status & ADIS16400_DIAG_STAT_ZACCL_FAIL) + dev_err(dev, "Z-axis accelerometer self-test failure\n"); + if (status & ADIS16400_DIAG_STAT_YACCL_FAIL) + dev_err(dev, "Y-axis accelerometer self-test failure\n"); + if (status & ADIS16400_DIAG_STAT_XACCL_FAIL) + dev_err(dev, "X-axis accelerometer self-test failure\n"); + if (status & ADIS16400_DIAG_STAT_XGYRO_FAIL) + dev_err(dev, "X-axis gyroscope self-test failure\n"); + if (status & ADIS16400_DIAG_STAT_YGYRO_FAIL) + dev_err(dev, "Y-axis gyroscope self-test failure\n"); + if (status & ADIS16400_DIAG_STAT_ZGYRO_FAIL) + dev_err(dev, "Z-axis gyroscope self-test failure\n"); + if (status & ADIS16400_DIAG_STAT_ALARM2) + dev_err(dev, "Alarm 2 active\n"); + if (status & ADIS16400_DIAG_STAT_ALARM1) + dev_err(dev, "Alarm 1 active\n"); + if (status & ADIS16400_DIAG_STAT_FLASH_CHK) + dev_err(dev, "Flash checksum error\n"); + if (status & ADIS16400_DIAG_STAT_SELF_TEST) + dev_err(dev, "Self test error\n"); + if (status & ADIS16400_DIAG_STAT_OVERFLOW) + dev_err(dev, "Sensor overrange\n"); + if (status & ADIS16400_DIAG_STAT_SPI_FAIL) + dev_err(dev, "SPI failure\n"); + if (status & ADIS16400_DIAG_STAT_FLASH_UPT) + dev_err(dev, "Flash update failed\n"); + if (status & ADIS16400_DIAG_STAT_POWER_HIGH) + dev_err(dev, "Power supply above 5.25V\n"); + if (status & ADIS16400_DIAG_STAT_POWER_LOW) + dev_err(dev, "Power supply below 4.75V\n"); + +error_ret: + return ret; +} + +static int adis16400_initial_setup(struct adis16400_state *st) +{ + int ret; + u16 prod_id, smp_prd; + struct device *dev = &st->indio_dev->dev; + + /* use low spi speed for init */ + st->us->max_speed_hz = ADIS16400_SPI_SLOW; + st->us->mode = SPI_MODE_3; + spi_setup(st->us); + + /* Disable IRQ */ + ret = adis16400_set_irq(dev, false); + if (ret) { + dev_err(dev, "disable irq failed"); + goto err_ret; + } + + /* Do self test */ + + /* Read status register to check the result */ + ret = adis16400_check_status(dev); + if (ret) { + adis16400_reset(dev); + dev_err(dev, "device not playing ball -> reset"); + msleep(ADIS16400_STARTUP_DELAY); + ret = adis16400_check_status(dev); + if (ret) { + dev_err(dev, "giving up"); + goto err_ret; + } + } + + ret = adis16400_spi_read_reg_16(dev, ADIS16400_PRODUCT_ID, &prod_id); + if (ret) + goto err_ret; + + if (prod_id != ADIS16400_PRODUCT_ID_DEFAULT) + dev_warn(dev, "unknown product id"); + + printk(KERN_INFO DRIVER_NAME ": prod_id 0x%04x at CS%d (irq %d)\n", + prod_id, st->us->chip_select, st->us->irq); + + /* use high spi speed if possible */ + ret = adis16400_spi_read_reg_16(dev, ADIS16400_SMPL_PRD, &smp_prd); + if (!ret && (smp_prd & ADIS16400_SMPL_PRD_DIV_MASK) < 0x0A) { + st->us->max_speed_hz = ADIS16400_SPI_SLOW; + spi_setup(st->us); + } + + +err_ret: + + return ret; +} + +static IIO_DEV_ATTR_ACCEL_X_OFFSET(S_IWUSR | S_IRUGO, + adis16400_read_12bit_signed, + adis16400_write_16bit, + ADIS16400_XACCL_OFF); + +static IIO_DEV_ATTR_ACCEL_Y_OFFSET(S_IWUSR | S_IRUGO, + adis16400_read_12bit_signed, + adis16400_write_16bit, + ADIS16400_YACCL_OFF); + +static IIO_DEV_ATTR_ACCEL_Z_OFFSET(S_IWUSR | S_IRUGO, + adis16400_read_12bit_signed, + adis16400_write_16bit, + ADIS16400_ZACCL_OFF); + +static IIO_DEV_ATTR_IN_NAMED_RAW(supply, adis16400_read_14bit_signed, + ADIS16400_SUPPLY_OUT); +static IIO_CONST_ATTR(in_supply_scale, "0.002418"); + +static IIO_DEV_ATTR_GYRO_X(adis16400_read_14bit_signed, + ADIS16400_XGYRO_OUT); +static IIO_DEV_ATTR_GYRO_Y(adis16400_read_14bit_signed, + ADIS16400_YGYRO_OUT); +static IIO_DEV_ATTR_GYRO_Z(adis16400_read_14bit_signed, + ADIS16400_ZGYRO_OUT); +static IIO_CONST_ATTR(gyro_scale, "0.05 deg/s"); + +static IIO_DEV_ATTR_ACCEL_X(adis16400_read_14bit_signed, + ADIS16400_XACCL_OUT); +static IIO_DEV_ATTR_ACCEL_Y(adis16400_read_14bit_signed, + ADIS16400_YACCL_OUT); +static IIO_DEV_ATTR_ACCEL_Z(adis16400_read_14bit_signed, + ADIS16400_ZACCL_OUT); +static IIO_CONST_ATTR(accel_scale, "0.00333 g"); + +static IIO_DEV_ATTR_MAGN_X(adis16400_read_14bit_signed, + ADIS16400_XMAGN_OUT); +static IIO_DEV_ATTR_MAGN_Y(adis16400_read_14bit_signed, + ADIS16400_YMAGN_OUT); +static IIO_DEV_ATTR_MAGN_Z(adis16400_read_14bit_signed, + ADIS16400_ZMAGN_OUT); +static IIO_CONST_ATTR(magn_scale, "0.0005 Gs"); + + +static IIO_DEV_ATTR_TEMP_RAW(adis16400_read_12bit_signed); +static IIO_CONST_ATTR(temp_offset, "198.16 K"); +static IIO_CONST_ATTR(temp_scale, "0.14 K"); + +static IIO_DEV_ATTR_IN_RAW(0, adis16400_read_12bit_unsigned, + ADIS16400_AUX_ADC); +static IIO_CONST_ATTR(in0_scale, "0.000806"); + +static IIO_DEV_ATTR_SAMP_FREQ(S_IWUSR | S_IRUGO, + adis16400_read_frequency, + adis16400_write_frequency); + +static IIO_DEVICE_ATTR(reset, S_IWUSR, NULL, adis16400_write_reset, 0); + +static IIO_CONST_ATTR_AVAIL_SAMP_FREQ("409 546 819 1638"); + +static IIO_CONST_ATTR(name, "adis16400"); + +static struct attribute *adis16400_event_attributes[] = { + NULL +}; + +static struct attribute_group adis16400_event_attribute_group = { + .attrs = adis16400_event_attributes, +}; + +static struct attribute *adis16400_attributes[] = { + &iio_dev_attr_accel_x_offset.dev_attr.attr, + &iio_dev_attr_accel_y_offset.dev_attr.attr, + &iio_dev_attr_accel_z_offset.dev_attr.attr, + &iio_dev_attr_in_supply_raw.dev_attr.attr, + &iio_const_attr_in_supply_scale.dev_attr.attr, + &iio_dev_attr_gyro_x_raw.dev_attr.attr, + &iio_dev_attr_gyro_y_raw.dev_attr.attr, + &iio_dev_attr_gyro_z_raw.dev_attr.attr, + &iio_const_attr_gyro_scale.dev_attr.attr, + &iio_dev_attr_accel_x_raw.dev_attr.attr, + &iio_dev_attr_accel_y_raw.dev_attr.attr, + &iio_dev_attr_accel_z_raw.dev_attr.attr, + &iio_const_attr_accel_scale.dev_attr.attr, + &iio_dev_attr_magn_x_raw.dev_attr.attr, + &iio_dev_attr_magn_y_raw.dev_attr.attr, + &iio_dev_attr_magn_z_raw.dev_attr.attr, + &iio_const_attr_magn_scale.dev_attr.attr, + &iio_dev_attr_temp_raw.dev_attr.attr, + &iio_const_attr_temp_offset.dev_attr.attr, + &iio_const_attr_temp_scale.dev_attr.attr, + &iio_dev_attr_in0_raw.dev_attr.attr, + &iio_const_attr_in0_scale.dev_attr.attr, + &iio_dev_attr_sampling_frequency.dev_attr.attr, + &iio_const_attr_available_sampling_frequency.dev_attr.attr, + &iio_dev_attr_reset.dev_attr.attr, + &iio_const_attr_name.dev_attr.attr, + NULL +}; + +static const struct attribute_group adis16400_attribute_group = { + .attrs = adis16400_attributes, +}; + +static int __devinit adis16400_probe(struct spi_device *spi) +{ + int ret, regdone = 0; + struct adis16400_state *st = kzalloc(sizeof *st, GFP_KERNEL); + if (!st) { + ret = -ENOMEM; + goto error_ret; + } + /* this is only used for removal purposes */ + spi_set_drvdata(spi, st); + + /* Allocate the comms buffers */ + st->rx = kzalloc(sizeof(*st->rx)*ADIS16400_MAX_RX, GFP_KERNEL); + if (st->rx == NULL) { + ret = -ENOMEM; + goto error_free_st; + } + st->tx = kzalloc(sizeof(*st->tx)*ADIS16400_MAX_TX, GFP_KERNEL); + if (st->tx == NULL) { + ret = -ENOMEM; + goto error_free_rx; + } + st->us = spi; + mutex_init(&st->buf_lock); + /* setup the industrialio driver allocated elements */ + st->indio_dev = iio_allocate_device(); + if (st->indio_dev == NULL) { + ret = -ENOMEM; + goto error_free_tx; + } + + st->indio_dev->dev.parent = &spi->dev; + st->indio_dev->num_interrupt_lines = 1; + st->indio_dev->event_attrs = &adis16400_event_attribute_group; + st->indio_dev->attrs = &adis16400_attribute_group; + st->indio_dev->dev_data = (void *)(st); + st->indio_dev->driver_module = THIS_MODULE; + st->indio_dev->modes = INDIO_DIRECT_MODE; + + ret = adis16400_configure_ring(st->indio_dev); + if (ret) + goto error_free_dev; + + ret = iio_device_register(st->indio_dev); + if (ret) + goto error_unreg_ring_funcs; + regdone = 1; + + ret = adis16400_initialize_ring(st->indio_dev->ring); + if (ret) { + printk(KERN_ERR "failed to initialize the ring\n"); + goto error_unreg_ring_funcs; + } + + if (spi->irq && gpio_is_valid(irq_to_gpio(spi->irq)) > 0) { +#if 0 /* fixme: here we should support */ + iio_init_work_cont(&st->work_cont_thresh, + NULL, + adis16400_thresh_handler_bh_no_check, + 0, + 0, + st); +#endif + ret = iio_register_interrupt_line(spi->irq, + st->indio_dev, + 0, + IRQF_TRIGGER_RISING, + "adis16400"); + if (ret) + goto error_uninitialize_ring; + + ret = adis16400_probe_trigger(st->indio_dev); + if (ret) + goto error_unregister_line; + } + + /* Get the device into a sane initial state */ + ret = adis16400_initial_setup(st); + if (ret) + goto error_remove_trigger; + return 0; + +error_remove_trigger: + if (st->indio_dev->modes & INDIO_RING_TRIGGERED) + adis16400_remove_trigger(st->indio_dev); +error_unregister_line: + if (st->indio_dev->modes & INDIO_RING_TRIGGERED) + iio_unregister_interrupt_line(st->indio_dev, 0); +error_uninitialize_ring: + adis16400_uninitialize_ring(st->indio_dev->ring); +error_unreg_ring_funcs: + adis16400_unconfigure_ring(st->indio_dev); +error_free_dev: + if (regdone) + iio_device_unregister(st->indio_dev); + else + iio_free_device(st->indio_dev); +error_free_tx: + kfree(st->tx); +error_free_rx: + kfree(st->rx); +error_free_st: + kfree(st); +error_ret: + return ret; +} + +/* fixme, confirm ordering in this function */ +static int adis16400_remove(struct spi_device *spi) +{ + int ret; + struct adis16400_state *st = spi_get_drvdata(spi); + struct iio_dev *indio_dev = st->indio_dev; + + ret = adis16400_stop_device(&(indio_dev->dev)); + if (ret) + goto err_ret; + + flush_scheduled_work(); + + adis16400_remove_trigger(indio_dev); + if (spi->irq && gpio_is_valid(irq_to_gpio(spi->irq)) > 0) + iio_unregister_interrupt_line(indio_dev, 0); + + adis16400_uninitialize_ring(indio_dev->ring); + adis16400_unconfigure_ring(indio_dev); + iio_device_unregister(indio_dev); + kfree(st->tx); + kfree(st->rx); + kfree(st); + + return 0; + +err_ret: + return ret; +} + +static struct spi_driver adis16400_driver = { + .driver = { + .name = "adis16400", + .owner = THIS_MODULE, + }, + .probe = adis16400_probe, + .remove = __devexit_p(adis16400_remove), +}; + +static __init int adis16400_init(void) +{ + return spi_register_driver(&adis16400_driver); +} +module_init(adis16400_init); + +static __exit void adis16400_exit(void) +{ + spi_unregister_driver(&adis16400_driver); +} +module_exit(adis16400_exit); + +MODULE_AUTHOR("Manuel Stahl <manuel.stahl@iis.fraunhofer.de>"); +MODULE_DESCRIPTION("Analog Devices ADIS16400/5 IMU SPI driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/iio/imu/adis16400_ring.c b/drivers/staging/iio/imu/adis16400_ring.c new file mode 100644 index 000000000000..5529b32bd2e3 --- /dev/null +++ b/drivers/staging/iio/imu/adis16400_ring.c @@ -0,0 +1,245 @@ +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/gpio.h> +#include <linux/workqueue.h> +#include <linux/mutex.h> +#include <linux/device.h> +#include <linux/kernel.h> +#include <linux/spi/spi.h> +#include <linux/sysfs.h> +#include <linux/list.h> + +#include "../iio.h" +#include "../sysfs.h" +#include "../ring_sw.h" +#include "../accel/accel.h" +#include "../trigger.h" +#include "adis16400.h" + +/** + * combine_8_to_16() utility function to munge to u8s into u16 + **/ +static inline u16 combine_8_to_16(u8 lower, u8 upper) +{ + u16 _lower = lower; + u16 _upper = upper; + return _lower | (_upper << 8); +} + +static IIO_SCAN_EL_C(supply, ADIS16400_SCAN_SUPPLY, IIO_SIGNED(14), + ADIS16400_SUPPLY_OUT, NULL); + +static IIO_SCAN_EL_C(gyro_x, ADIS16400_SCAN_GYRO_X, IIO_SIGNED(14), + ADIS16400_XGYRO_OUT, NULL); +static IIO_SCAN_EL_C(gyro_y, ADIS16400_SCAN_GYRO_Y, IIO_SIGNED(14), + ADIS16400_YGYRO_OUT, NULL); +static IIO_SCAN_EL_C(gyro_z, ADIS16400_SCAN_GYRO_Z, IIO_SIGNED(14), + ADIS16400_ZGYRO_OUT, NULL); + +static IIO_SCAN_EL_C(accel_x, ADIS16400_SCAN_ACC_X, IIO_SIGNED(14), + ADIS16400_XACCL_OUT, NULL); +static IIO_SCAN_EL_C(accel_y, ADIS16400_SCAN_ACC_Y, IIO_SIGNED(14), + ADIS16400_YACCL_OUT, NULL); +static IIO_SCAN_EL_C(accel_z, ADIS16400_SCAN_ACC_Z, IIO_SIGNED(14), + ADIS16400_ZACCL_OUT, NULL); + +static IIO_SCAN_EL_C(magn_x, ADIS16400_SCAN_MAGN_X, IIO_SIGNED(14), + ADIS16400_XMAGN_OUT, NULL); +static IIO_SCAN_EL_C(magn_y, ADIS16400_SCAN_MAGN_Y, IIO_SIGNED(14), + ADIS16400_YMAGN_OUT, NULL); +static IIO_SCAN_EL_C(magn_z, ADIS16400_SCAN_MAGN_Z, IIO_SIGNED(14), + ADIS16400_ZMAGN_OUT, NULL); + +static IIO_SCAN_EL_C(temp, ADIS16400_SCAN_TEMP, IIO_SIGNED(12), + ADIS16400_TEMP_OUT, NULL); +static IIO_SCAN_EL_C(adc_0, ADIS16400_SCAN_ADC_0, IIO_SIGNED(12), + ADIS16400_AUX_ADC, NULL); + +static IIO_SCAN_EL_TIMESTAMP(12); + +static struct attribute *adis16400_scan_el_attrs[] = { + &iio_scan_el_supply.dev_attr.attr, + &iio_scan_el_gyro_x.dev_attr.attr, + &iio_scan_el_gyro_y.dev_attr.attr, + &iio_scan_el_gyro_z.dev_attr.attr, + &iio_scan_el_accel_x.dev_attr.attr, + &iio_scan_el_accel_y.dev_attr.attr, + &iio_scan_el_accel_z.dev_attr.attr, + &iio_scan_el_magn_x.dev_attr.attr, + &iio_scan_el_magn_y.dev_attr.attr, + &iio_scan_el_magn_z.dev_attr.attr, + &iio_scan_el_temp.dev_attr.attr, + &iio_scan_el_adc_0.dev_attr.attr, + &iio_scan_el_timestamp.dev_attr.attr, + NULL, +}; + +static struct attribute_group adis16400_scan_el_group = { + .attrs = adis16400_scan_el_attrs, + .name = "scan_elements", +}; + +/** + * adis16400_poll_func_th() top half interrupt handler called by trigger + * @private_data: iio_dev + **/ +static void adis16400_poll_func_th(struct iio_dev *indio_dev) +{ + struct adis16400_state *st = iio_dev_get_devdata(indio_dev); + st->last_timestamp = indio_dev->trig->timestamp; + schedule_work(&st->work_trigger_to_ring); + /* Indicate that this interrupt is being handled */ + + /* Technically this is trigger related, but without this + * handler running there is currently no way for the interrupt + * to clear. + */ +} + +/* Whilst this makes a lot of calls to iio_sw_ring functions - it is to device + * specific to be rolled into the core. + */ +static void adis16400_trigger_bh_to_ring(struct work_struct *work_s) +{ + struct adis16400_state *st + = container_of(work_s, struct adis16400_state, + work_trigger_to_ring); + + int i = 0; + s16 *data; + size_t datasize = st->indio_dev + ->ring->access.get_bpd(st->indio_dev->ring); + + data = kmalloc(datasize , GFP_KERNEL); + if (data == NULL) { + dev_err(&st->us->dev, "memory alloc failed in ring bh"); + return; + } + + if (st->indio_dev->scan_count) + if (adis16400_spi_read_burst(&st->indio_dev->dev, st->rx) >= 0) + for (; i < st->indio_dev->scan_count; i++) { + data[i] = combine_8_to_16(st->rx[i*2+1], + st->rx[i*2]); + } + + /* Guaranteed to be aligned with 8 byte boundary */ + if (st->indio_dev->scan_timestamp) + *((s64 *)(data + ((i + 3)/4)*4)) = st->last_timestamp; + + st->indio_dev->ring->access.store_to(st->indio_dev->ring, + (u8 *)data, + st->last_timestamp); + + iio_trigger_notify_done(st->indio_dev->trig); + kfree(data); + + return; +} +/* in these circumstances is it better to go with unaligned packing and + * deal with the cost?*/ +static int adis16400_data_rdy_ring_preenable(struct iio_dev *indio_dev) +{ + size_t size; + dev_dbg(&indio_dev->dev, "%s\n", __func__); + /* Check if there are any scan elements enabled, if not fail*/ + if (!(indio_dev->scan_count || indio_dev->scan_timestamp)) + return -EINVAL; + + if (indio_dev->ring->access.set_bpd) { + if (indio_dev->scan_timestamp) + if (indio_dev->scan_count) /* Timestamp and data */ + size = 6*sizeof(s64); + else /* Timestamp only */ + size = sizeof(s64); + else /* Data only */ + size = indio_dev->scan_count*sizeof(s16); + indio_dev->ring->access.set_bpd(indio_dev->ring, size); + } + + return 0; +} + +static int adis16400_data_rdy_ring_postenable(struct iio_dev *indio_dev) +{ + return indio_dev->trig + ? iio_trigger_attach_poll_func(indio_dev->trig, + indio_dev->pollfunc) + : 0; +} + +static int adis16400_data_rdy_ring_predisable(struct iio_dev *indio_dev) +{ + return indio_dev->trig + ? iio_trigger_dettach_poll_func(indio_dev->trig, + indio_dev->pollfunc) + : 0; +} + +void adis16400_unconfigure_ring(struct iio_dev *indio_dev) +{ + kfree(indio_dev->pollfunc); + iio_sw_rb_free(indio_dev->ring); +} + +int adis16400_configure_ring(struct iio_dev *indio_dev) +{ + int ret = 0; + struct adis16400_state *st = indio_dev->dev_data; + struct iio_ring_buffer *ring; + INIT_WORK(&st->work_trigger_to_ring, adis16400_trigger_bh_to_ring); + /* Set default scan mode */ + + iio_scan_mask_set(indio_dev, iio_scan_el_supply.number); + iio_scan_mask_set(indio_dev, iio_scan_el_gyro_x.number); + iio_scan_mask_set(indio_dev, iio_scan_el_gyro_y.number); + iio_scan_mask_set(indio_dev, iio_scan_el_gyro_z.number); + iio_scan_mask_set(indio_dev, iio_scan_el_accel_x.number); + iio_scan_mask_set(indio_dev, iio_scan_el_accel_y.number); + iio_scan_mask_set(indio_dev, iio_scan_el_accel_z.number); + iio_scan_mask_set(indio_dev, iio_scan_el_magn_x.number); + iio_scan_mask_set(indio_dev, iio_scan_el_magn_y.number); + iio_scan_mask_set(indio_dev, iio_scan_el_magn_z.number); + iio_scan_mask_set(indio_dev, iio_scan_el_temp.number); + iio_scan_mask_set(indio_dev, iio_scan_el_adc_0.number); + indio_dev->scan_timestamp = true; + + indio_dev->scan_el_attrs = &adis16400_scan_el_group; + + ring = iio_sw_rb_allocate(indio_dev); + if (!ring) { + ret = -ENOMEM; + return ret; + } + indio_dev->ring = ring; + /* Effectively select the ring buffer implementation */ + iio_ring_sw_register_funcs(&ring->access); + ring->preenable = &adis16400_data_rdy_ring_preenable; + ring->postenable = &adis16400_data_rdy_ring_postenable; + ring->predisable = &adis16400_data_rdy_ring_predisable; + ring->owner = THIS_MODULE; + + indio_dev->pollfunc = kzalloc(sizeof(*indio_dev->pollfunc), GFP_KERNEL); + if (indio_dev->pollfunc == NULL) { + ret = -ENOMEM; + goto error_iio_sw_rb_free;; + } + indio_dev->pollfunc->poll_func_main = &adis16400_poll_func_th; + indio_dev->pollfunc->private_data = indio_dev; + indio_dev->modes |= INDIO_RING_TRIGGERED; + return 0; + +error_iio_sw_rb_free: + iio_sw_rb_free(indio_dev->ring); + return ret; +} + +int adis16400_initialize_ring(struct iio_ring_buffer *ring) +{ + return iio_ring_buffer_register(ring, 0); +} + +void adis16400_uninitialize_ring(struct iio_ring_buffer *ring) +{ + iio_ring_buffer_unregister(ring); +} diff --git a/drivers/staging/iio/imu/adis16400_trigger.c b/drivers/staging/iio/imu/adis16400_trigger.c new file mode 100644 index 000000000000..3b3250ac7680 --- /dev/null +++ b/drivers/staging/iio/imu/adis16400_trigger.c @@ -0,0 +1,127 @@ +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/mutex.h> +#include <linux/device.h> +#include <linux/kernel.h> +#include <linux/sysfs.h> +#include <linux/list.h> +#include <linux/spi/spi.h> + +#include "../iio.h" +#include "../sysfs.h" +#include "../trigger.h" +#include "adis16400.h" + +/** + * adis16400_data_rdy_trig_poll() the event handler for the data rdy trig + **/ +static int adis16400_data_rdy_trig_poll(struct iio_dev *dev_info, + int index, + s64 timestamp, + int no_test) +{ + struct adis16400_state *st = iio_dev_get_devdata(dev_info); + struct iio_trigger *trig = st->trig; + + trig->timestamp = timestamp; + iio_trigger_poll(trig); + + return IRQ_HANDLED; +} + +IIO_EVENT_SH(data_rdy_trig, &adis16400_data_rdy_trig_poll); + +static DEVICE_ATTR(name, S_IRUGO, iio_trigger_read_name, NULL); + +static struct attribute *adis16400_trigger_attrs[] = { + &dev_attr_name.attr, + NULL, +}; + +static const struct attribute_group adis16400_trigger_attr_group = { + .attrs = adis16400_trigger_attrs, +}; + +/** + * adis16400_data_rdy_trigger_set_state() set datardy interrupt state + **/ +static int adis16400_data_rdy_trigger_set_state(struct iio_trigger *trig, + bool state) +{ + struct adis16400_state *st = trig->private_data; + struct iio_dev *indio_dev = st->indio_dev; + int ret = 0; + + dev_dbg(&indio_dev->dev, "%s (%d)\n", __func__, state); + ret = adis16400_set_irq(&st->indio_dev->dev, state); + if (state == false) { + iio_remove_event_from_list(&iio_event_data_rdy_trig, + &indio_dev->interrupts[0] + ->ev_list); + /* possible quirk with handler currently worked around + by ensuring the work queue is empty */ + flush_scheduled_work(); + } else { + iio_add_event_to_list(&iio_event_data_rdy_trig, + &indio_dev->interrupts[0]->ev_list); + } + return ret; +} + +/** + * adis16400_trig_try_reen() try renabling irq for data rdy trigger + * @trig: the datardy trigger + **/ +static int adis16400_trig_try_reen(struct iio_trigger *trig) +{ + struct adis16400_state *st = trig->private_data; + enable_irq(st->us->irq); + /* irq reenabled so success! */ + return 0; +} + +int adis16400_probe_trigger(struct iio_dev *indio_dev) +{ + int ret; + struct adis16400_state *st = indio_dev->dev_data; + + st->trig = iio_allocate_trigger(); + st->trig->name = kmalloc(IIO_TRIGGER_NAME_LENGTH, GFP_KERNEL); + if (!st->trig->name) { + ret = -ENOMEM; + goto error_free_trig; + } + snprintf((char *)st->trig->name, + IIO_TRIGGER_NAME_LENGTH, + "adis16400-dev%d", indio_dev->id); + st->trig->dev.parent = &st->us->dev; + st->trig->owner = THIS_MODULE; + st->trig->private_data = st; + st->trig->set_trigger_state = &adis16400_data_rdy_trigger_set_state; + st->trig->try_reenable = &adis16400_trig_try_reen; + st->trig->control_attrs = &adis16400_trigger_attr_group; + ret = iio_trigger_register(st->trig); + + /* select default trigger */ + indio_dev->trig = st->trig; + if (ret) + goto error_free_trig_name; + + return 0; + +error_free_trig_name: + kfree(st->trig->name); +error_free_trig: + iio_free_trigger(st->trig); + + return ret; +} + +void adis16400_remove_trigger(struct iio_dev *indio_dev) +{ + struct adis16400_state *state = indio_dev->dev_data; + + iio_trigger_unregister(state->trig); + kfree(state->trig->name); + iio_free_trigger(state->trig); +} diff --git a/drivers/staging/iio/industrialio-core.c b/drivers/staging/iio/industrialio-core.c index 1d77082c8531..01030684ef28 100644 --- a/drivers/staging/iio/industrialio-core.c +++ b/drivers/staging/iio/industrialio-core.c @@ -42,16 +42,10 @@ dev_t iio_devt; EXPORT_SYMBOL(iio_devt); #define IIO_DEV_MAX 256 -static char *iio_devnode(struct device *dev, mode_t *mode) -{ - return kasprintf(GFP_KERNEL, "iio/%s", dev_name(dev)); -} - -struct class iio_class = { +struct bus_type iio_bus_type = { .name = "iio", - .devnode = iio_devnode, }; -EXPORT_SYMBOL(iio_class); +EXPORT_SYMBOL(iio_bus_type); void __iio_change_event(struct iio_detected_event_list *ev, int ev_code, @@ -405,7 +399,7 @@ int iio_setup_ev_int(struct iio_event_interface *ev_int, { int ret, minor; - ev_int->dev.class = &iio_class; + ev_int->dev.bus = &iio_bus_type; ev_int->dev.parent = dev; ev_int->dev.type = &iio_event_type; device_initialize(&ev_int->dev); @@ -478,23 +472,23 @@ static int __init iio_init(void) { int ret; - /* Create sysfs class */ - ret = class_register(&iio_class); + /* Register sysfs bus */ + ret = bus_register(&iio_bus_type); if (ret < 0) { printk(KERN_ERR - "%s could not create sysfs class\n", + "%s could not register bus type\n", __FILE__); goto error_nothing; } ret = iio_dev_init(); if (ret < 0) - goto error_unregister_class; + goto error_unregister_bus_type; return 0; -error_unregister_class: - class_unregister(&iio_class); +error_unregister_bus_type: + bus_unregister(&iio_bus_type); error_nothing: return ret; } @@ -502,7 +496,7 @@ error_nothing: static void __exit iio_exit(void) { iio_dev_exit(); - class_unregister(&iio_class); + bus_unregister(&iio_bus_type); } static int iio_device_register_sysfs(struct iio_dev *dev_info) @@ -667,8 +661,9 @@ static int iio_device_register_eventset(struct iio_dev *dev_info) dev_info->event_interfaces[i].id = ret; snprintf(dev_info->event_interfaces[i]._name, 20, - "event_line%d", - dev_info->event_interfaces[i].id); + "%s:event%d", + dev_name(&dev_info->dev), + dev_info->event_interfaces[i].id); ret = iio_setup_ev_int(&dev_info->event_interfaces[i], (const char *)(dev_info @@ -683,16 +678,14 @@ static int iio_device_register_eventset(struct iio_dev *dev_info) dev_info->event_interfaces[i].id); goto error_free_setup_ev_ints; } - } - for (i = 0; i < dev_info->num_interrupt_lines; i++) { - snprintf(dev_info->event_interfaces[i]._attrname, 20, - "event_line%d_sources", i); - dev_info->event_attrs[i].name - = (const char *) - (dev_info->event_interfaces[i]._attrname); - ret = sysfs_create_group(&dev_info->dev.kobj, - &dev_info->event_attrs[i]); + dev_set_drvdata(&dev_info->event_interfaces[i].dev, + (void *)dev_info); + ret = sysfs_create_group(&dev_info + ->event_interfaces[i] + .dev.kobj, + &dev_info->event_attrs[i]); + if (ret) { dev_err(&dev_info->dev, "Failed to register sysfs for event attrs"); @@ -714,13 +707,13 @@ error_unregister_config_attrs: i = dev_info->num_interrupt_lines - 1; error_remove_sysfs_interfaces: for (j = 0; j < i; j++) - sysfs_remove_group(&dev_info->dev.kobj, + sysfs_remove_group(&dev_info + ->event_interfaces[j].dev.kobj, &dev_info->event_attrs[j]); - i = dev_info->num_interrupt_lines - 1; error_free_setup_ev_ints: for (j = 0; j < i; j++) { iio_free_idr_val(&iio_event_idr, - dev_info->event_interfaces[i].id); + dev_info->event_interfaces[j].id); iio_free_ev_int(&dev_info->event_interfaces[j]); } kfree(dev_info->interrupts); @@ -738,7 +731,8 @@ static void iio_device_unregister_eventset(struct iio_dev *dev_info) if (dev_info->num_interrupt_lines == 0) return; for (i = 0; i < dev_info->num_interrupt_lines; i++) - sysfs_remove_group(&dev_info->dev.kobj, + sysfs_remove_group(&dev_info + ->event_interfaces[i].dev.kobj, &dev_info->event_attrs[i]); for (i = 0; i < dev_info->num_interrupt_lines; i++) { @@ -769,7 +763,7 @@ struct iio_dev *iio_allocate_device(void) if (dev) { dev->dev.type = &iio_dev_type; - dev->dev.class = &iio_class; + dev->dev.bus = &iio_bus_type; device_initialize(&dev->dev); dev_set_drvdata(&dev->dev, (void *)dev); mutex_init(&dev->mlock); @@ -810,7 +804,7 @@ int iio_device_register(struct iio_dev *dev_info) ret = iio_device_register_eventset(dev_info); if (ret) { dev_err(dev_info->dev.parent, - "Failed to register event set \n"); + "Failed to register event set\n"); goto error_free_sysfs; } if (dev_info->modes & INDIO_RING_TRIGGERED) diff --git a/drivers/staging/iio/industrialio-ring.c b/drivers/staging/iio/industrialio-ring.c index e53e214bfeb0..ada159bbb1f7 100644 --- a/drivers/staging/iio/industrialio-ring.c +++ b/drivers/staging/iio/industrialio-ring.c @@ -20,19 +20,11 @@ #include <linux/poll.h> #include <linux/module.h> #include <linux/cdev.h> -#include <linux/idr.h> #include <linux/slab.h> #include "iio.h" #include "ring_generic.h" -/* IDR for ring buffer identifier */ -static DEFINE_IDR(iio_ring_idr); -/* IDR for ring event identifier */ -static DEFINE_IDR(iio_ring_event_idr); -/* IDR for ring access identifier */ -static DEFINE_IDR(iio_ring_access_idr); - int iio_push_ring_event(struct iio_ring_buffer *ring_buf, int event_code, s64 timestamp) @@ -66,7 +58,7 @@ EXPORT_SYMBOL(iio_push_or_escallate_ring_event); * This function relies on all ring buffer implementations having an * iio_ring_buffer as their first element. **/ -int iio_ring_open(struct inode *inode, struct file *filp) +static int iio_ring_open(struct inode *inode, struct file *filp) { struct iio_handler *hand = container_of(inode->i_cdev, struct iio_handler, chrdev); @@ -85,7 +77,7 @@ int iio_ring_open(struct inode *inode, struct file *filp) * This function relies on all ring buffer implementations having an * iio_ring_buffer as their first element. **/ -int iio_ring_release(struct inode *inode, struct file *filp) +static int iio_ring_release(struct inode *inode, struct file *filp) { struct cdev *cd = inode->i_cdev; struct iio_handler *hand = iio_cdev_to_handler(cd); @@ -104,10 +96,8 @@ int iio_ring_release(struct inode *inode, struct file *filp) * This function relies on all ring buffer implementations having an * iio_ring _bufer as their first element. **/ -ssize_t iio_ring_rip_outer(struct file *filp, - char *buf, - size_t count, - loff_t *f_ps) +static ssize_t iio_ring_rip_outer(struct file *filp, char __user *buf, + size_t count, loff_t *f_ps) { struct iio_ring_buffer *rb = filp->private_data; int ret, dead_offset, copied; @@ -158,25 +148,21 @@ __iio_request_ring_buffer_event_chrdev(struct iio_ring_buffer *buf, struct device *dev) { int ret; - ret = iio_get_new_idr_val(&iio_ring_event_idr); - if (ret < 0) - goto error_ret; - else - buf->ev_int.id = ret; - snprintf(buf->ev_int._name, 20, - "ring_event_line%d", + buf->ev_int.id = id; + + snprintf(buf->ev_int._name, sizeof(buf->ev_int._name), + "%s:event%d", + dev_name(&buf->dev), buf->ev_int.id); ret = iio_setup_ev_int(&(buf->ev_int), buf->ev_int._name, owner, dev); if (ret) - goto error_free_id; + goto error_ret; return 0; -error_free_id: - iio_free_idr_val(&iio_ring_event_idr, buf->ev_int.id); error_ret: return ret; } @@ -185,7 +171,6 @@ static inline void __iio_free_ring_buffer_event_chrdev(struct iio_ring_buffer *buf) { iio_free_ev_int(&(buf->ev_int)); - iio_free_idr_val(&iio_ring_event_idr, buf->ev_int.id); } static void iio_ring_access_release(struct device *dev) @@ -210,7 +195,7 @@ __iio_request_ring_buffer_access_chrdev(struct iio_ring_buffer *buf, buf->access_handler.flags = 0; buf->access_dev.parent = &buf->dev; - buf->access_dev.class = &iio_class; + buf->access_dev.bus = &iio_bus_type; buf->access_dev.type = &iio_ring_access_type; device_initialize(&buf->access_dev); @@ -221,16 +206,16 @@ __iio_request_ring_buffer_access_chrdev(struct iio_ring_buffer *buf, } buf->access_dev.devt = MKDEV(MAJOR(iio_devt), minor); - ret = iio_get_new_idr_val(&iio_ring_access_idr); - if (ret < 0) - goto error_device_put; - else - buf->access_id = ret; - dev_set_name(&buf->access_dev, "ring_access%d", buf->access_id); + + buf->access_id = id; + + dev_set_name(&buf->access_dev, "%s:access%d", + dev_name(&buf->dev), + buf->access_id); ret = device_add(&buf->access_dev); if (ret < 0) { printk(KERN_ERR "failed to add the ring access dev\n"); - goto error_free_idr; + goto error_device_put; } cdev_init(&buf->access_handler.chrdev, &iio_ring_fileops); @@ -242,10 +227,9 @@ __iio_request_ring_buffer_access_chrdev(struct iio_ring_buffer *buf, goto error_device_unregister; } return 0; + error_device_unregister: device_unregister(&buf->access_dev); -error_free_idr: - iio_free_idr_val(&iio_ring_access_idr, buf->access_id); error_device_put: put_device(&buf->access_dev); @@ -254,7 +238,6 @@ error_device_put: static void __iio_free_ring_buffer_access_chrdev(struct iio_ring_buffer *buf) { - iio_free_idr_val(&iio_ring_access_idr, buf->access_id); device_unregister(&buf->access_dev); } @@ -266,22 +249,23 @@ void iio_ring_buffer_init(struct iio_ring_buffer *ring, ring->indio_dev = dev_info; ring->ev_int.private = ring; ring->access_handler.private = ring; + ring->shared_ev_pointer.ev_p = NULL; + spin_lock_init(&ring->shared_ev_pointer.lock); } EXPORT_SYMBOL(iio_ring_buffer_init); -int iio_ring_buffer_register(struct iio_ring_buffer *ring) +int iio_ring_buffer_register(struct iio_ring_buffer *ring, int id) { int ret; - ret = iio_get_new_idr_val(&iio_ring_idr); - if (ret < 0) - goto error_ret; - else - ring->id = ret; - dev_set_name(&ring->dev, "ring_buffer%d", ring->id); + ring->id = id; + + dev_set_name(&ring->dev, "%s:buffer%d", + dev_name(ring->dev.parent), + ring->id); ret = device_add(&ring->dev); if (ret) - goto error_free_id; + goto error_ret; ret = __iio_request_ring_buffer_event_chrdev(ring, 0, @@ -302,8 +286,6 @@ error_free_ring_buffer_event_chrdev: __iio_free_ring_buffer_event_chrdev(ring); error_remove_device: device_del(&ring->dev); -error_free_id: - iio_free_idr_val(&iio_ring_idr, ring->id); error_ret: return ret; } @@ -314,7 +296,6 @@ void iio_ring_buffer_unregister(struct iio_ring_buffer *ring) __iio_free_ring_buffer_access_chrdev(ring); __iio_free_ring_buffer_event_chrdev(ring); device_del(&ring->dev); - iio_free_idr_val(&iio_ring_idr, ring->id); } EXPORT_SYMBOL(iio_ring_buffer_unregister); diff --git a/drivers/staging/iio/industrialio-trigger.c b/drivers/staging/iio/industrialio-trigger.c index 35ec80ba444f..5682e61600f6 100644 --- a/drivers/staging/iio/industrialio-trigger.c +++ b/drivers/staging/iio/industrialio-trigger.c @@ -18,6 +18,7 @@ #include "iio.h" #include "trigger.h" +#include "trigger_consumer.h" /* RFC - Question of approach * Make the common case (single sensor single trigger) @@ -92,9 +93,9 @@ idr_again: **/ static void iio_trigger_unregister_id(struct iio_trigger *trig_info) { - spin_lock(&iio_trigger_idr_lock); - idr_remove(&iio_trigger_idr, trig_info->id); - spin_unlock(&iio_trigger_idr_lock); + spin_lock(&iio_trigger_idr_lock); + idr_remove(&iio_trigger_idr, trig_info->id); + spin_unlock(&iio_trigger_idr_lock); } int iio_trigger_register(struct iio_trigger *trig_info) @@ -156,6 +157,9 @@ struct iio_trigger *iio_trigger_find_by_name(const char *name, size_t len) struct iio_trigger *trig; bool found = false; + if (len && name[len - 1] == '\n') + len--; + mutex_lock(&iio_trigger_list_lock); list_for_each_entry(trig, &iio_trigger_list, list) { if (strncmp(trig->name, name, len) == 0) { @@ -166,7 +170,7 @@ struct iio_trigger *iio_trigger_find_by_name(const char *name, size_t len) mutex_unlock(&iio_trigger_list_lock); return found ? trig : NULL; -}; +} EXPORT_SYMBOL(iio_trigger_find_by_name); void iio_trigger_poll(struct iio_trigger *trig) @@ -331,9 +335,9 @@ static ssize_t iio_trigger_write_current(struct device *dev, return len; } -DEVICE_ATTR(current_trigger, S_IRUGO | S_IWUSR, - iio_trigger_read_current, - iio_trigger_write_current); +static DEVICE_ATTR(current_trigger, S_IRUGO | S_IWUSR, + iio_trigger_read_current, + iio_trigger_write_current); static struct attribute *iio_trigger_consumer_attrs[] = { &dev_attr_current_trigger.attr, @@ -362,7 +366,7 @@ struct iio_trigger *iio_allocate_trigger(void) trig = kzalloc(sizeof *trig, GFP_KERNEL); if (trig) { trig->dev.type = &iio_trig_type; - trig->dev.class = &iio_class; + trig->dev.bus = &iio_bus_type; device_initialize(&trig->dev); dev_set_drvdata(&trig->dev, (void *)trig); spin_lock_init(&trig->pollfunc_list_lock); diff --git a/drivers/staging/iio/light/tsl2563.c b/drivers/staging/iio/light/tsl2563.c index 8770a00e3652..e4b0a5ef1c1f 100644 --- a/drivers/staging/iio/light/tsl2563.c +++ b/drivers/staging/iio/light/tsl2563.c @@ -592,18 +592,30 @@ static ssize_t tsl2563_calib1_store(struct device *dev, * once I understand what they mean */ static DEVICE_ATTR(adc0, S_IRUGO, tsl2563_adc0_show, NULL); static DEVICE_ATTR(adc1, S_IRUGO, tsl2563_adc1_show, NULL); -static DEVICE_ATTR(lux, S_IRUGO, tsl2563_lux_show, NULL); +static DEVICE_ATTR(illuminance0_input, S_IRUGO, tsl2563_lux_show, NULL); static DEVICE_ATTR(calib0, S_IRUGO | S_IWUSR, tsl2563_calib0_show, tsl2563_calib0_store); static DEVICE_ATTR(calib1, S_IRUGO | S_IWUSR, tsl2563_calib1_show, tsl2563_calib1_store); +static ssize_t tsl2563_show_name(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct tsl2563_chip *chip = indio_dev->dev_data; + return sprintf(buf, "%s\n", chip->client->name); +} + +static DEVICE_ATTR(name, S_IRUGO, tsl2563_show_name, NULL); + static struct attribute *tsl2563_attributes[] = { &dev_attr_adc0.attr, &dev_attr_adc1.attr, - &dev_attr_lux.attr, + &dev_attr_illuminance0_input.attr, &dev_attr_calib0.attr, &dev_attr_calib1.attr, + &dev_attr_name.attr, NULL }; @@ -634,7 +646,7 @@ static int __devinit tsl2563_probe(struct i2c_client *client, err = tsl2563_detect(chip); if (err) { - dev_err(&client->dev, "device not found, error %d \n", -err); + dev_err(&client->dev, "device not found, error %d\n", -err); goto fail1; } @@ -682,7 +694,6 @@ static int __devinit tsl2563_probe(struct i2c_client *client, fail2: iio_device_unregister(chip->indio_dev); fail1: - i2c_set_clientdata(client, NULL); kfree(chip); return err; } @@ -693,7 +704,6 @@ static int tsl2563_remove(struct i2c_client *client) iio_device_unregister(chip->indio_dev); - i2c_set_clientdata(client, NULL); kfree(chip); return 0; } diff --git a/drivers/staging/iio/magnetometer/magnet.h b/drivers/staging/iio/magnetometer/magnet.h new file mode 100644 index 000000000000..64338301f8df --- /dev/null +++ b/drivers/staging/iio/magnetometer/magnet.h @@ -0,0 +1,31 @@ + +#include "../sysfs.h" + +/* Magnetometer types of attribute */ + +#define IIO_DEV_ATTR_MAGN_X_OFFSET(_mode, _show, _store, _addr) \ + IIO_DEVICE_ATTR(magn_x_offset, _mode, _show, _store, _addr) + +#define IIO_DEV_ATTR_MAGN_Y_OFFSET(_mode, _show, _store, _addr) \ + IIO_DEVICE_ATTR(magn_y_offset, _mode, _show, _store, _addr) + +#define IIO_DEV_ATTR_MAGN_Z_OFFSET(_mode, _show, _store, _addr) \ + IIO_DEVICE_ATTR(magn_z_offset, _mode, _show, _store, _addr) + +#define IIO_DEV_ATTR_MAGN_X_GAIN(_mode, _show, _store, _addr) \ + IIO_DEVICE_ATTR(magn_x_gain, _mode, _show, _store, _addr) + +#define IIO_DEV_ATTR_MAGN_Y_GAIN(_mode, _show, _store, _addr) \ + IIO_DEVICE_ATTR(magn_y_gain, _mode, _show, _store, _addr) + +#define IIO_DEV_ATTR_MAGN_Z_GAIN(_mode, _show, _store, _addr) \ + IIO_DEVICE_ATTR(magn_z_gain, _mode, _show, _store, _addr) + +#define IIO_DEV_ATTR_MAGN_X(_show, _addr) \ + IIO_DEVICE_ATTR(magn_x_raw, S_IRUGO, _show, NULL, _addr) + +#define IIO_DEV_ATTR_MAGN_Y(_show, _addr) \ + IIO_DEVICE_ATTR(magn_y_raw, S_IRUGO, _show, NULL, _addr) + +#define IIO_DEV_ATTR_MAGN_Z(_show, _addr) \ + IIO_DEVICE_ATTR(magn_z_raw, S_IRUGO, _show, NULL, _addr) diff --git a/drivers/staging/iio/ring_generic.h b/drivers/staging/iio/ring_generic.h index 09044adf7327..0e443757b029 100644 --- a/drivers/staging/iio/ring_generic.h +++ b/drivers/staging/iio/ring_generic.h @@ -134,19 +134,17 @@ void iio_ring_buffer_init(struct iio_ring_buffer *ring, struct iio_dev *dev_info); /** - * __iio_init_ring_buffer() - initialize common elements of ring buffers + * __iio_update_ring_buffer() - update common elements of ring buffers * @ring: ring buffer that is the event source * @bytes_per_datum: size of individual datum including timestamp * @length: number of datums in ring **/ -static inline void __iio_init_ring_buffer(struct iio_ring_buffer *ring, - int bytes_per_datum, int length) +static inline void __iio_update_ring_buffer(struct iio_ring_buffer *ring, + int bytes_per_datum, int length) { ring->bpd = bytes_per_datum; ring->length = length; ring->loopcount = 0; - ring->shared_ev_pointer.ev_p = 0; - spin_lock_init(&ring->shared_ev_pointer.lock); } /** @@ -198,25 +196,6 @@ ssize_t iio_scan_el_store(struct device *dev, struct device_attribute *attr, **/ ssize_t iio_scan_el_show(struct device *dev, struct device_attribute *attr, char *buf); -/** - * IIO_SCAN_EL - declare and initialize a scan element without control func - * @_name: identifying name. Resulting struct is iio_scan_el_##_name, - * sysfs element, scan_en_##_name. - * @_number: unique id number for the scan element. - * @_bits: number of bits in the scan element result (used in mixed bit - * length devices). - * @_label: indentification variable used by drivers. Often a reg address. - **/ -#define IIO_SCAN_EL(_name, _number, _bits, _label) \ - struct iio_scan_el iio_scan_el_##_name = { \ - .dev_attr = __ATTR(scan_en_##_name, \ - S_IRUGO | S_IWUSR, \ - iio_scan_el_show, \ - iio_scan_el_store), \ - .mask = (1 << _number), \ - .bit_count = _bits, \ - .label = _label, \ - } ssize_t iio_scan_el_ts_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t len); @@ -227,7 +206,7 @@ ssize_t iio_scan_el_ts_show(struct device *dev, struct device_attribute *attr, * IIO_SCAN_EL_C - declare and initialize a scan element with a control func * * @_name: identifying name. Resulting struct is iio_scan_el_##_name, - * sysfs element, scan_en_##_name. + * sysfs element, _name##_en. * @_number: unique id number for the scan element. * @_bits: number of bits in the scan element result (used in mixed bit * length devices). @@ -236,7 +215,7 @@ ssize_t iio_scan_el_ts_show(struct device *dev, struct device_attribute *attr, **/ #define IIO_SCAN_EL_C(_name, _number, _bits, _label, _controlfunc) \ struct iio_scan_el iio_scan_el_##_name = { \ - .dev_attr = __ATTR(scan_en_##_name, \ + .dev_attr = __ATTR(_number##_##_name##_en, \ S_IRUGO | S_IWUSR, \ iio_scan_el_show, \ iio_scan_el_store), \ @@ -245,14 +224,27 @@ ssize_t iio_scan_el_ts_show(struct device *dev, struct device_attribute *attr, .label = _label, \ .set_state = _controlfunc, \ } + +#define IIO_SCAN_NAMED_EL_C(_name, _string, _number, _bits, _label, _cf) \ + struct iio_scan_el iio_scan_el_##_name = { \ + .dev_attr = __ATTR(_number##_##_string##_en, \ + S_IRUGO | S_IWUSR, \ + iio_scan_el_show, \ + iio_scan_el_store), \ + .number = _number, \ + .bit_count = _bits, \ + .label = _label, \ + .set_state = _cf, \ + } + /** * IIO_SCAN_EL_TIMESTAMP - declare a special scan element for timestamps * * Odd one out. Handled slightly differently from other scan elements. **/ -#define IIO_SCAN_EL_TIMESTAMP \ +#define IIO_SCAN_EL_TIMESTAMP(number) \ struct iio_scan_el iio_scan_el_timestamp = { \ - .dev_attr = __ATTR(scan_en_timestamp, \ + .dev_attr = __ATTR(number##_timestamp_en, \ S_IRUGO | S_IWUSR, \ iio_scan_el_ts_show, \ iio_scan_el_ts_store), \ @@ -267,7 +259,7 @@ static inline void iio_put_ring_buffer(struct iio_ring_buffer *ring) container_of(d, struct iio_ring_buffer, dev) #define access_dev_to_iio_ring_buffer(d) \ container_of(d, struct iio_ring_buffer, access_dev) -int iio_ring_buffer_register(struct iio_ring_buffer *ring); +int iio_ring_buffer_register(struct iio_ring_buffer *ring, int id); void iio_ring_buffer_unregister(struct iio_ring_buffer *ring); ssize_t iio_read_ring_length(struct device *dev, diff --git a/drivers/staging/iio/ring_sw.c b/drivers/staging/iio/ring_sw.c index cf22c091668c..294272d0619f 100644 --- a/drivers/staging/iio/ring_sw.c +++ b/drivers/staging/iio/ring_sw.c @@ -14,20 +14,23 @@ #include <linux/workqueue.h> #include "ring_sw.h" -static inline int __iio_init_sw_ring_buffer(struct iio_sw_ring_buffer *ring, - int bytes_per_datum, int length) +static inline int __iio_allocate_sw_ring_buffer(struct iio_sw_ring_buffer *ring, + int bytes_per_datum, int length) { if ((length == 0) || (bytes_per_datum == 0)) return -EINVAL; + __iio_update_ring_buffer(&ring->buf, bytes_per_datum, length); + ring->data = kmalloc(length*ring->buf.bpd, GFP_ATOMIC); + ring->read_p = NULL; + ring->write_p = NULL; + ring->last_written_p = NULL; + ring->half_p = NULL; + return ring->data ? 0 : -ENOMEM; +} - __iio_init_ring_buffer(&ring->buf, bytes_per_datum, length); +static inline void __iio_init_sw_ring_buffer(struct iio_sw_ring_buffer *ring) +{ spin_lock_init(&ring->use_lock); - ring->data = kmalloc(length*ring->buf.bpd, GFP_KERNEL); - ring->read_p = 0; - ring->write_p = 0; - ring->last_written_p = 0; - ring->half_p = 0; - return ring->data ? 0 : -ENOMEM; } static inline void __iio_free_sw_ring_buffer(struct iio_sw_ring_buffer *ring) @@ -59,16 +62,15 @@ EXPORT_SYMBOL(iio_unmark_sw_rb_in_use); * in the device driver */ /* Lock always held if their is a chance this may be called */ /* Only one of these per ring may run concurrently - enforced by drivers */ -int iio_store_to_sw_ring(struct iio_sw_ring_buffer *ring, - unsigned char *data, - s64 timestamp) +static int iio_store_to_sw_ring(struct iio_sw_ring_buffer *ring, + unsigned char *data, s64 timestamp) { int ret = 0; int code; unsigned char *temp_ptr, *change_test_ptr; /* initial store */ - if (unlikely(ring->write_p == 0)) { + if (unlikely(ring->write_p == NULL)) { ring->write_p = ring->data; /* Doesn't actually matter if this is out of the set * as long as the read pointer is valid before this @@ -99,7 +101,7 @@ int iio_store_to_sw_ring(struct iio_sw_ring_buffer *ring, */ ring->write_p = temp_ptr; - if (ring->read_p == 0) + if (ring->read_p == NULL) ring->read_p = ring->data; /* Buffer full - move the read pointer and create / escalate * ring event */ @@ -123,8 +125,7 @@ int iio_store_to_sw_ring(struct iio_sw_ring_buffer *ring, spin_lock(&ring->buf.shared_ev_pointer.lock); ret = iio_push_or_escallate_ring_event(&ring->buf, - IIO_EVENT_CODE_RING_100_FULL, - timestamp); + IIO_EVENT_CODE_RING_100_FULL, timestamp); spin_unlock(&ring->buf.shared_ev_pointer.lock); if (ret) goto error_ret; @@ -180,7 +181,7 @@ int iio_rip_sw_rb(struct iio_ring_buffer *r, /* build local copy */ initial_read_p = ring->read_p; - if (unlikely(initial_read_p == 0)) { /* No data here as yet */ + if (unlikely(initial_read_p == NULL)) { /* No data here as yet */ ret = 0; goto error_free_data_cpy; } @@ -278,8 +279,8 @@ int iio_store_to_sw_rb(struct iio_ring_buffer *r, u8 *data, s64 timestamp) } EXPORT_SYMBOL(iio_store_to_sw_rb); -int iio_read_last_from_sw_ring(struct iio_sw_ring_buffer *ring, - unsigned char *data) +static int iio_read_last_from_sw_ring(struct iio_sw_ring_buffer *ring, + unsigned char *data) { unsigned char *last_written_p_copy; @@ -289,7 +290,7 @@ again: last_written_p_copy = ring->last_written_p; barrier(); /*unnessecary? */ /* Check there is anything here */ - if (last_written_p_copy == 0) + if (last_written_p_copy == NULL) return -EAGAIN; memcpy(data, last_written_p_copy, ring->buf.bpd); @@ -320,7 +321,8 @@ int iio_request_update_sw_rb(struct iio_ring_buffer *r) goto error_ret; } __iio_free_sw_ring_buffer(ring); - ret = __iio_init_sw_ring_buffer(ring, ring->buf.bpd, ring->buf.length); + ret = __iio_allocate_sw_ring_buffer(ring, ring->buf.bpd, + ring->buf.length); error_ret: spin_unlock(&ring->use_lock); return ret; @@ -409,14 +411,14 @@ struct iio_ring_buffer *iio_sw_rb_allocate(struct iio_dev *indio_dev) ring = kzalloc(sizeof *ring, GFP_KERNEL); if (!ring) - return 0; + return NULL; buf = &ring->buf; - iio_ring_buffer_init(buf, indio_dev); + __iio_init_sw_ring_buffer(ring); buf->dev.type = &iio_sw_ring_type; device_initialize(&buf->dev); buf->dev.parent = &indio_dev->dev; - buf->dev.class = &iio_class; + buf->dev.bus = &iio_bus_type; dev_set_drvdata(&buf->dev, (void *)buf); return buf; diff --git a/drivers/staging/iio/sysfs.h b/drivers/staging/iio/sysfs.h index e501e1338e11..afcf5ab85f48 100644 --- a/drivers/staging/iio/sysfs.h +++ b/drivers/staging/iio/sysfs.h @@ -98,6 +98,9 @@ struct iio_const_attr { struct iio_dev_attr iio_dev_attr_##_name \ = IIO_ATTR(_name, _mode, _show, _store, _addr) +#define IIO_DEVICE_ATTR_NAMED(_vname, _name, _mode, _show, _store, _addr) \ + struct iio_dev_attr iio_dev_attr_##_vname \ + = IIO_ATTR(_name, _mode, _show, _store, _addr) #define IIO_DEVICE_ATTR_2(_name, _mode, _show, _store, _addr, _val2) \ struct iio_dev_attr iio_dev_attr_##_name \ @@ -141,18 +144,25 @@ struct iio_const_attr { * * May be mode dependent on some devices **/ +/* Deprecated */ #define IIO_DEV_ATTR_AVAIL_SAMP_FREQ(_show) \ IIO_DEVICE_ATTR(available_sampling_frequency, S_IRUGO, _show, NULL, 0) +#define IIO_DEV_ATTR_SAMP_FREQ_AVAIL(_show) \ + IIO_DEVICE_ATTR(sampling_frequency_available, S_IRUGO, _show, NULL, 0) /** * IIO_CONST_ATTR_AVAIL_SAMP_FREQ - list available sampling frequencies * @_string: frequency string for the attribute * * Constant version **/ -#define IIO_CONST_ATTR_AVAIL_SAMP_FREQ(_string) \ +/* Deprecated */ +#define IIO_CONST_ATTR_AVAIL_SAMP_FREQ(_string) \ IIO_CONST_ATTR(available_sampling_frequency, _string) +#define IIO_CONST_ATTR_SAMP_FREQ_AVAIL(_string) \ + IIO_CONST_ATTR(sampling_frequency_available, _string) + /** * IIO_DEV_ATTR_SCAN_MODE - select a scan mode * @_mode: sysfs file mode/permissions @@ -231,6 +241,9 @@ struct iio_const_attr { #define IIO_DEV_ATTR_TEMP(_show) \ IIO_DEVICE_ATTR(temp, S_IRUGO, _show, NULL, 0) +#define IIO_DEV_ATTR_TEMP_RAW(_show) \ + IIO_DEVICE_ATTR(temp_raw, S_IRUGO, _show, NULL, 0) + /** * IIO_EVENT_SH - generic shared event handler * @_name: event name diff --git a/drivers/staging/iio/trigger/iio-trig-gpio.c b/drivers/staging/iio/trigger/iio-trig-gpio.c index 0c3bad3187f5..1da285d28632 100644 --- a/drivers/staging/iio/trigger/iio-trig-gpio.c +++ b/drivers/staging/iio/trigger/iio-trig-gpio.c @@ -13,7 +13,6 @@ * TODO: * * Add board config elements to allow specification of startup settings. - * Add configuration settings (irq type etc) */ #include <linux/kernel.h> @@ -26,12 +25,12 @@ #include "../iio.h" #include "../trigger.h" -LIST_HEAD(iio_gpio_trigger_list); -DEFINE_MUTEX(iio_gpio_trigger_list_lock); +static LIST_HEAD(iio_gpio_trigger_list); +static DEFINE_MUTEX(iio_gpio_trigger_list_lock); struct iio_gpio_trigger_info { struct mutex in_use; - int gpio; + unsigned int irq; }; /* * Need to reference count these triggers and only enable gpio interrupts @@ -58,78 +57,77 @@ static const struct attribute_group iio_gpio_trigger_attr_group = { .attrs = iio_gpio_trigger_attrs, }; -static int iio_gpio_trigger_probe(struct platform_device *dev) +static int iio_gpio_trigger_probe(struct platform_device *pdev) { - int *pdata = dev->dev.platform_data; struct iio_gpio_trigger_info *trig_info; struct iio_trigger *trig, *trig2; - int i, irq, ret = 0; - if (!pdata) { - printk(KERN_ERR "No IIO gpio trigger platform data found\n"); - goto error_ret; - } - for (i = 0;; i++) { - if (!gpio_is_valid(pdata[i])) - break; - trig = iio_allocate_trigger(); - if (!trig) { - ret = -ENOMEM; - goto error_free_completed_registrations; - } + unsigned long irqflags; + struct resource *irq_res; + int irq, ret = 0, irq_res_cnt = 0; - trig_info = kzalloc(sizeof(*trig_info), GFP_KERNEL); - if (!trig_info) { - ret = -ENOMEM; - goto error_put_trigger; - } - trig->control_attrs = &iio_gpio_trigger_attr_group; - trig->private_data = trig_info; - trig_info->gpio = pdata[i]; - trig->owner = THIS_MODULE; - trig->name = kmalloc(IIO_TRIGGER_NAME_LENGTH, GFP_KERNEL); - if (!trig->name) { - ret = -ENOMEM; - goto error_free_trig_info; + do { + irq_res = platform_get_resource(pdev, + IORESOURCE_IRQ, irq_res_cnt); + + if (irq_res == NULL) { + if (irq_res_cnt == 0) + dev_err(&pdev->dev, "No GPIO IRQs specified"); + break; } - snprintf((char *)trig->name, - IIO_TRIGGER_NAME_LENGTH, - "gpiotrig%d", - pdata[i]); - ret = gpio_request(trig_info->gpio, trig->name); - if (ret) - goto error_free_name; - - ret = gpio_direction_input(trig_info->gpio); - if (ret) - goto error_release_gpio; - - irq = gpio_to_irq(trig_info->gpio); - if (irq < 0) { - ret = irq; - goto error_release_gpio; + irqflags = (irq_res->flags & IRQF_TRIGGER_MASK) | IRQF_SHARED; + + for (irq = irq_res->start; irq <= irq_res->end; irq++) { + + trig = iio_allocate_trigger(); + if (!trig) { + ret = -ENOMEM; + goto error_free_completed_registrations; + } + + trig_info = kzalloc(sizeof(*trig_info), GFP_KERNEL); + if (!trig_info) { + ret = -ENOMEM; + goto error_put_trigger; + } + trig->control_attrs = &iio_gpio_trigger_attr_group; + trig->private_data = trig_info; + trig_info->irq = irq; + trig->owner = THIS_MODULE; + trig->name = kmalloc(IIO_TRIGGER_NAME_LENGTH, + GFP_KERNEL); + if (!trig->name) { + ret = -ENOMEM; + goto error_free_trig_info; + } + snprintf((char *)trig->name, + IIO_TRIGGER_NAME_LENGTH, + "irqtrig%d", irq); + + ret = request_irq(irq, iio_gpio_trigger_poll, + irqflags, trig->name, trig); + if (ret) { + dev_err(&pdev->dev, + "request IRQ-%d failed", irq); + goto error_free_name; + } + + ret = iio_trigger_register(trig); + if (ret) + goto error_release_irq; + + list_add_tail(&trig->alloc_list, + &iio_gpio_trigger_list); } - ret = request_irq(irq, iio_gpio_trigger_poll, - IRQF_TRIGGER_RISING, - trig->name, - trig); - if (ret) - goto error_release_gpio; + irq_res_cnt++; + } while (irq_res != NULL); - ret = iio_trigger_register(trig); - if (ret) - goto error_release_irq; - list_add_tail(&trig->alloc_list, &iio_gpio_trigger_list); - - } return 0; /* First clean up the partly allocated trigger */ error_release_irq: free_irq(irq, trig); -error_release_gpio: - gpio_free(trig_info->gpio); error_free_name: kfree(trig->name); error_free_trig_info: @@ -143,18 +141,16 @@ error_free_completed_registrations: &iio_gpio_trigger_list, alloc_list) { trig_info = trig->private_data; - free_irq(gpio_to_irq(trig_info->gpio), trig); - gpio_free(trig_info->gpio); + free_irq(gpio_to_irq(trig_info->irq), trig); kfree(trig->name); kfree(trig_info); iio_trigger_unregister(trig); } -error_ret: return ret; } -static int iio_gpio_trigger_remove(struct platform_device *dev) +static int iio_gpio_trigger_remove(struct platform_device *pdev) { struct iio_trigger *trig, *trig2; struct iio_gpio_trigger_info *trig_info; @@ -166,8 +162,7 @@ static int iio_gpio_trigger_remove(struct platform_device *dev) alloc_list) { trig_info = trig->private_data; iio_trigger_unregister(trig); - free_irq(gpio_to_irq(trig_info->gpio), trig); - gpio_free(trig_info->gpio); + free_irq(trig_info->irq, trig); kfree(trig->name); kfree(trig_info); iio_put_trigger(trig); diff --git a/drivers/staging/iio/trigger/iio-trig-periodic-rtc.c b/drivers/staging/iio/trigger/iio-trig-periodic-rtc.c index 4295bbc7b50d..4ee3ae1ef892 100644 --- a/drivers/staging/iio/trigger/iio-trig-periodic-rtc.c +++ b/drivers/staging/iio/trigger/iio-trig-periodic-rtc.c @@ -19,8 +19,8 @@ #include "../iio.h" #include "../trigger.h" -LIST_HEAD(iio_prtc_trigger_list); -DEFINE_MUTEX(iio_prtc_trigger_list_lock); +static LIST_HEAD(iio_prtc_trigger_list); +static DEFINE_MUTEX(iio_prtc_trigger_list_lock); struct iio_prtc_trigger_info { struct rtc_device *rtc; |