diff options
Diffstat (limited to 'drivers/staging/iio')
191 files changed, 31254 insertions, 21563 deletions
diff --git a/drivers/staging/iio/Documentation/dac/max517 b/drivers/staging/iio/Documentation/dac/max517 new file mode 100644 index 00000000000..e60ec2f91a7 --- /dev/null +++ b/drivers/staging/iio/Documentation/dac/max517 @@ -0,0 +1,41 @@ +Kernel driver max517 +==================== + +Supported chips: +  * Maxim MAX517, MAX518, MAX519 +    Prefix: 'max517' +    Datasheet: Publicly available at the Maxim website +               http://www.maxim-ic.com/ + +Author: +        Roland Stigge <stigge@antcom.de> + +Description +----------- + +The Maxim MAX517/518/519 is an 8-bit DAC on the I2C bus. The following table +shows the different feature sets of the variants MAX517, MAX518 and MAX519: + +Feature                              MAX517 MAX518 MAX519 +-------------------------------------------------------------------------- +One output channel                   X +Two output channels                         X      X +Simultaneous output updates                 X      X +Supply voltage as reference                 X +Separate reference input             X +Reference input for each DAC                       X + +Via the iio sysfs interface, there are three attributes available: out1_raw, +out2_raw and out12_raw. With out1_raw and out2_raw, the current output values +(0..255) of the DACs can be written to the device. out12_raw can be used to set +both output channel values simultaneously. + +With MAX517, only out1_raw is available. + +Via out1_scale (and where appropriate, out2_scale), the current scaling factor +in mV can be read. + +When the operating system goes to a power down state, the Power Down function +of the chip is activated, reducing the supply current to 4uA. + +On power-up, the device is in 0V-output state. diff --git a/drivers/staging/iio/Documentation/device.txt b/drivers/staging/iio/Documentation/device.txt index 69d9570f29f..8be32e5a0af 100644 --- a/drivers/staging/iio/Documentation/device.txt +++ b/drivers/staging/iio/Documentation/device.txt @@ -8,36 +8,66 @@ The crucial structure for device drivers in iio is iio_dev.  First allocate one using: -struct iio_dev *indio_dev = iio_allocate_device(); +struct iio_dev *indio_dev = iio_device_alloc(sizeof(struct chip_state)); +where chip_state is a structure of local state data for this instance of +the chip. -Then fill in the following: - -indio_dev->dev.parent -  the struct device associated with the underlying hardware. - -indio_dev->num_interrupt_lines -   number of event triggering hardware lines the device has. +That data can be accessed using iio_priv(struct iio_dev *). -indio_dev->event_attrs -   attributes used to enable / disable hardware events - note the -   attributes are embedded in iio_event_attr structures with an -   associated iio_event_handler which may or may note be shared. -   If num_interrupt_lines = 0, then no need to fill this in. - -indio_dev->attrs -   general attributes such as polled access to device channels. +Then fill in the following: -indio_dev->dev_data -   private device specific data. +- indio_dev->dev.parent +	Struct device associated with the underlying hardware. +- indio_dev->name +	Name of the device being driven - made available as the name +	attribute in sysfs. -indio_dev->driver_module -   typically set to THIS_MODULE. Used to specify ownership of some -   iio created resources. +- indio_dev->info +	pointer to a structure with elements that tend to be fixed for +	large sets of different parts supported by a given driver. +	This contains: +	* info->driver_module: +		Set to THIS_MODULE. Used to ensure correct ownership +		of various resources allocate by the core. +	* info->event_attrs: +		Attributes used to enable / disable hardware events. +	* info->attrs: +		General device attributes. Typically used for the weird +		and the wonderful bits not covered by the channel specification. +	* info->read_raw: +		Raw data reading function. Used for both raw channel access +		and for associate parameters such as offsets and scales. +	* info->write_raw: +		Raw value writing function. Used for writable device values such +		as DAC values and calibbias. +	* info->read_event_config: +		Typically only set if there are some interrupt lines.  This +		is used to read if an on sensor event detector is enabled. +	* info->write_event_config: +		Enable / disable an on sensor event detector. +	* info->read_event_value: +		Read value associated with on sensor event detectors. Note that +		the meaning of the returned value is dependent on the event +		type. +	* info->write_event_value: +		Write the value associated with on sensor event detectors. E.g. +		a threshold above which an interrupt occurs.  Note that the +		meaning of the value to be set is event type dependant. -indio_dev->modes -   whether direct access and / or ring buffer access is supported. +- indio_dev->modes: +	Specify whether direct access and / or ring buffer access is supported. +- indio_dev->buffer: +	An optional associated buffer. +- indio_dev->pollfunc: +	Poll function related elements. This controls what occurs when a trigger +	to which this device is attached sends an event. +- indio_dev->channels: +	Specification of device channels. Most attributes etc. are built +	from this spec. +- indio_dev->num_channels: +	How many channels are there? -Once these are set up, a call to iio_device_register(indio_dev), +Once these are set up, a call to iio_device_register(indio_dev)  will register the device with the iio core.  Worth noting here is that, if a ring buffer is to be used, it can be @@ -46,4 +76,4 @@ be registered afterwards (otherwise the whole parentage of devices  gets confused)  On remove, iio_device_unregister(indio_dev) will remove the device from -the core, and iio_free_device will clean up. +the core, and iio_device_free(indio_dev) will clean up. diff --git a/drivers/staging/iio/Documentation/generic_buffer.c b/drivers/staging/iio/Documentation/generic_buffer.c index df23aeb9d52..40d0ecac047 100644 --- a/drivers/staging/iio/Documentation/generic_buffer.c +++ b/drivers/staging/iio/Documentation/generic_buffer.c @@ -18,6 +18,8 @@   *   */ +#define _GNU_SOURCE +  #include <unistd.h>  #include <dirent.h>  #include <fcntl.h> @@ -26,15 +28,17 @@  #include <sys/stat.h>  #include <sys/dir.h>  #include <linux/types.h> +#include <string.h> +#include <poll.h> +#include <endian.h> +#include <getopt.h> +#include <inttypes.h>  #include "iio_utils.h" -const int buf_len = 128; -const int num_loops = 2; -  /**   * size_from_channelarray() - calculate the storage size of a scan - * @channels: the channel info array - * @num_channels: size of the channel info array + * @channels:		the channel info array + * @num_channels:	number of channels   *   * Has the side effect of filling the channels[i].location values used   * in processing the buffer output. @@ -55,59 +59,77 @@ int size_from_channelarray(struct iio_channel_info *channels, int num_channels)  	return bytes;  } +void print2byte(int input, struct iio_channel_info *info) +{ +	/* First swap if incorrect endian */ +	if (info->be) +		input = be16toh((uint16_t)input); +	else +		input = le16toh((uint16_t)input); + +	/* +	 * Shift before conversion to avoid sign extension +	 * of left aligned data +	 */ +	input = input >> info->shift; +	if (info->is_signed) { +		int16_t val = input; +		val &= (1 << info->bits_used) - 1; +		val = (int16_t)(val << (16 - info->bits_used)) >> +			(16 - info->bits_used); +		printf("%05f ", ((float)val + info->offset)*info->scale); +	} else { +		uint16_t val = input; +		val &= (1 << info->bits_used) - 1; +		printf("%05f ", ((float)val + info->offset)*info->scale); +	} +}  /**   * process_scan() - print out the values in SI units   * @data:		pointer to the start of the scan - * @infoarray:		information about the channels. Note + * @channels:		information about the channels. Note   *  size_from_channelarray must have been called first to fill the   *  location offsets. - * @num_channels:	the number of active channels + * @num_channels:	number of channels   **/  void process_scan(char *data, -		  struct iio_channel_info *infoarray, +		  struct iio_channel_info *channels,  		  int num_channels)  {  	int k;  	for (k = 0; k < num_channels; k++) -		switch (infoarray[k].bytes) { +		switch (channels[k].bytes) {  			/* only a few cases implemented so far */  		case 2: -			if (infoarray[k].is_signed) { -				int16_t val = *(int16_t *) -					(data -					 + infoarray[k].location); -				if ((val >> infoarray[k].bits_used) & 1) -					val = (val & infoarray[k].mask) | -						~infoarray[k].mask; -				printf("%05f ", ((float)val + -						 infoarray[k].offset)* -				       infoarray[k].scale); -			} else { -				uint16_t val = *(uint16_t *) -					(data + -					 infoarray[k].location); -				val = (val & infoarray[k].mask); +			print2byte(*(uint16_t *)(data + channels[k].location), +				   &channels[k]); +			break; +		case 4: +			if (!channels[k].is_signed) { +				uint32_t val = *(uint32_t *) +					(data + channels[k].location);  				printf("%05f ", ((float)val + -						 infoarray[k].offset)* -				       infoarray[k].scale); +						 channels[k].offset)* +				       channels[k].scale); +  			}  			break;  		case 8: -			if (infoarray[k].is_signed) { +			if (channels[k].is_signed) {  				int64_t val = *(int64_t *)  					(data + -					 infoarray[k].location); -				if ((val >> infoarray[k].bits_used) & 1) -					val = (val & infoarray[k].mask) | -						~infoarray[k].mask; +					 channels[k].location); +				if ((val >> channels[k].bits_used) & 1) +					val = (val & channels[k].mask) | +						~channels[k].mask;  				/* special case for timestamp */ -				if (infoarray[k].scale == 1.0f && -				    infoarray[k].offset == 0.0f) -					printf(" %lld", val); +				if (channels[k].scale == 1.0f && +				    channels[k].offset == 0.0f) +					printf("%" PRId64 " ", val);  				else  					printf("%05f ", ((float)val + -							 infoarray[k].offset)* -					       infoarray[k].scale); +							 channels[k].offset)* +					       channels[k].scale);  			}  			break;  		default: @@ -118,9 +140,11 @@ void process_scan(char *data,  int main(int argc, char **argv)  { -	int ret, c, i, j, toread; +	unsigned long num_loops = 2; +	unsigned long timedelay = 1000000; +	unsigned long buf_len = 128; -	FILE *fp_ev; +	int ret, c, i, j, toread;  	int fp;  	int num_channels; @@ -129,15 +153,16 @@ int main(int argc, char **argv)  	int datardytrigger = 1;  	char *data; -	size_t read_size; -	struct iio_event_data dat; +	ssize_t read_size;  	int dev_num, trig_num; -	char *buffer_access, *buffer_event; +	char *buffer_access;  	int scan_size; +	int noevents = 0; +	char *dummy; -	struct iio_channel_info *infoarray; +	struct iio_channel_info *channels; -	while ((c = getopt(argc, argv, "t:n:")) != -1) { +	while ((c = getopt(argc, argv, "l:w:c:et:n:")) != -1) {  		switch (c) {  		case 'n':  			device_name = optarg; @@ -146,13 +171,28 @@ int main(int argc, char **argv)  			trigger_name = optarg;  			datardytrigger = 0;  			break; +		case 'e': +			noevents = 1; +			break; +		case 'c': +			num_loops = strtoul(optarg, &dummy, 10); +			break; +		case 'w': +			timedelay = strtoul(optarg, &dummy, 10); +			break; +		case 'l': +			buf_len = strtoul(optarg, &dummy, 10); +			break;  		case '?':  			return -1;  		}  	} +	if (device_name == NULL) +		return -1; +  	/* Find the device requested */ -	dev_num = find_type_by_name(device_name, "device"); +	dev_num = find_type_by_name(device_name, "iio:device");  	if (dev_num < 0) {  		printf("Failed to find the %s\n", device_name);  		ret = -ENODEV; @@ -160,10 +200,10 @@ int main(int argc, char **argv)  	}  	printf("iio device number being used is %d\n", dev_num); -	asprintf(&dev_dir_name, "%sdevice%d", iio_dir, dev_num); +	asprintf(&dev_dir_name, "%siio:device%d", iio_dir, dev_num);  	if (trigger_name == NULL) {  		/* -		 * Build the trigger name. If it is device associated it's +		 * Build the trigger name. If it is device associated its  		 * name is <device_name>_dev[n] where n matches the device  		 * number found above  		 */ @@ -188,9 +228,10 @@ int main(int argc, char **argv)  	 * Parse the files in scan_elements to identify what channels are  	 * present  	 */ -	ret = build_channel_array(dev_dir_name, &infoarray, &num_channels); +	ret = build_channel_array(dev_dir_name, &channels, &num_channels);  	if (ret) { -		printf("Problem reading scan element information \n"); +		printf("Problem reading scan element information\n"); +		printf("diag %s\n", dev_dir_name);  		goto error_free_triggername;  	} @@ -199,13 +240,14 @@ int main(int argc, char **argv)  	 * 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); +	ret = asprintf(&buf_dir_name, +		       "%siio:device%d/buffer", iio_dir, dev_num);  	if (ret < 0) {  		ret = -ENOMEM;  		goto error_free_triggername;  	}  	printf("%s %s\n", dev_dir_name, trigger_name); -	/* Set the device trigger to be the data rdy trigger found above */ +	/* Set the device trigger to be the data ready trigger found above */  	ret = write_sysfs_string_and_verify("trigger/current_trigger",  					dev_dir_name,  					trigger_name); @@ -223,59 +265,43 @@ int main(int argc, char **argv)  	ret = write_sysfs_int("enable", buf_dir_name, 1);  	if (ret < 0)  		goto error_free_buf_dir_name; -	scan_size = size_from_channelarray(infoarray, num_channels); +	scan_size = size_from_channelarray(channels, num_channels);  	data = malloc(scan_size*buf_len);  	if (!data) {  		ret = -ENOMEM;  		goto error_free_buf_dir_name;  	} -	ret = asprintf(&buffer_access, -		       "/dev/device%d:buffer0:access0", -		       dev_num); +	ret = asprintf(&buffer_access, "/dev/iio:device%d", 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_buffer_access; -	}  	/* Attempt to open non blocking the access dev */  	fp = open(buffer_access, O_RDONLY | O_NONBLOCK); -	if (fp == -1) { /*If it isn't there make the node */ +	if (fp == -1) { /* If it isn't there make the node */  		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(buffer_event, "rb"); -	if (fp_ev == NULL) { -		printf("Failed to open %s\n", buffer_event); -		ret = -errno; -		goto error_close_buffer_access; +		goto error_free_buffer_access;  	}  	/* Wait for events 10 times */  	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: +		if (!noevents) { +			struct pollfd pfd = { +				.fd = fp, +				.events = POLLIN, +			}; + +			poll(&pfd, 1, -1);  			toread = buf_len; -			break; -		case IIO_EVENT_CODE_RING_75_FULL: -			toread = buf_len*3/4; -			break; -		case IIO_EVENT_CODE_RING_50_FULL: -			toread = buf_len/2; -			break; -		default: -			printf("Unexpecteded event code\n"); -			continue; + +		} else { +			usleep(timedelay); +			toread = 64;  		} +  		read_size = read(fp,  				 data,  				 toread*scan_size); @@ -285,29 +311,25 @@ int main(int argc, char **argv)  		}  		for (i = 0; i < read_size/scan_size; i++)  			process_scan(data + scan_size*i, -				     infoarray, +				     channels,  				     num_channels);  	} -	/* Stop the ring buffer */ +	/* Stop the buffer */  	ret = write_sysfs_int("enable", buf_dir_name, 0);  	if (ret < 0) -		goto error_close_buffer_event; +		goto error_close_buffer_access; -	/* Disconnect from the trigger - just write a dummy name.*/ +	/* Disconnect 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);  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: diff --git a/drivers/staging/iio/Documentation/iio_event_monitor.c b/drivers/staging/iio/Documentation/iio_event_monitor.c new file mode 100644 index 00000000000..3a9b0008740 --- /dev/null +++ b/drivers/staging/iio/Documentation/iio_event_monitor.c @@ -0,0 +1,255 @@ +/* Industrialio event test code. + * + * Copyright (c) 2011-2012 Lars-Peter Clausen <lars@metafoo.de> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is primarily intended as an example application. + * Reads the current buffer setup from sysfs and starts a short capture + * from the specified device, pretty printing the result after appropriate + * conversion. + * + * Usage: + *	iio_event_monitor <device_name> + * + */ + +#define _GNU_SOURCE + +#include <unistd.h> +#include <stdbool.h> +#include <stdio.h> +#include <errno.h> +#include <string.h> +#include <poll.h> +#include <fcntl.h> +#include <sys/ioctl.h> +#include "iio_utils.h" +#include <linux/iio/events.h> + +static const char * const iio_chan_type_name_spec[] = { +	[IIO_VOLTAGE] = "voltage", +	[IIO_CURRENT] = "current", +	[IIO_POWER] = "power", +	[IIO_ACCEL] = "accel", +	[IIO_ANGL_VEL] = "anglvel", +	[IIO_MAGN] = "magn", +	[IIO_LIGHT] = "illuminance", +	[IIO_INTENSITY] = "intensity", +	[IIO_PROXIMITY] = "proximity", +	[IIO_TEMP] = "temp", +	[IIO_INCLI] = "incli", +	[IIO_ROT] = "rot", +	[IIO_ANGL] = "angl", +	[IIO_TIMESTAMP] = "timestamp", +	[IIO_CAPACITANCE] = "capacitance", +	[IIO_ALTVOLTAGE] = "altvoltage", +}; + +static const char * const iio_ev_type_text[] = { +	[IIO_EV_TYPE_THRESH] = "thresh", +	[IIO_EV_TYPE_MAG] = "mag", +	[IIO_EV_TYPE_ROC] = "roc", +	[IIO_EV_TYPE_THRESH_ADAPTIVE] = "thresh_adaptive", +	[IIO_EV_TYPE_MAG_ADAPTIVE] = "mag_adaptive", +}; + +static const char * const iio_ev_dir_text[] = { +	[IIO_EV_DIR_EITHER] = "either", +	[IIO_EV_DIR_RISING] = "rising", +	[IIO_EV_DIR_FALLING] = "falling" +}; + +static const char * const iio_modifier_names[] = { +	[IIO_MOD_X] = "x", +	[IIO_MOD_Y] = "y", +	[IIO_MOD_Z] = "z", +	[IIO_MOD_LIGHT_BOTH] = "both", +	[IIO_MOD_LIGHT_IR] = "ir", +	[IIO_MOD_ROOT_SUM_SQUARED_X_Y] = "sqrt(x^2+y^2)", +	[IIO_MOD_SUM_SQUARED_X_Y_Z] = "x^2+y^2+z^2", +	[IIO_MOD_LIGHT_CLEAR] = "clear", +	[IIO_MOD_LIGHT_RED] = "red", +	[IIO_MOD_LIGHT_GREEN] = "green", +	[IIO_MOD_LIGHT_BLUE] = "blue", +}; + +static bool event_is_known(struct iio_event_data *event) +{ +	enum iio_chan_type type = IIO_EVENT_CODE_EXTRACT_CHAN_TYPE(event->id); +	enum iio_modifier mod = IIO_EVENT_CODE_EXTRACT_MODIFIER(event->id); +	enum iio_event_type ev_type = IIO_EVENT_CODE_EXTRACT_TYPE(event->id); +	enum iio_event_direction dir = IIO_EVENT_CODE_EXTRACT_DIR(event->id); + +	switch (type) { +	case IIO_VOLTAGE: +	case IIO_CURRENT: +	case IIO_POWER: +	case IIO_ACCEL: +	case IIO_ANGL_VEL: +	case IIO_MAGN: +	case IIO_LIGHT: +	case IIO_INTENSITY: +	case IIO_PROXIMITY: +	case IIO_TEMP: +	case IIO_INCLI: +	case IIO_ROT: +	case IIO_ANGL: +	case IIO_TIMESTAMP: +	case IIO_CAPACITANCE: +	case IIO_ALTVOLTAGE: +		break; +	default: +		return false; +	} + +	switch (mod) { +	case IIO_NO_MOD: +	case IIO_MOD_X: +	case IIO_MOD_Y: +	case IIO_MOD_Z: +	case IIO_MOD_LIGHT_BOTH: +	case IIO_MOD_LIGHT_IR: +	case IIO_MOD_ROOT_SUM_SQUARED_X_Y: +	case IIO_MOD_SUM_SQUARED_X_Y_Z: +	case IIO_MOD_LIGHT_CLEAR: +	case IIO_MOD_LIGHT_RED: +	case IIO_MOD_LIGHT_GREEN: +	case IIO_MOD_LIGHT_BLUE: +		break; +	default: +		return false; +	} + +	switch (ev_type) { +	case IIO_EV_TYPE_THRESH: +	case IIO_EV_TYPE_MAG: +	case IIO_EV_TYPE_ROC: +	case IIO_EV_TYPE_THRESH_ADAPTIVE: +	case IIO_EV_TYPE_MAG_ADAPTIVE: +		break; +	default: +		return false; +	} + +	switch (dir) { +	case IIO_EV_DIR_EITHER: +	case IIO_EV_DIR_RISING: +	case IIO_EV_DIR_FALLING: +		break; +	default: +		return false; +	} + +	return true; +} + +static void print_event(struct iio_event_data *event) +{ +	enum iio_chan_type type = IIO_EVENT_CODE_EXTRACT_CHAN_TYPE(event->id); +	enum iio_modifier mod = IIO_EVENT_CODE_EXTRACT_MODIFIER(event->id); +	enum iio_event_type ev_type = IIO_EVENT_CODE_EXTRACT_TYPE(event->id); +	enum iio_event_direction dir = IIO_EVENT_CODE_EXTRACT_DIR(event->id); +	int chan = IIO_EVENT_CODE_EXTRACT_CHAN(event->id); +	int chan2 = IIO_EVENT_CODE_EXTRACT_CHAN2(event->id); +	bool diff = IIO_EVENT_CODE_EXTRACT_DIFF(event->id); + +	if (!event_is_known(event)) { +		printf("Unknown event: time: %lld, id: %llx\n", +				event->timestamp, event->id); +		return; +	} + +	printf("Event: time: %lld, ", event->timestamp); + +	if (mod != IIO_NO_MOD) { +		printf("type: %s(%s), ", +			iio_chan_type_name_spec[type], +			iio_modifier_names[mod]); +	} else { +		printf("type: %s, ", +			iio_chan_type_name_spec[type]); +	} + +	if (diff && chan >= 0 && chan2 >= 0) +		printf("channel: %d-%d, ", chan, chan2); +	else if (chan >= 0) +		printf("channel: %d, ", chan); + +	printf("evtype: %s, direction: %s\n", +		iio_ev_type_text[ev_type], +		iio_ev_dir_text[dir]); +} + +int main(int argc, char **argv) +{ +	struct iio_event_data event; +	const char *device_name; +	char *chrdev_name; +	int ret; +	int dev_num; +	int fd, event_fd; + +	if (argc <= 1) { +		printf("Usage: %s <device_name>\n", argv[0]); +		return -1; +	} + +	device_name = argv[1]; + +	dev_num = find_type_by_name(device_name, "iio:device"); +	if (dev_num >= 0) { +		printf("Found IIO device with name %s with device number %d\n", +			device_name, dev_num); +		ret = asprintf(&chrdev_name, "/dev/iio:device%d", dev_num); +		if (ret < 0) { +			ret = -ENOMEM; +			goto error_ret; +		} +	} else { +		/* If we can't find a IIO device by name assume device_name is a +		   IIO chrdev */ +		chrdev_name = strdup(device_name); +	} + +	fd = open(chrdev_name, 0); +	if (fd == -1) { +		fprintf(stdout, "Failed to open %s\n", chrdev_name); +		ret = -errno; +		goto error_free_chrdev_name; +	} + +	ret = ioctl(fd, IIO_GET_EVENT_FD_IOCTL, &event_fd); + +	close(fd); + +	if (ret == -1 || event_fd == -1) { +		fprintf(stdout, "Failed to retrieve event fd\n"); +		ret = -errno; +		goto error_free_chrdev_name; +	} + +	while (true) { +		ret = read(event_fd, &event, sizeof(event)); +		if (ret == -1) { +			if (errno == EAGAIN) { +				printf("nothing available\n"); +				continue; +			} else { +				perror("Failed to read event from device"); +				ret = -errno; +				break; +			} +		} + +		print_event(&event); +	} + +	close(event_fd); +error_free_chrdev_name: +	free(chrdev_name); +error_ret: +	return ret; +} diff --git a/drivers/staging/iio/Documentation/iio_utils.h b/drivers/staging/iio/Documentation/iio_utils.h index 03724246b95..a9cfc06edb0 100644 --- a/drivers/staging/iio/Documentation/iio_utils.h +++ b/drivers/staging/iio/Documentation/iio_utils.h @@ -7,40 +7,28 @@   * the Free Software Foundation.   */ -/* Made up value to limit allocation sizes */  #include <string.h>  #include <stdlib.h>  #include <ctype.h>  #include <stdio.h>  #include <stdint.h> +#include <dirent.h> +#include <errno.h> +/* Made up value to limit allocation sizes */  #define IIO_MAX_NAME_LENGTH 30 -#define IIO_EV_CLASS_BUFFER		0 -#define IIO_BUFFER_EVENT_CODE(code)		\ -	(IIO_EV_CLASS_BUFFER | (code << 8)) - -#define IIO_EVENT_CODE_RING_50_FULL	IIO_BUFFER_EVENT_CODE(0) -#define IIO_EVENT_CODE_RING_75_FULL	IIO_BUFFER_EVENT_CODE(1) -#define IIO_EVENT_CODE_RING_100_FULL	IIO_BUFFER_EVENT_CODE(2) - - -#define FORMAT_SCAN_ELEMENTS_DIR "%s:buffer0/scan_elements" +#define FORMAT_SCAN_ELEMENTS_DIR "%s/scan_elements"  #define FORMAT_TYPE_FILE "%s_type"  const char *iio_dir = "/sys/bus/iio/devices/"; -struct iio_event_data { -	int id; -	__s64 timestamp; -}; -  /**   * iioutils_break_up_name() - extract generic name from full channel name   * @full_name: the full channel name   * @generic_name: the output generic channel name   **/ -static int iioutils_break_up_name(const char *full_name, +inline int iioutils_break_up_name(const char *full_name,  				  char **generic_name)  {  	char *current; @@ -51,7 +39,7 @@ static int iioutils_break_up_name(const char *full_name,  	w = working;  	r = working; -	while(*r != '\0') { +	while (*r != '\0') {  		if (!isdigit(*r)) {  			*w = *r;  			w++; @@ -85,9 +73,10 @@ struct iio_channel_info {  	unsigned index;  	unsigned bytes;  	unsigned bits_used; +	unsigned shift;  	uint64_t mask; +	unsigned be;  	unsigned is_signed; -	unsigned enabled;  	unsigned location;  }; @@ -96,6 +85,7 @@ struct iio_channel_info {   * @is_signed: output whether channel is signed   * @bytes: output how many bytes the channel storage occupies   * @mask: output a bit mask for the raw data + * @be: big endian   * @device_dir: the iio device directory   * @name: the channel name   * @generic_name: the channel type name @@ -103,7 +93,9 @@ struct iio_channel_info {  inline int iioutils_get_type(unsigned *is_signed,  			     unsigned *bytes,  			     unsigned *bits_used, +			     unsigned *shift,  			     uint64_t *mask, +			     unsigned *be,  			     const char *device_dir,  			     const char *name,  			     const char *generic_name) @@ -112,8 +104,8 @@ inline int iioutils_get_type(unsigned *is_signed,  	int ret;  	DIR *dp;  	char *scan_el_dir, *builtname, *builtname_generic, *filename = 0; -	char signchar; -	unsigned sizeint, padint; +	char signchar, endianchar; +	unsigned padint;  	const struct dirent *ent;  	ret = asprintf(&scan_el_dir, FORMAT_SCAN_ELEMENTS_DIR, device_dir); @@ -156,10 +148,21 @@ inline int iioutils_get_type(unsigned *is_signed,  				ret = -errno;  				goto error_free_filename;  			} -			fscanf(sysfsfp, -			       "%c%u/%u", &signchar, bits_used, &padint); + +			ret = fscanf(sysfsfp, +				     "%ce:%c%u/%u>>%u", +				     &endianchar, +				     &signchar, +				     bits_used, +				     &padint, shift); +			if (ret < 0) { +				printf("failed to pass scan type description\n"); +				ret = -errno; +				goto error_close_sysfsfp; +			} +			*be = (endianchar == 'b');  			*bytes = padint / 8; -			if (sizeint == 64) +			if (*bits_used == 64)  				*mask = ~0;  			else  				*mask = (1 << *bits_used) - 1; @@ -167,7 +170,15 @@ inline int iioutils_get_type(unsigned *is_signed,  				*is_signed = 1;  			else  				*is_signed = 0; +			fclose(sysfsfp); +			free(filename); + +			filename = 0; +			sysfsfp = 0;  		} +error_close_sysfsfp: +	if (sysfsfp) +		fclose(sysfsfp);  error_free_filename:  	if (filename)  		free(filename); @@ -242,6 +253,26 @@ error_ret:  	return ret;  } +/** + * bsort_channel_array_by_index() - reorder so that the array is in index order + * + **/ + +inline void bsort_channel_array_by_index(struct iio_channel_info **ci_array, +					 int cnt) +{ + +	struct iio_channel_info temp; +	int x, y; + +	for (x = 0; x < cnt; x++) +		for (y = 0; y < (cnt - 1); y++) +			if ((*ci_array)[y].index > (*ci_array)[y+1].index) { +				temp = (*ci_array)[y + 1]; +				(*ci_array)[y + 1] = (*ci_array)[y]; +				(*ci_array)[y] = temp; +			} +}  /**   * build_channel_array() - function to figure out what channels are present @@ -254,7 +285,7 @@ inline int build_channel_array(const char *device_dir,  {  	DIR *dp;  	FILE *sysfsfp; -	int count = 0, temp, i; +	int count, i;  	struct iio_channel_info *current;  	int ret;  	const struct dirent *ent; @@ -287,21 +318,23 @@ inline int build_channel_array(const char *device_dir,  				free(filename);  				goto error_close_dir;  			} -			fscanf(sysfsfp, "%u", &ret); +			fscanf(sysfsfp, "%i", &ret);  			if (ret == 1)  				(*counter)++;  			fclose(sysfsfp);  			free(filename);  		} -	*ci_array = malloc(sizeof(**ci_array)*(*counter)); +	*ci_array = malloc(sizeof(**ci_array) * (*counter));  	if (*ci_array == NULL) {  		ret = -ENOMEM;  		goto error_close_dir;  	}  	seekdir(dp, 0); +	count = 0;  	while (ent = readdir(dp), ent != NULL) {  		if (strcmp(ent->d_name + strlen(ent->d_name) - strlen("_en"),  			   "_en") == 0) { +			int current_enabled = 0;  			current = &(*ci_array)[count++];  			ret = asprintf(&filename,  				       "%s/%s", scan_el_dir, ent->d_name); @@ -317,9 +350,15 @@ inline int build_channel_array(const char *device_dir,  				ret = -errno;  				goto error_cleanup_array;  			} -			fscanf(sysfsfp, "%u", ¤t->enabled); +			fscanf(sysfsfp, "%i", ¤t_enabled);  			fclose(sysfsfp); -			free(filename); + +			if (!current_enabled) { +				free(filename); +				count--; +				continue; +			} +  			current->scale = 1.0;  			current->offset = 0;  			current->name = strndup(ent->d_name, @@ -368,37 +407,23 @@ inline int build_channel_array(const char *device_dir,  			ret = iioutils_get_type(¤t->is_signed,  						¤t->bytes,  						¤t->bits_used, +						¤t->shift,  						¤t->mask, +						¤t->be,  						device_dir,  						current->name,  						current->generic_name);  		}  	} -	/* reorder so that the array is in index order*/ -	current = malloc(sizeof(**ci_array)**counter); -	if (current == NULL) { -		ret = -ENOMEM; -		goto error_cleanup_array; -	} +  	closedir(dp); -	count = 0; -	temp = 0; -	while (count < *counter) -		for (i = 0; i < *counter; i++) -			if ((*ci_array)[i].index == temp) { -				memcpy(¤t[count++], -				       &(*ci_array)[i], -				       sizeof(*current)); -				temp++; -				break; -			} -	free(*ci_array); -	*ci_array = current; +	/* reorder so that the array is in index order */ +	bsort_channel_array_by_index(ci_array, *counter);  	return 0;  error_cleanup_array: -	for (i = count - 1;  i >= 0; i++) +	for (i = count - 1;  i >= 0; i--)  		free((*ci_array)[i].name);  	free(*ci_array);  error_close_dir: @@ -428,7 +453,7 @@ inline int find_type_by_name(const char *name, const char *type)  	dp = opendir(iio_dir);  	if (dp == NULL) { -		printf("No industrialio devices available"); +		printf("No industrialio devices available\n");  		return -ENODEV;  	} @@ -448,29 +473,36 @@ inline int find_type_by_name(const char *name, const char *type)  						+ strlen(type)  						+ numstrlen  						+ 6); -				if (filename == NULL) +				if (filename == NULL) { +					closedir(dp);  					return -ENOMEM; +				}  				sprintf(filename, "%s%s%d/name",  					iio_dir,  					type,  					number);  				nameFile = fopen(filename, "r"); -				if (!nameFile) +				if (!nameFile) { +					free(filename);  					continue; +				}  				free(filename);  				fscanf(nameFile, "%s", thisname); -				if (strcmp(name, thisname) == 0) -					return number;  				fclose(nameFile); +				if (strcmp(name, thisname) == 0) { +					closedir(dp); +					return number; +				}  			}  		}  	} +	closedir(dp);  	return -ENODEV;  }  inline int _write_sysfs_int(char *filename, char *basedir, int val, int verify)  { -	int ret; +	int ret = 0;  	FILE *sysfsfp;  	int test;  	char *temp = malloc(strlen(basedir) + strlen(filename) + 2); @@ -493,6 +525,7 @@ inline int _write_sysfs_int(char *filename, char *basedir, int val, int verify)  			goto error_free;  		}  		fscanf(sysfsfp, "%d", &test); +		fclose(sysfsfp);  		if (test != val) {  			printf("Possible failure in int write %d to %s%s\n",  				val, @@ -542,10 +575,11 @@ int _write_sysfs_string(char *filename, char *basedir, char *val, int verify)  			goto error_free;  		}  		fscanf(sysfsfp, "%s", temp); +		fclose(sysfsfp);  		if (strcmp(temp, val) != 0) {  			printf("Possible failure in string write of %s "  				"Should be %s " -				"writen to %s\%s\n", +				"written to %s\%s\n",  				temp,  				val,  				basedir, @@ -618,3 +652,25 @@ error_free:  	free(temp);  	return ret;  } + +read_sysfs_string(const char *filename, const char *basedir, char *str) +{ +	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, "%s\n", str); +	fclose(sysfsfp); +error_free: +	free(temp); +	return ret; +} diff --git a/drivers/staging/iio/Documentation/inkernel.txt b/drivers/staging/iio/Documentation/inkernel.txt new file mode 100644 index 00000000000..ab528409bba --- /dev/null +++ b/drivers/staging/iio/Documentation/inkernel.txt @@ -0,0 +1,58 @@ +Industrial I/O Subsystem in kernel consumers. + +The IIO subsystem can act as a layer under other elements of the kernel +providing a means of obtaining ADC type readings or of driving DAC type +signals.  The functionality supported will grow as use cases arise. + +Describing the channel mapping (iio/machine.h) + +Channel associations are described using: + +struct iio_map { +	const char *adc_channel_label; +	const char *consumer_dev_name; +	const char *consumer_channel; +}; + +adc_channel_label identifies the channel on the IIO device by being +matched against the datasheet_name field of the iio_chan_spec. + +consumer_dev_name allows identification of the consumer device. +This are then used to find the channel mapping from the consumer device (see +below). + +Finally consumer_channel is a string identifying the channel to the consumer. +(Perhaps 'battery_voltage' or similar). + +An array of these structures is then passed to the IIO driver. + +Supporting in kernel interfaces in the driver (driver.h) + +The driver must provide datasheet_name values for its channels and +must pass the iio_map structures and a pointer to its own iio_dev structure + on to the core via a call to iio_map_array_register.  On removal, +iio_map_array_unregister reverses this process. + +The result of this is that the IIO core now has all the information needed +to associate a given channel with the consumer requesting it. + +Acting as an IIO consumer (consumer.h) + +The consumer first has to obtain an iio_channel structure from the core +by calling iio_channel_get().  The correct channel is identified by: + +* matching dev or dev_name against consumer_dev and consumer_dev_name +* matching consumer_channel against consumer_channel in the map + +There are then a number of functions that can be used to get information +about this channel such as it's current reading. + +e.g. +iio_read_channel_raw() - get a reading +iio_get_channel_type() - get the type of channel + +There is also provision for retrieving all of the channels associated +with a given consumer.  This is useful for generic drivers such as +iio_hwmon where the number and naming of channels is not known by the +consumer driver.  To do this, use iio_channel_get_all. + diff --git a/drivers/staging/iio/Documentation/light/sysfs-bus-iio-light-tsl2583 b/drivers/staging/iio/Documentation/light/sysfs-bus-iio-light-tsl2583 new file mode 100644 index 00000000000..470f7ad9c07 --- /dev/null +++ b/drivers/staging/iio/Documentation/light/sysfs-bus-iio-light-tsl2583 @@ -0,0 +1,6 @@ +What:		/sys/bus/iio/devices/device[n]/in_illuminance0_calibrate +KernelVersion:	2.6.37 +Contact:	linux-iio@vger.kernel.org +Description: +		This property causes an internal calibration of the als gain trim +		value which is later used in calculating illuminance in lux. diff --git a/drivers/staging/iio/Documentation/light/sysfs-bus-iio-light-tsl2x7x b/drivers/staging/iio/Documentation/light/sysfs-bus-iio-light-tsl2x7x new file mode 100644 index 00000000000..b2798b258bf --- /dev/null +++ b/drivers/staging/iio/Documentation/light/sysfs-bus-iio-light-tsl2x7x @@ -0,0 +1,13 @@ +What:		/sys/bus/iio/devices/device[n]/in_illuminance0_calibrate +KernelVersion:	3.3-rc1 +Contact:	linux-iio@vger.kernel.org +Description: +		Causes an internal calibration of the als gain trim +		value which is later used in calculating illuminance in lux. + +What:		/sys/bus/iio/devices/device[n]/in_proximity0_calibrate +KernelVersion:	3.3-rc1 +Contact:	linux-iio@vger.kernel.org +Description: +		Causes a recalculation and adjustment to the +		proximity_thresh_rising_value. diff --git a/drivers/staging/iio/Documentation/lsiio.c b/drivers/staging/iio/Documentation/lsiio.c new file mode 100644 index 00000000000..24ae9694eb4 --- /dev/null +++ b/drivers/staging/iio/Documentation/lsiio.c @@ -0,0 +1,157 @@ +/* + * Industrial I/O utilities - lsiio.c + * + * Copyright (c) 2010 Manuel Stahl <manuel.stahl@iis.fraunhofer.de> + * + * 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 <string.h> +#include <dirent.h> +#include <stdio.h> +#include <errno.h> +#include <stdint.h> +#include <stdlib.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/dir.h> +#include "iio_utils.h" + + +static enum verbosity { +	VERBLEVEL_DEFAULT,	/* 0 gives lspci behaviour */ +	VERBLEVEL_SENSORS,	/* 1 lists sensors */ +} verblevel = VERBLEVEL_DEFAULT; + +const char *type_device = "iio:device"; +const char *type_trigger = "trigger"; + + +static inline int check_prefix(const char *str, const char *prefix) +{ +	return strlen(str) > strlen(prefix) && +		strncmp(str, prefix, strlen(prefix)) == 0; +} + +static inline int check_postfix(const char *str, const char *postfix) +{ +	return strlen(str) > strlen(postfix) && +		strcmp(str + strlen(str) - strlen(postfix), postfix) == 0; +} + +static int dump_channels(const char *dev_dir_name) +{ +	DIR *dp; +	const struct dirent *ent; +	dp = opendir(dev_dir_name); +	if (dp == NULL) +		return -errno; +	while (ent = readdir(dp), ent != NULL) +		if (check_prefix(ent->d_name, "in_") && +		    check_postfix(ent->d_name, "_raw")) { +			printf("   %-10s\n", ent->d_name); +		} + +	return 0; +} + +static int dump_one_device(const char *dev_dir_name) +{ +	char name[IIO_MAX_NAME_LENGTH]; +	int dev_idx; + +	sscanf(dev_dir_name + strlen(iio_dir) + strlen(type_device), +			"%i", &dev_idx); +	read_sysfs_string("name", dev_dir_name, name); +	printf("Device %03d: %s\n", dev_idx, name); + +	if (verblevel >= VERBLEVEL_SENSORS) { +		int ret = dump_channels(dev_dir_name); +		if (ret) +			return ret; +	} +	return 0; +} + +static int dump_one_trigger(const char *dev_dir_name) +{ +	char name[IIO_MAX_NAME_LENGTH]; +	int dev_idx; + +	sscanf(dev_dir_name + strlen(iio_dir) + strlen(type_trigger), +			"%i", &dev_idx); +	read_sysfs_string("name", dev_dir_name, name); +	printf("Trigger %03d: %s\n", dev_idx, name); +	return 0; +} + +static void dump_devices(void) +{ +	const struct dirent *ent; +	int number, numstrlen; + +	FILE *nameFile; +	DIR *dp; +	char thisname[IIO_MAX_NAME_LENGTH]; +	char *filename; + +	dp = opendir(iio_dir); +	if (dp == NULL) { +		printf("No industrial I/O devices available\n"); +		return; +	} + +	while (ent = readdir(dp), ent != NULL) { +		if (check_prefix(ent->d_name, type_device)) { +			char *dev_dir_name; +			asprintf(&dev_dir_name, "%s%s", iio_dir, ent->d_name); +			dump_one_device(dev_dir_name); +			free(dev_dir_name); +			if (verblevel >= VERBLEVEL_SENSORS) +				printf("\n"); +		} +	} +	rewinddir(dp); +	while (ent = readdir(dp), ent != NULL) { +		if (check_prefix(ent->d_name, type_trigger)) { +			char *dev_dir_name; +			asprintf(&dev_dir_name, "%s%s", iio_dir, ent->d_name); +			dump_one_trigger(dev_dir_name); +			free(dev_dir_name); +		} +	} +	closedir(dp); +} + +int main(int argc, char **argv) +{ +	int c, err = 0; + +	while ((c = getopt(argc, argv, "d:D:v")) != EOF) { +		switch (c) { +		case 'v': +			verblevel++; +			break; + +		case '?': +		default: +			err++; +			break; +		} +	} +	if (err || argc > optind) { +		fprintf(stderr, "Usage: lsiio [options]...\n" +			"List industrial I/O devices\n" +			"  -v, --verbose\n" +			"      Increase verbosity (may be given multiple times)\n" +			); +		exit(1); +	} + +	dump_devices(); + +	return 0; +} diff --git a/drivers/staging/iio/Documentation/overview.txt b/drivers/staging/iio/Documentation/overview.txt index d97106cb2b9..43f92b06bc3 100644 --- a/drivers/staging/iio/Documentation/overview.txt +++ b/drivers/staging/iio/Documentation/overview.txt @@ -3,13 +3,12 @@ Overview of IIO  The Industrial I/O subsystem is intended to provide support for devices  that in some sense are analog to digital converters (ADCs). As many  actual devices combine some ADCs with digital to analog converters -(DACs) the intention is to add that functionality at a future date -(hence the name). +(DACs) that functionality is also supported.  The aim is to fill the gap between the somewhat similar hwmon and  input subsystems.  Hwmon is very much directed at low sample rate  sensors used in applications such as fan speed control and temperature -measurement.  Input is, as it's name suggests focused on input +measurement.  Input is, as its name suggests focused on input  devices. In some cases there is considerable overlap between these and  IIO. @@ -31,32 +30,28 @@ event must be accessed via polling.  Note: A given device may have one or more event channel.  These events are  turned on or off (if possible) via sysfs interfaces. -* Hardware ring buffer support.  Some recent sensors have included +* Hardware buffer support.  Some recent sensors have included  fifo / ring buffers on the sensor chip.  These greatly reduce the load  on the host CPU by buffering relatively large numbers of data samples  based on an internal sampling clock. Examples include VTI SCA3000 -series and Analog Device ADXL345 accelerometers.  Each ring buffer -typically has an event chrdev (similar to the more general ones above) -to pass on events such as buffer 50% full and an access chrdev via -which the raw data it self may be read back. +series and Analog Device ADXL345 accelerometers.  Each buffer supports +polling to establish when data is available. -* Trigger and software ring buffer support. In many data analysis +* Trigger and software buffer support. In many data analysis  applications it it useful to be able to capture data based on some  external signal (trigger).  These triggers might be a data ready  signal, a gpio line connected to some external system or an on  processor periodic interrupt.  A single trigger may initialize data  capture or reading from a number of sensors.  These triggers are -used in IIO to fill software ring buffers acting in a very similar +used in IIO to fill software buffers acting in a very similar  fashion to the hardware buffers described above.  Other documentation: -userspace.txt - overview of ring buffer reading from userspace. -  device.txt - elements of a typical device driver.  trigger.txt - elements of a typical trigger driver. -ring.txt - additional elements required for ring buffer support. +ring.txt - additional elements required for buffer support.  sysfs-bus-iio - abi documentation file. diff --git a/drivers/staging/iio/Documentation/ring.txt b/drivers/staging/iio/Documentation/ring.txt index 3696c364e64..e1da43381d0 100644 --- a/drivers/staging/iio/Documentation/ring.txt +++ b/drivers/staging/iio/Documentation/ring.txt @@ -1,59 +1,47 @@ -Ring buffer support within IIO +Buffer support within IIO  This document is intended as a general overview of the functionality -a ring buffer may supply and how it is specified within IIO.  For more -specific information on a given ring buffer implementation, see the -comments in the source code.  Note that the intention is to allow -some drivers to specify ring buffers choice at probe or runtime, but -for now the selection is hard coded within a given driver. +a buffer may supply and how it is specified within IIO.  For more +specific information on a given buffer implementation, see the +comments in the source code.  Note that some drivers allow buffer +implementation to be selected at compile time via Kconfig options. -A given ring buffer implementation typically embedded a struct +A given buffer implementation typically embeds a struct  iio_ring_buffer and it is a pointer to this that is provided to the  IIO core. Access to the embedding structure is typically done via  container_of functions. -struct iio_ring_buffer contains 4 function pointers -(preenable, postenable, predisable, postdisable). -These are used to perform implementation specific steps on either side -of the core changing it's current mode to indicate that the ring buffer -is enabled or disabled (along with enabling triggering etc as appropriate). +struct iio_ring_buffer contains a struct iio_ring_setup_ops *setup_ops +which in turn contains the 4 function pointers +(preenable, postenable, predisable and postdisable). +These are used to perform device specific steps on either side +of the core changing its current mode to indicate that the buffer +is enabled or disabled (along with enabling triggering etc. as appropriate).  Also in struct iio_ring_buffer is a struct iio_ring_access_funcs.  The function pointers within here are used to allow the core to handle -as much ring buffer functionality as possible. Note almost all of these +as much buffer functionality as possible. Note almost all of these  are optional. -mark_in_use, unmark_in_use -  Basically indicate that not changes should be made to the ring -  buffer state that will effect the form of the data being captures -  (e.g. scan elements or length) -  store_to -  If possible, push data to ring buffer. +  If possible, push data to the buffer.  read_last -  If possible get the most recent entry from the buffer (without removal). +  If possible, get the most recent scan from the buffer (without removal).    This provides polling like functionality whilst the ring buffering is in    use without a separate read from the device. -rip_lots -  The primary ring buffer reading function. Note that it may well not return -  as much data as requested.  The deadoffset is used to indicate that some -  initial data in the data array is not guaranteed to be valid. +rip_first_n +  The primary buffer reading function. Note that it may well not return +  as much data as requested. -mark_param_changed -  Used to indicate that something has changed. Used in conjunction with  request_update    If parameters have changed that require reinitialization or configuration of -  the ring buffer this will trigger it. +  the buffer this will trigger it.  get_bytes_per_datum, set_bytes_per_datum    Get/set the number of bytes for a complete scan. (All samples + timestamp)  get_length / set_length -  Get/set the number of sample sets that may be held by the buffer. +  Get/set the number of complete scans that may be held by the buffer. -is_enabled -  Query if ring buffer is in use -enable -  Start the ring buffer. diff --git a/drivers/staging/iio/Documentation/sysfs-bus-iio b/drivers/staging/iio/Documentation/sysfs-bus-iio deleted file mode 100644 index fdb017a1c1a..00000000000 --- a/drivers/staging/iio/Documentation/sysfs-bus-iio +++ /dev/null @@ -1,390 +0,0 @@ -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[m][_name]_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). -		If the device supports events on this channel then m must be -		specified (even on named channels) so as to allow the source -		of event codes to be identified. - -What:		/sys/.../device[n]/in[m][_name]_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[m][_name]_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[m][_name]_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[m][_name]_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[m][_name]_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[m][_name]_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[_rising|_falling]_en -KernelVersion:	2.6.37 -Contact:	linux-iio@vger.kernel.org -Description: -		Event generated when accel_x0 passes a threshold in the specfied -		(_rising|_falling) direction. If the direction is not specified, -		then either the device will report an event which ever direction -		a single threshold value is called in (e.g. -		accel_x0_<raw|input>_thresh_value) or -		accel_x0_<raw|input>_thresh_rising_value and -		accel_x0_<raw|input>_thresh_falling_value may take different -		values, but the device can only enable both thresholds or -		neither. -		Note the driver will assume the last p events requested are -		to be enabled where p is however many it supports (which may -		vary depending on the exact set requested. So if you want to be -		sure you have set what you think you have, check the contents of -		these attributes after everything is configured. 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 event was previously enabled). - -What:		/sys/.../accel_x0_<raw|input>_thresh[_rising|_falling]_value -KernelVersion:	2.6.37 -Contact:	linux-iio@vger.kernel.org -Description: -		Specifies the value of threshold that the device is comparing -		against for the events enabled by -		accel_x0_<raw|input>_thresh[_rising|falling]_en. -		If seperate exist for the two directions, but direction is -		not specified for this attribute, then a single threshold value -		applies to both directions. -		The raw or input element of the name indicates whether the -		value is in raw device units or in processed units (as _raw -		and _input do on sysfs direct channel read attributes). - -What:		/sys/.../accel_x0_thresh[_rising|_falling]_meanperiod -KernelVersion:	2.6.37 -Contact:	linux-iio@vger.kernel.org -Description: -		Period of time (in seconds) over which the raw channel value -		is averaged before being compared with the threshold set in -		accel_x0_thresh[_rising|_falling]_meanperiod.  If direction is -		not specified then this mean period applies to both directions. - -What:		/sys/.../accel_x0_thresh[_rising|_falling]_period -KernelVersion:	2.6.37 -Contact:	linux-iio@vger.kernel.org -Description: -		Period of time (in seconds) for which the threshold must be -		passed before an event is generated. If direction is not -		specified then this period applies to both directions. - -What:		/sys/.../device[n]:event[m]/accel_x0_mag[_rising|_falling]_en -KernelVersion:	2.6.37 -Contact:	linux-iio@vger.kernel.org -Description: -		Similar to accel_x0_thresh[_rising|_falling]_en, but here the -		magnitude of the channel is compared to the threshold, not its -		signed value. - -What:		/sys/.../accel_x0_<raw|input>_mag[_rising|_falling]_value -KernelVersion:	2.6.37 -Contact:	linux-iio@vger.kernel.org -Description: -		The value to which the magnitude of the channel is compared. - -What:		/sys/.../accel_x0_mag[_rising|_falling]_meanperiod -KernelVersion:	2.6.37 -Contact:	linux-iio@vger.kernel.org -Description: -		Period of time (in seconds) over which the value of the channel -		is averaged before being compared to the threshold - -What:		/sys/.../accel_x0_mag[_rising|_falling]_period -KernelVersion:	2.6.37 -Contact:	linux-iio@vger.kernel.org -Description: -		Period of time (in seconds) for which the condition must be true -		before an event occurs. - -What:		/sys/.../device[n]:event[m]/accel_x0_roc[_rising|_falling]_en -KernelVersion:	2.6.37 -Contact:	linux-iio@vger.kernel.org -Description: -		Similar to accel_x0_thresh[_rising|_falling]_en, but here the -		first differential is compared with the threshold. - -What:		/sys/.../accel_x0_<raw|input>_roc[_rising|_falling]_value -KernelVersion:	2.6.37 -Contact:	linux-iio@vger.kernel.org -Description: -		The value to which the first differential of the channel is -		compared. - -What:		/sys/.../accel_x0_roc[_rising|_falling]_meanperiod -KernelVersion:	2.6.37 -Contact:	linux-iio@vger.kernel.org -Description: -		Period of time (in seconds) over which the value of the channel -		is averaged before being compared to the threshold - -What:		/sys/.../accel_x0_roc[_rising|_falling]_period -KernelVersion:	2.6.37 -Contact:	linux-iio@vger.kernel.org -Description: -		Period of time (in seconds) for which the condition must be true -		before an event occurs. - -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/bytes_per_datum -KernelVersion:	2.6.37 -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) - -What:		/sys/.../device[n]/buffer/scan_elements -KernelVersion:	2.6.37 -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]/buffer/scan_elements/accel_x0_en -KernelVersion:	2.6.37 -Contact:	linux-iio@vger.kernel.org -Description: -		Scan element control for triggered data capture. - -What:		/sys/.../device[n]/buffer/scan_elements/accel[_x0]_type -KernelVersion:	2.6.37 -Contact:	linux-iio@vger.kernel.org -Description: -		Description of the scan element data storage within the buffer -		and hence the form in which it is read from userspace. -		Form is [s|u]bits/storagebits.  s or u specifies if signed -		(2's complement) or unsigned. bits is the number of bits of -		data and storagebits is the space (after padding) that it -		occupies in the buffer.  Note that some devices will have -		additional information in the unused bits so to get a clean -		value, the bits value must be used to mask the buffer output -		value appropriately.  The storagebits value also specifies the -		data alignment.  So s48/64 will be a signed 48 bit integer -		stored in a 64 bit location aligned to a a64 bit boundary. -		For other storage combinations this attribute will be extended -		appropriately. - -What:		/sys/.../device[n]/buffer/scan_elements/accel[_x0]_index -KernelVersion:	2.6.37 -Contact:	linux-iio@vger.kernel.org -Description: -		A single positive integer specifying the position of this -		scan element in the buffer. Note these are not dependant on -		what is enabled and may not be contiguous. Thus for userspace -		to establish the full layout these must be used in conjunction -		with all _en attributes to establish which channels are present, -		and the relevant _type attributes to establish the data storage -		format. - -What:		/sys/.../device[n]/buffer/scan_elements/accel[_x0]_shift -KernelVersion:	2.6.37 -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. diff --git a/drivers/staging/iio/Documentation/sysfs-bus-iio-ad7192 b/drivers/staging/iio/Documentation/sysfs-bus-iio-ad7192 new file mode 100644 index 00000000000..1c35c507cc0 --- /dev/null +++ b/drivers/staging/iio/Documentation/sysfs-bus-iio-ad7192 @@ -0,0 +1,20 @@ +What:		/sys/.../iio:deviceX/ac_excitation_en +KernelVersion:	3.1.0 +Contact:	linux-iio@vger.kernel.org +Description: +		This attribute, if available, is used to enable the AC +		excitation mode found on some converters. In ac excitation mode, +		the polarity of the excitation voltage is reversed on +		alternate cycles, to eliminate DC errors. + +What:		/sys/.../iio:deviceX/bridge_switch_en +KernelVersion:	3.1.0 +Contact:	linux-iio@vger.kernel.org +Description: +		This attribute, if available, is used to close or open the +		bridge power down switch found on some converters. +		In bridge applications, such as strain gauges and load cells, +		the bridge itself consumes the majority of the current in the +		system. To minimize the current consumption of the system, +		the bridge can be disconnected (when it is not being used +		using the bridge_switch_en attribute. diff --git a/drivers/staging/iio/Documentation/sysfs-bus-iio-adc-ad7280a b/drivers/staging/iio/Documentation/sysfs-bus-iio-adc-ad7280a new file mode 100644 index 00000000000..863d3856718 --- /dev/null +++ b/drivers/staging/iio/Documentation/sysfs-bus-iio-adc-ad7280a @@ -0,0 +1,21 @@ +What:		/sys/bus/iio/devices/deviceX/inY-inZ_balance_switch_en +KernelVersion:	3.0.0 +Contact:	linux-iio@vger.kernel.org +Description: +		Writing 1 enables the cell balance output switch corresponding +		to input Y. Writing 0 disables it. If the inY-inZ_balance_timer +		is set to a none zero value, the corresponding switch will +		enable for the programmed amount of time, before it +		automatically disables. + +What:		/sys/bus/iio/devices/deviceX/inY-inZ_balance_timer +KernelVersion:	3.0.0 +Contact:	linux-iio@vger.kernel.org +Description: +		The inY-inZ_balance_timer file allows the user to program +		individual times for each cell balance output. The AD7280A +		allows the user to set the timer to a value from 0 minutes to +		36.9 minutes. The resolution of the timer is 71.5 sec. +		The value written is the on-time in milliseconds. When the +		timer value is set 0, the timer is disabled. The cell balance +		outputs are controlled only by inY-inZ_balance_switch_en. diff --git a/drivers/staging/iio/Documentation/sysfs-bus-iio-dds b/drivers/staging/iio/Documentation/sysfs-bus-iio-dds new file mode 100644 index 00000000000..ee8c509c673 --- /dev/null +++ b/drivers/staging/iio/Documentation/sysfs-bus-iio-dds @@ -0,0 +1,96 @@ + +What:		/sys/bus/iio/devices/.../out_altvoltageX_frequencyY +KernelVersion:	2.6.37 +Contact:	linux-iio@vger.kernel.org +Description: +		Stores frequency into tuning word Y. +		There will be more than one out_altvoltageX_frequencyY file, +		which allows for pin controlled FSK Frequency Shift Keying +		(out_altvoltageX_pincontrol_frequency_en is active) or the user +		can control the desired active tuning word by writing Y to the +		out_altvoltageX_frequencysymbol file. + +What:		/sys/bus/iio/devices/.../out_altvoltageX_frequencyY_scale +KernelVersion:	2.6.37 +Contact:	linux-iio@vger.kernel.org +Description: +		Scale to be applied to out_altvoltageX_frequencyY in order to +		obtain the desired value in Hz. If shared across all frequency +		registers Y is not present. It is also possible X is not present +		if shared across all channels. + +What:		/sys/bus/iio/devices/.../out_altvoltageX_frequencysymbol +KernelVersion:	2.6.37 +Contact:	linux-iio@vger.kernel.org +Description: +		Specifies the active output frequency tuning word. The value +		corresponds to the Y in out_altvoltageX_frequencyY. +		To exit this mode the user can write +		out_altvoltageX_pincontrol_frequency_en or +		out_altvoltageX_out_enable file. + +What:		/sys/bus/iio/devices/.../out_altvoltageX_phaseY +KernelVersion:	2.6.37 +Contact:	linux-iio@vger.kernel.org +Description: +		Stores phase into Y. +		There will be more than one out_altvoltageX_phaseY file, which +		allows for pin controlled PSK Phase Shift Keying +		(out_altvoltageX_pincontrol_phase_en is active) or the user can +		control the desired phase Y which is added to the phase +		accumulator output by writing Y to the phase_en file. + +What:		/sys/bus/iio/devices/.../out_altvoltageX_phaseY_scale +KernelVersion:	2.6.37 +Contact:	linux-iio@vger.kernel.org +Description: +		Scale to be applied to out_altvoltageX_phaseY in order to obtain +		the desired value in rad. If shared across all phase registers +		Y is not present. It is also possible X is not present if +		shared across all channels. + +What:		/sys/bus/iio/devices/.../out_altvoltageX_phasesymbol +KernelVersion:	2.6.37 +Contact:	linux-iio@vger.kernel.org +Description: +		Specifies the active phase Y which is added to the phase +		accumulator output. The value corresponds to the Y in +		out_altvoltageX_phaseY. To exit this mode the user can write +		out_altvoltageX_pincontrol_phase_en or disable file. + +What:		/sys/bus/iio/devices/.../out_altvoltageX_pincontrol_en +What:		/sys/bus/iio/devices/.../out_altvoltageX_pincontrol_frequency_en +What:		/sys/bus/iio/devices/.../out_altvoltageX_pincontrol_phase_en +KernelVersion:	2.6.37 +Contact:	linux-iio@vger.kernel.org +Description: +		out_altvoltageX_pincontrol_en: Both, the active frequency and +		phase is controlled by the respective phase and frequency +		control inputs. In case the device in features independent +		controls, then there are dedicated files +		(out_altvoltageX_pincontrol_frequency_en, +		out_altvoltageX_pincontrol_phase_en). + +What:		/sys/bus/iio/devices/.../out_altvoltageX_out_enable +What:		/sys/bus/iio/devices/.../out_altvoltageX_outY_enable +KernelVersion:	2.6.37 +Contact:	linux-iio@vger.kernel.org +Description: +		out_altvoltageX_outY_enable controls signal generation on +		output Y of channel X. Y may be suppressed if all channels are +		controlled together. + +What:		/sys/bus/iio/devices/.../out_altvoltageX_outY_wavetype +KernelVersion:	2.6.37 +Contact:	linux-iio@vger.kernel.org +Description: +		Specifies the output waveform. +		(sine, triangle, ramp, square, ...) +		For a list of available output waveform options read +		available_output_modes. + +What:		/sys/bus/iio/devices/.../out_altvoltageX_outY_wavetype_available +KernelVersion:	2.6.37 +Contact:	linux-iio@vger.kernel.org +Description: +		Lists all available output waveform options. diff --git a/drivers/staging/iio/Documentation/sysfs-bus-iio-impedance-analyzer-ad5933 b/drivers/staging/iio/Documentation/sysfs-bus-iio-impedance-analyzer-ad5933 new file mode 100644 index 00000000000..79c7e88c64c --- /dev/null +++ b/drivers/staging/iio/Documentation/sysfs-bus-iio-impedance-analyzer-ad5933 @@ -0,0 +1,30 @@ +What:		/sys/bus/iio/devices/iio:deviceX/outY_freq_start +KernelVersion:	3.1.0 +Contact:	linux-iio@vger.kernel.org +Description: +		Frequency sweep start frequency in Hz. + +What:		/sys/bus/iio/devices/iio:deviceX/outY_freq_increment +KernelVersion:	3.1.0 +Contact:	linux-iio@vger.kernel.org +Description: +		Frequency increment in Hz (step size) between consecutive +		frequency points along the sweep. + +What:		/sys/bus/iio/devices/iio:deviceX/outY_freq_points +KernelVersion:	3.1.0 +Contact:	linux-iio@vger.kernel.org +Description: +		Number of frequency points (steps) in the frequency sweep. +		This value, in conjunction with the outY_freq_start and the +		outY_freq_increment, determines the frequency sweep range +		for the sweep operation. + +What:		/sys/bus/iio/devices/iio:deviceX/outY_settling_cycles +KernelVersion:	3.1.0 +Contact:	linux-iio@vger.kernel.org +Description: +		Number of output excitation cycles (settling time cycles) +		that are allowed to pass through the unknown impedance, +		after each frequency increment, and before the ADC is triggered +		to perform a conversion sequence of the response signal. diff --git a/drivers/staging/iio/Documentation/sysfs-bus-iio-light b/drivers/staging/iio/Documentation/sysfs-bus-iio-light index 5d84856dc14..17e5c9c515d 100644 --- a/drivers/staging/iio/Documentation/sysfs-bus-iio-light +++ b/drivers/staging/iio/Documentation/sysfs-bus-iio-light @@ -26,7 +26,7 @@ Description:  		Hardware dependent list of possible values supported for the  		adc_resolution of the given sensor. -What:		/sys/bus/iio/devices/device[n]/illuminance0[_input|_raw] +What:		/sys/bus/iio/devices/device[n]/in_illuminance0[_input|_raw]  KernelVersion:	2.6.35  Contact:	linux-iio@vger.kernel.org  Description: @@ -34,7 +34,7 @@ Description:  		it comes back in SI units, it should also include _input else it  		should include _raw to signify it is not in SI units. -What:		/sys/.../device[n]/proximity_on_chip_ambient_infrared_supression +What:		/sys/.../device[n]/proximity_on_chip_ambient_infrared_suppression  KernelVersion:	2.6.37  Contact:	linux-iio@vger.kernel.org  Description: @@ -45,7 +45,7 @@ Description:  		do this calculation manually by reading the infrared sensor  		value and doing the negation in sw. -What:		/sys/bus/iio/devices/device[n]/proximity[_input|_raw] +What:		/sys/bus/iio/devices/device[n]/in_proximity[_input|_raw]  KernelVersion:	2.6.37  Contact:	linux-iio@vger.kernel.org  Description: @@ -62,3 +62,46 @@ Description:  		sensing mode. This value should be the output from a reading  		and if expressed in SI units, should include _input. If this  		value is not in SI units, then it should include _raw. + +What:		/sys/bus/iio/devices/device[n]/in_illuminance0_target +KernelVersion:	2.6.37 +Contact:	linux-iio@vger.kernel.org +Description: +		This property gets/sets the last known external +		lux measurement used in/for calibration. + +What:		/sys/bus/iio/devices/device[n]/in_illuminance0_integration_time +KernelVersion:	2.6.37 +Contact:	linux-iio@vger.kernel.org +Description: +		This property gets/sets the sensors ADC analog integration time. + +What:		/sys/bus/iio/devices/device[n]/in_illuminance0_lux_table +KernelVersion:	2.6.37 +Contact:	linux-iio@vger.kernel.org +Description: +		This property gets/sets the table of coefficients +		used in calculating illuminance in lux. + +What:		/sys/bus/iio/devices/device[n]/in_intensity_clear[_input|_raw] +What:		/sys/bus/iio/devices/device[n]/in_intensity_red[_input|_raw] +What:		/sys/bus/iio/devices/device[n]/in_intensity_green[_input|_raw] +What:		/sys/bus/iio/devices/device[n]/in_intensity_blue[_input|_raw] +KernelVersion:	3.6.0 +Contact:	linux-iio@vger.kernel.org +Description: +		This property is supported by sensors that have a RGBC +		sensing mode. This value should be the output from a reading +		and if expressed in SI units, should include _input. If this +		value is not in SI units (irradiance, uW/mm^2), then it should +		include _raw. + +What:		/sys/bus/iio/devices/device[n]/in_cct0[_input|_raw] +KernelVersion:	3.6.0 +Contact:	linux-iio@vger.kernel.org +Description: +		This should return the correlated color temperature from the +		light sensor. If it comes back in SI units, it should also +		include _input else it should include _raw to signify it is not +		in SI units. + diff --git a/drivers/staging/iio/Documentation/sysfs-bus-iio-light-tsl2583 b/drivers/staging/iio/Documentation/sysfs-bus-iio-light-tsl2583 new file mode 100644 index 00000000000..660781df409 --- /dev/null +++ b/drivers/staging/iio/Documentation/sysfs-bus-iio-light-tsl2583 @@ -0,0 +1,20 @@ +What:		/sys/bus/iio/devices/device[n]/lux_table +KernelVersion:	2.6.37 +Contact:	linux-iio@vger.kernel.org +Description: +		This property gets/sets the table of coefficients +		used in calculating illuminance in lux. + +What:		/sys/bus/iio/devices/device[n]/illuminance0_calibrate +KernelVersion:	2.6.37 +Contact:	linux-iio@vger.kernel.org +Description: +		This property causes an internal calibration of the als gain trim +		value which is later used in calculating illuminance in lux. + +What:		/sys/bus/iio/devices/device[n]/illuminance0_input_target +KernelVersion:	2.6.37 +Contact:	linux-iio@vger.kernel.org +Description: +		This property is the known externally illuminance (in lux). +		It is used in the process of calibrating the device accuracy. diff --git a/drivers/staging/iio/Documentation/sysfs-bus-iio-trigger-sysfs b/drivers/staging/iio/Documentation/sysfs-bus-iio-trigger-sysfs new file mode 100644 index 00000000000..5235e6c749a --- /dev/null +++ b/drivers/staging/iio/Documentation/sysfs-bus-iio-trigger-sysfs @@ -0,0 +1,11 @@ +What:		/sys/bus/iio/devices/triggerX/trigger_now +KernelVersion:	2.6.38 +Contact:	linux-iio@vger.kernel.org +Description: +		This file is provided by the iio-trig-sysfs stand-alone trigger +		driver. Writing this file with any value triggers an event +		driven driver, associated with this trigger, to capture data +		into an in kernel buffer. This approach can be valuable during +		automated testing or in situations, where other trigger methods +		are not applicable. For example no RTC or spare GPIOs. +		X is the IIO index of the trigger. diff --git a/drivers/staging/iio/Documentation/trigger.txt b/drivers/staging/iio/Documentation/trigger.txt index 650157f5c9d..64e2e08fb4d 100644 --- a/drivers/staging/iio/Documentation/trigger.txt +++ b/drivers/staging/iio/Documentation/trigger.txt @@ -5,23 +5,21 @@ an IIO device.  Whilst this can create device specific complexities  such triggers are registered with the core in the same way as  stand-alone triggers. -struct iio_trig *trig = iio_allocate_trigger(); +struct iio_trig *trig = iio_trigger_alloc("<trigger format string>", ...);  allocates a trigger structure.  The key elements to then fill in within  a driver are: -trig->control_attrs -	Any sysfs attributes needed to control parameters of the trigger - -trig->private_data -	Device specific private data. -  trig->owner  	Typically set to THIS_MODULE. Used to ensure correct  	ownership of core allocated resources. -trig->name -	A unique name for the trigger. +trig->set_trigger_state: +	Function that enables / disables the underlying source of the trigger. + +There is also a +trig->alloc_list which is useful for drivers that allocate multiple +triggers to keep track of what they have created.  When these have been set call: @@ -30,9 +28,8 @@ iio_trigger_register(trig);  to register the trigger with the core, making it available to trigger  consumers. -  Trigger Consumers -Currently triggers are only used for the filling of software ring +Currently triggers are only used for the filling of software  buffers and as such any device supporting INDIO_RING_TRIGGERED has the  consumer interface automatically created. diff --git a/drivers/staging/iio/Documentation/userspace.txt b/drivers/staging/iio/Documentation/userspace.txt deleted file mode 100644 index ff06e5dc718..00000000000 --- a/drivers/staging/iio/Documentation/userspace.txt +++ /dev/null @@ -1,12 +0,0 @@ -Userspace access to IIO - -The sysfs attributes are documented in sysfs-bus-iio. - -Udev will create the following entries under /dev by default: - -device0:buffer0:access0 - ring access chrdev -device0:buffer0:event0 - ring event chrdev -device0:event0 - general event chrdev. - -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 ed48815a916..fa38be0982f 100644 --- a/drivers/staging/iio/Kconfig +++ b/drivers/staging/iio/Kconfig @@ -1,52 +1,47 @@  # -# Industrial I/O subsytem configuration +# Industrial I/O subsystem configuration  # - -menuconfig IIO -	tristate "Industrial I/O support" -	depends on !S390 -	---help--- -	  The industrial I/O subsystem provides a unified framework for -	  drivers for many different types of embedded sensors using a -	  number of different physical interfaces (i2c, spi, etc). See -	  Documentation/industrialio for more information. -if IIO - -config IIO_RING_BUFFER -	bool "Enable ring buffer support within IIO" -	help -	  Provide core support for various ring buffer based data -	  acquisition methods. - -if IIO_RING_BUFFER - -config IIO_SW_RING -       select IIO_TRIGGER -	tristate "Industrial I/O lock free software ring" -	help -	  Example software ring buffer implementation.  The design aim -	  of this particular realization was to minimize write locking -	  with the intention that some devices would be able to write -	  in interrupt context. - -endif # IIO_RINGBUFFER - -config IIO_TRIGGER -	boolean "Enable triggered sampling support" -	help -	  Provides IIO core support for triggers.  Currently these -	  are used to initialize capture of samples to push into -	  ring buffers.  The triggers are effectively a 'capture -	  data now' interrupt. - +menu "IIO staging drivers" +	depends on IIO  source "drivers/staging/iio/accel/Kconfig"  source "drivers/staging/iio/adc/Kconfig" +source "drivers/staging/iio/addac/Kconfig" +source "drivers/staging/iio/cdc/Kconfig" +source "drivers/staging/iio/frequency/Kconfig"  source "drivers/staging/iio/gyro/Kconfig" -source "drivers/staging/iio/imu/Kconfig" +source "drivers/staging/iio/impedance-analyzer/Kconfig"  source "drivers/staging/iio/light/Kconfig"  source "drivers/staging/iio/magnetometer/Kconfig" - +source "drivers/staging/iio/meter/Kconfig" +source "drivers/staging/iio/resolver/Kconfig"  source "drivers/staging/iio/trigger/Kconfig" -endif # IIO +config IIO_DUMMY_EVGEN +       tristate + +config IIO_SIMPLE_DUMMY +       tristate "An example driver with no hardware requirements" +       help +	 Driver intended mainly as documentation for how to write +	 a driver. May also be useful for testing userspace code +	 without hardware. + +if IIO_SIMPLE_DUMMY + +config IIO_SIMPLE_DUMMY_EVENTS +       boolean "Event generation support" +       select IIO_DUMMY_EVGEN +       help +         Add some dummy events to the simple dummy driver. + +config IIO_SIMPLE_DUMMY_BUFFER +	boolean "Buffered capture support" +	select IIO_BUFFER +	select IIO_KFIFO_BUF +	help +	  Add buffered data capture to the simple dummy driver. + +endif # IIO_SIMPLE_DUMMY + +endmenu diff --git a/drivers/staging/iio/Makefile b/drivers/staging/iio/Makefile index e909674920f..d87106135b2 100644 --- a/drivers/staging/iio/Makefile +++ b/drivers/staging/iio/Makefile @@ -2,17 +2,22 @@  # Makefile for the industrial I/O core.  # -obj-$(CONFIG_IIO) += industrialio.o -industrialio-y := industrialio-core.o -industrialio-$(CONFIG_IIO_RING_BUFFER) += industrialio-ring.o -industrialio-$(CONFIG_IIO_TRIGGER) += industrialio-trigger.o +obj-$(CONFIG_IIO_SIMPLE_DUMMY) += iio_dummy.o +iio_dummy-y := iio_simple_dummy.o +iio_dummy-$(CONFIG_IIO_SIMPLE_DUMMY_EVENTS) += iio_simple_dummy_events.o +iio_dummy-$(CONFIG_IIO_SIMPLE_DUMMY_BUFFER) += iio_simple_dummy_buffer.o -obj-$(CONFIG_IIO_SW_RING) += ring_sw.o +obj-$(CONFIG_IIO_DUMMY_EVGEN) += iio_dummy_evgen.o  obj-y += accel/  obj-y += adc/ +obj-y += addac/ +obj-y += cdc/ +obj-y += frequency/  obj-y += gyro/ -obj-y += imu/ +obj-y += impedance-analyzer/  obj-y += light/ -obj-y += trigger/  obj-y += magnetometer/ +obj-y += meter/ +obj-y += resolver/ +obj-y += trigger/ diff --git a/drivers/staging/iio/TODO b/drivers/staging/iio/TODO index 898cba1c939..c22a0edd152 100644 --- a/drivers/staging/iio/TODO +++ b/drivers/staging/iio/TODO @@ -13,6 +13,17 @@ Would be nice  3) Expand device set. Lots of other maxim adc's have very     similar interfaces. +MXS LRADC driver: +This is a classic MFD device as it combines the following subdevices + - touchscreen controller (input subsystem related device) + - general purpose ADC channels + - battery voltage monitor (power subsystem related device) + - die temperature monitor (thermal management) + +At least the battery voltage and die temperature feature is required in-kernel +by a driver of the SoC's battery charging unit to avoid any damage to the +silicon and the battery. +  TSL2561  Would be nice  1) Open question of userspace vs kernel space balance when @@ -61,9 +72,13 @@ necessitate a header that is also visible from arch board  files. (avoided at the moment to keep the driver set  contained in staging). +ADI Drivers: +CC the device-drivers-devel@blackfin.uclinux.org mailing list when +e-mailing the normal IIO list (see below). +  Documentation  1) Lots of cleanup and expansion. -2) Some device require indvidual docs. +2) Some device require individual docs. -Contact: Jonathan Cameron <jic23@cam.ac.uk>. +Contact: Jonathan Cameron <jic23@kernel.org>.  Mailing list: linux-iio@vger.kernel.org diff --git a/drivers/staging/iio/accel/Kconfig b/drivers/staging/iio/accel/Kconfig index 5926c03be1a..ad45dfbdf41 100644 --- a/drivers/staging/iio/accel/Kconfig +++ b/drivers/staging/iio/accel/Kconfig @@ -1,13 +1,40 @@  #  # Accelerometer drivers  # -comment "Accelerometers" +menu "Accelerometers" + +config ADIS16201 +	tristate "Analog Devices ADIS16201 Dual-Axis Digital Inclinometer and Accelerometer" +	depends on SPI +	select IIO_ADIS_LIB +	select IIO_ADIS_LIB_BUFFER if IIO_BUFFER +	help +	  Say yes here to build support for Analog Devices adis16201 dual-axis +	  digital inclinometer and accelerometer. + +config ADIS16203 +	tristate "Analog Devices ADIS16203 Programmable 360 Degrees Inclinometer" +	depends on SPI +	select IIO_ADIS_LIB +	select IIO_ADIS_LIB_BUFFER if IIO_BUFFER +	help +	  Say yes here to build support for Analog Devices adis16203 Programmable +	  360 Degrees Inclinometer. + +config ADIS16204 +	tristate "Analog Devices ADIS16204 Programmable High-g Digital Impact Sensor and Recorder" +	depends on SPI +	select IIO_ADIS_LIB +	select IIO_ADIS_LIB_BUFFER if IIO_BUFFER +	help +	  Say yes here to build support for Analog Devices adis16204 Programmable +	  High-g Digital Impact Sensor and Recorder.  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 +	select IIO_ADIS_LIB +	select IIO_ADIS_LIB_BUFFER if IIO_BUFFER  	help  	  Say yes here to build support for Analog Devices adis16209 dual-axis digital inclinometer  	  and accelerometer. @@ -15,6 +42,7 @@ config ADIS16209  config ADIS16220  	tristate "Analog Devices ADIS16220 Programmable Digital Vibration Sensor"  	depends on SPI +	select IIO_ADIS_LIB  	help  	  Say yes here to build support for Analog Devices adis16220 programmable  	  digital vibration sensor. @@ -22,33 +50,29 @@ config ADIS16220  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 +	select IIO_ADIS_LIB +	select IIO_ADIS_LIB_BUFFER if IIO_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 -	help -	  Say yes here to build support for the Kionix KXSD9 accelerometer. -	  Currently this only supports the device via an SPI interface. -  config LIS3L02DQ  	tristate "ST Microelectronics LIS3L02DQ Accelerometer Driver"  	depends on SPI -	select IIO_TRIGGER if IIO_RING_BUFFER -	select IIO_SW_RING if IIO_RING_BUFFER +	select IIO_TRIGGER if IIO_BUFFER +	depends on !IIO_BUFFER || IIO_KFIFO_BUF +	depends on GPIOLIB  	help  	  Say yes here to build SPI support for the ST microelectronics  	  accelerometer. The driver supplies direct access via sysfs files  	  and an event interface via a character device.  config SCA3000 -	depends on IIO_RING_BUFFER +	depends on IIO_BUFFER  	depends on SPI  	tristate "VTI SCA3000 series accelerometers"  	help  	  Say yes here to build support for the VTI SCA3000 series of SPI  	  accelerometers. These devices use a hardware ring buffer. + +endmenu diff --git a/drivers/staging/iio/accel/Makefile b/drivers/staging/iio/accel/Makefile index ff84703a16f..1ed137f1a50 100644 --- a/drivers/staging/iio/accel/Makefile +++ b/drivers/staging/iio/accel/Makefile @@ -2,21 +2,26 @@  # Makefile for industrial I/O accelerometer drivers  # +adis16201-y             := adis16201_core.o +obj-$(CONFIG_ADIS16201) += adis16201.o + +adis16203-y             := adis16203_core.o +obj-$(CONFIG_ADIS16203) += adis16203.o + +adis16204-y             := adis16204_core.o +obj-$(CONFIG_ADIS16204) += adis16204.o +  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 -lis3l02dq-$(CONFIG_IIO_RING_BUFFER) += lis3l02dq_ring.o +lis3l02dq-$(CONFIG_IIO_BUFFER) += lis3l02dq_ring.o  obj-$(CONFIG_LIS3L02DQ)	+= lis3l02dq.o  sca3000-y		:= sca3000_core.o sca3000_ring.o diff --git a/drivers/staging/iio/accel/accel.h b/drivers/staging/iio/accel/accel.h deleted file mode 100644 index f5f61b2497a..00000000000 --- a/drivers/staging/iio/accel/accel.h +++ /dev/null @@ -1,67 +0,0 @@ - -#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) - -#define IIO_DEV_ATTR_ACCEL_Y_OFFSET(_mode, _show, _store, _addr)	\ -	IIO_DEVICE_ATTR(accel_y_offset, _mode, _show, _store, _addr) - -#define IIO_DEV_ATTR_ACCEL_Z_OFFSET(_mode, _show, _store, _addr)	\ -	IIO_DEVICE_ATTR(accel_z_offset, _mode, _show, _store, _addr) - -#define IIO_CONST_ATTR_ACCEL_SCALE(_string)		\ -	IIO_CONST_ATTR(accel_scale, _string) - -#define IIO_DEV_ATTR_ACCEL_SCALE(_mode, _show, _store, _addr)		\ -	IIO_DEVICE_ATTR(accel_scale, _mode, _show, _store, _addr) - -#define IIO_DEV_ATTR_ACCEL_X_SCALE(_mode, _show, _store, _addr)		\ -	IIO_DEVICE_ATTR(accel_x_scale, _mode, _show, _store, _addr) - -#define IIO_DEV_ATTR_ACCEL_Y_SCALE(_mode, _show, _store, _addr)		\ -	IIO_DEVICE_ATTR(accel_y_scale, _mode, _show, _store, _addr) - -#define IIO_DEV_ATTR_ACCEL_Z_SCALE(_mode, _show, _store, _addr)		\ -	IIO_DEVICE_ATTR(accel_z_scale, _mode, _show, _store, _addr) - -#define IIO_DEV_ATTR_ACCEL_CALIBBIAS(_mode, _show, _store, _addr)		\ -	IIO_DEVICE_ATTR(accel_calibbias, _mode, _show, _store, _addr) - -#define IIO_DEV_ATTR_ACCEL_X_CALIBBIAS(_mode, _show, _store, _addr)		\ -	IIO_DEVICE_ATTR(accel_x_calibbias, _mode, _show, _store, _addr) - -#define IIO_DEV_ATTR_ACCEL_Y_CALIBBIAS(_mode, _show, _store, _addr)		\ -	IIO_DEVICE_ATTR(accel_y_calibbias, _mode, _show, _store, _addr) - -#define IIO_DEV_ATTR_ACCEL_Z_CALIBBIAS(_mode, _show, _store, _addr)		\ -	IIO_DEVICE_ATTR(accel_z_calibbias, _mode, _show, _store, _addr) - -#define IIO_DEV_ATTR_ACCEL_CALIBSCALE(_mode, _show, _store, _addr)		\ -	IIO_DEVICE_ATTR(accel_calibscale, _mode, _show, _store, _addr) - -#define IIO_DEV_ATTR_ACCEL_X_CALIBSCALE(_mode, _show, _store, _addr)		\ -	IIO_DEVICE_ATTR(accel_x_calibscale, _mode, _show, _store, _addr) - -#define IIO_DEV_ATTR_ACCEL_Y_CALIBSCALE(_mode, _show, _store, _addr)		\ -	IIO_DEVICE_ATTR(accel_y_calibscale, _mode, _show, _store, _addr) - -#define IIO_DEV_ATTR_ACCEL_Z_CALIBSCALE(_mode, _show, _store, _addr)		\ -	IIO_DEVICE_ATTR(accel_z_calibscale, _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_raw, S_IRUGO, _show, NULL, _addr) - -#define IIO_DEV_ATTR_ACCEL_Y(_show, _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_raw, S_IRUGO, _show, NULL, _addr) - diff --git a/drivers/staging/iio/accel/adis16201.h b/drivers/staging/iio/accel/adis16201.h new file mode 100644 index 00000000000..8747de5a980 --- /dev/null +++ b/drivers/staging/iio/accel/adis16201.h @@ -0,0 +1,66 @@ +#ifndef SPI_ADIS16201_H_ +#define SPI_ADIS16201_H_ + +#define ADIS16201_STARTUP_DELAY	220 /* ms */ + +#define ADIS16201_FLASH_CNT      0x00 /* Flash memory write count */ +#define ADIS16201_SUPPLY_OUT     0x02 /* Output, power supply */ +#define ADIS16201_XACCL_OUT      0x04 /* Output, x-axis accelerometer */ +#define ADIS16201_YACCL_OUT      0x06 /* Output, y-axis accelerometer */ +#define ADIS16201_AUX_ADC        0x08 /* Output, auxiliary ADC input */ +#define ADIS16201_TEMP_OUT       0x0A /* Output, temperature */ +#define ADIS16201_XINCL_OUT      0x0C /* Output, x-axis inclination */ +#define ADIS16201_YINCL_OUT      0x0E /* Output, y-axis inclination */ +#define ADIS16201_XACCL_OFFS     0x10 /* Calibration, x-axis acceleration offset */ +#define ADIS16201_YACCL_OFFS     0x12 /* Calibration, y-axis acceleration offset */ +#define ADIS16201_XACCL_SCALE    0x14 /* x-axis acceleration scale factor */ +#define ADIS16201_YACCL_SCALE    0x16 /* y-axis acceleration scale factor */ +#define ADIS16201_XINCL_OFFS     0x18 /* Calibration, x-axis inclination offset */ +#define ADIS16201_YINCL_OFFS     0x1A /* Calibration, y-axis inclination offset */ +#define ADIS16201_XINCL_SCALE    0x1C /* x-axis inclination scale factor */ +#define ADIS16201_YINCL_SCALE    0x1E /* y-axis inclination scale factor */ +#define ADIS16201_ALM_MAG1       0x20 /* Alarm 1 amplitude threshold */ +#define ADIS16201_ALM_MAG2       0x22 /* Alarm 2 amplitude threshold */ +#define ADIS16201_ALM_SMPL1      0x24 /* Alarm 1, sample period */ +#define ADIS16201_ALM_SMPL2      0x26 /* Alarm 2, sample period */ +#define ADIS16201_ALM_CTRL       0x28 /* Alarm control */ +#define ADIS16201_AUX_DAC        0x30 /* Auxiliary DAC data */ +#define ADIS16201_GPIO_CTRL      0x32 /* General-purpose digital input/output control */ +#define ADIS16201_MSC_CTRL       0x34 /* Miscellaneous control */ +#define ADIS16201_SMPL_PRD       0x36 /* Internal sample period (rate) control */ +#define ADIS16201_AVG_CNT        0x38 /* Operation, filter configuration */ +#define ADIS16201_SLP_CNT        0x3A /* Operation, sleep mode control */ +#define ADIS16201_DIAG_STAT      0x3C /* Diagnostics, system status register */ +#define ADIS16201_GLOB_CMD       0x3E /* Operation, system command register */ + +/* MSC_CTRL */ +#define ADIS16201_MSC_CTRL_SELF_TEST_EN	        (1 << 8)  /* Self-test enable */ +#define ADIS16201_MSC_CTRL_DATA_RDY_EN	        (1 << 2)  /* Data-ready enable: 1 = enabled, 0 = disabled */ +#define ADIS16201_MSC_CTRL_ACTIVE_HIGH	        (1 << 1)  /* Data-ready polarity: 1 = active high, 0 = active low */ +#define ADIS16201_MSC_CTRL_DATA_RDY_DIO1	(1 << 0)  /* Data-ready line selection: 1 = DIO1, 0 = DIO0 */ + +/* DIAG_STAT */ +#define ADIS16201_DIAG_STAT_ALARM2        (1<<9) /* Alarm 2 status: 1 = alarm active, 0 = alarm inactive */ +#define ADIS16201_DIAG_STAT_ALARM1        (1<<8) /* Alarm 1 status: 1 = alarm active, 0 = alarm inactive */ +#define ADIS16201_DIAG_STAT_SPI_FAIL_BIT   3 /* SPI communications failure */ +#define ADIS16201_DIAG_STAT_FLASH_UPT_BIT  2 /* Flash update failure */ +#define ADIS16201_DIAG_STAT_POWER_HIGH_BIT 1 /* Power supply above 3.625 V */ +#define ADIS16201_DIAG_STAT_POWER_LOW_BIT  0 /* Power supply below 3.15 V */ + +/* GLOB_CMD */ +#define ADIS16201_GLOB_CMD_SW_RESET	(1<<7) +#define ADIS16201_GLOB_CMD_FACTORY_CAL	(1<<1) + +#define ADIS16201_ERROR_ACTIVE          (1<<14) + +enum adis16201_scan { +	ADIS16201_SCAN_ACC_X, +	ADIS16201_SCAN_ACC_Y, +	ADIS16201_SCAN_INCLI_X, +	ADIS16201_SCAN_INCLI_Y, +	ADIS16201_SCAN_SUPPLY, +	ADIS16201_SCAN_AUX_ADC, +	ADIS16201_SCAN_TEMP, +}; + +#endif /* SPI_ADIS16201_H_ */ diff --git a/drivers/staging/iio/accel/adis16201_core.c b/drivers/staging/iio/accel/adis16201_core.c new file mode 100644 index 00000000000..2105576fa77 --- /dev/null +++ b/drivers/staging/iio/accel/adis16201_core.c @@ -0,0 +1,247 @@ +/* + * ADIS16201 Programmable Digital Vibration Sensor driver + * + * Copyright 2010 Analog Devices Inc. + * + * Licensed under the GPL-2 or later. + */ + +#include <linux/delay.h> +#include <linux/mutex.h> +#include <linux/device.h> +#include <linux/kernel.h> +#include <linux/spi/spi.h> +#include <linux/slab.h> +#include <linux/sysfs.h> +#include <linux/module.h> + +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> +#include <linux/iio/buffer.h> +#include <linux/iio/imu/adis.h> + +#include "adis16201.h" + +static const u8 adis16201_addresses[] = { +	[ADIS16201_SCAN_ACC_X] = ADIS16201_XACCL_OFFS, +	[ADIS16201_SCAN_ACC_Y] = ADIS16201_YACCL_OFFS, +	[ADIS16201_SCAN_INCLI_X] = ADIS16201_XINCL_OFFS, +	[ADIS16201_SCAN_INCLI_Y] = ADIS16201_YINCL_OFFS, +}; + +static int adis16201_read_raw(struct iio_dev *indio_dev, +			      struct iio_chan_spec const *chan, +			      int *val, int *val2, +			      long mask) +{ +	struct adis *st = iio_priv(indio_dev); +	int ret; +	int bits; +	u8 addr; +	s16 val16; + +	switch (mask) { +	case IIO_CHAN_INFO_RAW: +		return adis_single_conversion(indio_dev, chan, +				ADIS16201_ERROR_ACTIVE, val); +	case IIO_CHAN_INFO_SCALE: +		switch (chan->type) { +		case IIO_VOLTAGE: +			if (chan->channel == 0) { +				*val = 1; +				*val2 = 220000; /* 1.22 mV */ +			} else { +				*val = 0; +				*val2 = 610000; /* 0.610 mV */ +			} +			return IIO_VAL_INT_PLUS_MICRO; +		case IIO_TEMP: +			*val = -470; /* 0.47 C */ +			*val2 = 0; +			return IIO_VAL_INT_PLUS_MICRO; +		case IIO_ACCEL: +			*val = 0; +			*val2 = IIO_G_TO_M_S_2(462400); /* 0.4624 mg */ +			return IIO_VAL_INT_PLUS_NANO; +		case IIO_INCLI: +			*val = 0; +			*val2 = 100000; /* 0.1 degree */ +			return IIO_VAL_INT_PLUS_MICRO; +		default: +			return -EINVAL; +		} +		break; +	case IIO_CHAN_INFO_OFFSET: +		*val = 25000 / -470 - 1278; /* 25 C = 1278 */ +		return IIO_VAL_INT; +	case IIO_CHAN_INFO_CALIBBIAS: +		switch (chan->type) { +		case IIO_ACCEL: +			bits = 12; +			break; +		case IIO_INCLI: +			bits = 9; +			break; +		default: +			return -EINVAL; +		} +		mutex_lock(&indio_dev->mlock); +		addr = adis16201_addresses[chan->scan_index]; +		ret = adis_read_reg_16(st, addr, &val16); +		if (ret) { +			mutex_unlock(&indio_dev->mlock); +			return ret; +		} +		val16 &= (1 << bits) - 1; +		val16 = (s16)(val16 << (16 - bits)) >> (16 - bits); +		*val = val16; +		mutex_unlock(&indio_dev->mlock); +		return IIO_VAL_INT; +	} +	return -EINVAL; +} + +static int adis16201_write_raw(struct iio_dev *indio_dev, +			       struct iio_chan_spec const *chan, +			       int val, +			       int val2, +			       long mask) +{ +	struct adis *st = iio_priv(indio_dev); +	int bits; +	s16 val16; +	u8 addr; +	switch (mask) { +	case IIO_CHAN_INFO_CALIBBIAS: +		switch (chan->type) { +		case IIO_ACCEL: +			bits = 12; +			break; +		case IIO_INCLI: +			bits = 9; +			break; +		default: +			return -EINVAL; +		} +		val16 = val & ((1 << bits) - 1); +		addr = adis16201_addresses[chan->scan_index]; +		return adis_write_reg_16(st, addr, val16); +	} +	return -EINVAL; +} + +static const struct iio_chan_spec adis16201_channels[] = { +	ADIS_SUPPLY_CHAN(ADIS16201_SUPPLY_OUT, ADIS16201_SCAN_SUPPLY, 12), +	ADIS_TEMP_CHAN(ADIS16201_TEMP_OUT, ADIS16201_SCAN_TEMP, 12), +	ADIS_ACCEL_CHAN(X, ADIS16201_XACCL_OUT, ADIS16201_SCAN_ACC_X, +		BIT(IIO_CHAN_INFO_CALIBBIAS), 14), +	ADIS_ACCEL_CHAN(Y, ADIS16201_YACCL_OUT, ADIS16201_SCAN_ACC_Y, +		BIT(IIO_CHAN_INFO_CALIBBIAS), 14), +	ADIS_AUX_ADC_CHAN(ADIS16201_AUX_ADC, ADIS16201_SCAN_AUX_ADC, 12), +	ADIS_INCLI_CHAN(X, ADIS16201_XINCL_OUT, ADIS16201_SCAN_INCLI_X, +		BIT(IIO_CHAN_INFO_CALIBBIAS), 14), +	ADIS_INCLI_CHAN(X, ADIS16201_YINCL_OUT, ADIS16201_SCAN_INCLI_Y, +		BIT(IIO_CHAN_INFO_CALIBBIAS), 14), +	IIO_CHAN_SOFT_TIMESTAMP(7) +}; + +static const struct iio_info adis16201_info = { +	.read_raw = &adis16201_read_raw, +	.write_raw = &adis16201_write_raw, +	.update_scan_mode = adis_update_scan_mode, +	.driver_module = THIS_MODULE, +}; + +static const char * const adis16201_status_error_msgs[] = { +	[ADIS16201_DIAG_STAT_SPI_FAIL_BIT] = "SPI failure", +	[ADIS16201_DIAG_STAT_FLASH_UPT_BIT] = "Flash update failed", +	[ADIS16201_DIAG_STAT_POWER_HIGH_BIT] = "Power supply above 3.625V", +	[ADIS16201_DIAG_STAT_POWER_LOW_BIT] = "Power supply below 3.15V", +}; + +static const struct adis_data adis16201_data = { +	.read_delay = 20, +	.msc_ctrl_reg = ADIS16201_MSC_CTRL, +	.glob_cmd_reg = ADIS16201_GLOB_CMD, +	.diag_stat_reg = ADIS16201_DIAG_STAT, + +	.self_test_mask = ADIS16201_MSC_CTRL_SELF_TEST_EN, +	.startup_delay = ADIS16201_STARTUP_DELAY, + +	.status_error_msgs = adis16201_status_error_msgs, +	.status_error_mask = BIT(ADIS16201_DIAG_STAT_SPI_FAIL_BIT) | +		BIT(ADIS16201_DIAG_STAT_FLASH_UPT_BIT) | +		BIT(ADIS16201_DIAG_STAT_POWER_HIGH_BIT) | +		BIT(ADIS16201_DIAG_STAT_POWER_LOW_BIT), +}; + +static int adis16201_probe(struct spi_device *spi) +{ +	int ret; +	struct adis *st; +	struct iio_dev *indio_dev; + +	/* setup the industrialio driver allocated elements */ +	indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); +	if (!indio_dev) +		return -ENOMEM; + +	st = iio_priv(indio_dev); +	/* this is only used for removal purposes */ +	spi_set_drvdata(spi, indio_dev); + +	indio_dev->name = spi->dev.driver->name; +	indio_dev->dev.parent = &spi->dev; +	indio_dev->info = &adis16201_info; + +	indio_dev->channels = adis16201_channels; +	indio_dev->num_channels = ARRAY_SIZE(adis16201_channels); +	indio_dev->modes = INDIO_DIRECT_MODE; + +	ret = adis_init(st, indio_dev, spi, &adis16201_data); +	if (ret) +		return ret; +	ret = adis_setup_buffer_and_trigger(st, indio_dev, NULL); +	if (ret) +		return ret; + +	/* Get the device into a sane initial state */ +	ret = adis_initial_startup(st); +	if (ret) +		goto error_cleanup_buffer_trigger; + +	ret = iio_device_register(indio_dev); +	if (ret < 0) +		goto error_cleanup_buffer_trigger; +	return 0; + +error_cleanup_buffer_trigger: +	adis_cleanup_buffer_and_trigger(st, indio_dev); +	return ret; +} + +static int adis16201_remove(struct spi_device *spi) +{ +	struct iio_dev *indio_dev = spi_get_drvdata(spi); +	struct adis *st = iio_priv(indio_dev); + +	iio_device_unregister(indio_dev); +	adis_cleanup_buffer_and_trigger(st, indio_dev); + +	return 0; +} + +static struct spi_driver adis16201_driver = { +	.driver = { +		.name = "adis16201", +		.owner = THIS_MODULE, +	}, +	.probe = adis16201_probe, +	.remove = adis16201_remove, +}; +module_spi_driver(adis16201_driver); + +MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>"); +MODULE_DESCRIPTION("Analog Devices ADIS16201 Programmable Digital Vibration Sensor driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("spi:adis16201"); diff --git a/drivers/staging/iio/accel/adis16203.h b/drivers/staging/iio/accel/adis16203.h new file mode 100644 index 00000000000..acc688d7ea9 --- /dev/null +++ b/drivers/staging/iio/accel/adis16203.h @@ -0,0 +1,59 @@ +#ifndef SPI_ADIS16203_H_ +#define SPI_ADIS16203_H_ + +#define ADIS16203_STARTUP_DELAY	220 /* ms */ + +#define ADIS16203_FLASH_CNT      0x00 /* Flash memory write count */ +#define ADIS16203_SUPPLY_OUT     0x02 /* Output, power supply */ +#define ADIS16203_AUX_ADC        0x08 /* Output, auxiliary ADC input */ +#define ADIS16203_TEMP_OUT       0x0A /* Output, temperature */ +#define ADIS16203_XINCL_OUT      0x0C /* Output, x-axis inclination */ +#define ADIS16203_YINCL_OUT      0x0E /* Output, y-axis inclination */ +#define ADIS16203_INCL_NULL      0x18 /* Incline null calibration */ +#define ADIS16203_ALM_MAG1       0x20 /* Alarm 1 amplitude threshold */ +#define ADIS16203_ALM_MAG2       0x22 /* Alarm 2 amplitude threshold */ +#define ADIS16203_ALM_SMPL1      0x24 /* Alarm 1, sample period */ +#define ADIS16203_ALM_SMPL2      0x26 /* Alarm 2, sample period */ +#define ADIS16203_ALM_CTRL       0x28 /* Alarm control */ +#define ADIS16203_AUX_DAC        0x30 /* Auxiliary DAC data */ +#define ADIS16203_GPIO_CTRL      0x32 /* General-purpose digital input/output control */ +#define ADIS16203_MSC_CTRL       0x34 /* Miscellaneous control */ +#define ADIS16203_SMPL_PRD       0x36 /* Internal sample period (rate) control */ +#define ADIS16203_AVG_CNT        0x38 /* Operation, filter configuration */ +#define ADIS16203_SLP_CNT        0x3A /* Operation, sleep mode control */ +#define ADIS16203_DIAG_STAT      0x3C /* Diagnostics, system status register */ +#define ADIS16203_GLOB_CMD       0x3E /* Operation, system command register */ + +/* MSC_CTRL */ +#define ADIS16203_MSC_CTRL_PWRUP_SELF_TEST	(1 << 10) /* Self-test at power-on: 1 = disabled, 0 = enabled */ +#define ADIS16203_MSC_CTRL_REVERSE_ROT_EN	(1 << 9)  /* Reverses rotation of both inclination outputs */ +#define ADIS16203_MSC_CTRL_SELF_TEST_EN	        (1 << 8)  /* Self-test enable */ +#define ADIS16203_MSC_CTRL_DATA_RDY_EN	        (1 << 2)  /* Data-ready enable: 1 = enabled, 0 = disabled */ +#define ADIS16203_MSC_CTRL_ACTIVE_HIGH	        (1 << 1)  /* Data-ready polarity: 1 = active high, 0 = active low */ +#define ADIS16203_MSC_CTRL_DATA_RDY_DIO1	(1 << 0)  /* Data-ready line selection: 1 = DIO1, 0 = DIO0 */ + +/* DIAG_STAT */ +#define ADIS16203_DIAG_STAT_ALARM2        (1<<9) /* Alarm 2 status: 1 = alarm active, 0 = alarm inactive */ +#define ADIS16203_DIAG_STAT_ALARM1        (1<<8) /* Alarm 1 status: 1 = alarm active, 0 = alarm inactive */ +#define ADIS16203_DIAG_STAT_SELFTEST_FAIL_BIT 5 /* Self-test diagnostic error flag */ +#define ADIS16203_DIAG_STAT_SPI_FAIL_BIT      3 /* SPI communications failure */ +#define ADIS16203_DIAG_STAT_FLASH_UPT_BIT     2 /* Flash update failure */ +#define ADIS16203_DIAG_STAT_POWER_HIGH_BIT    1 /* Power supply above 3.625 V */ +#define ADIS16203_DIAG_STAT_POWER_LOW_BIT     0 /* Power supply below 3.15 V */ + +/* GLOB_CMD */ +#define ADIS16203_GLOB_CMD_SW_RESET	(1<<7) +#define ADIS16203_GLOB_CMD_CLEAR_STAT	(1<<4) +#define ADIS16203_GLOB_CMD_FACTORY_CAL	(1<<1) + +#define ADIS16203_ERROR_ACTIVE          (1<<14) + +enum adis16203_scan { +	ADIS16203_SCAN_INCLI_X, +	ADIS16203_SCAN_INCLI_Y, +	ADIS16203_SCAN_SUPPLY, +	ADIS16203_SCAN_AUX_ADC, +	ADIS16203_SCAN_TEMP, +}; + +#endif /* SPI_ADIS16203_H_ */ diff --git a/drivers/staging/iio/accel/adis16203_core.c b/drivers/staging/iio/accel/adis16203_core.c new file mode 100644 index 00000000000..409a28ed904 --- /dev/null +++ b/drivers/staging/iio/accel/adis16203_core.c @@ -0,0 +1,213 @@ +/* + * ADIS16203 Programmable Digital Vibration Sensor driver + * + * Copyright 2030 Analog Devices Inc. + * + * Licensed under the GPL-2 or later. + */ + +#include <linux/delay.h> +#include <linux/mutex.h> +#include <linux/device.h> +#include <linux/kernel.h> +#include <linux/spi/spi.h> +#include <linux/slab.h> +#include <linux/sysfs.h> +#include <linux/module.h> + +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> +#include <linux/iio/buffer.h> +#include <linux/iio/imu/adis.h> + +#include "adis16203.h" + +#define DRIVER_NAME		"adis16203" + +static const u8 adis16203_addresses[] = { +	[ADIS16203_SCAN_INCLI_X] = ADIS16203_INCL_NULL, +}; + +static int adis16203_write_raw(struct iio_dev *indio_dev, +			       struct iio_chan_spec const *chan, +			       int val, +			       int val2, +			       long mask) +{ +	struct adis *st = iio_priv(indio_dev); +	/* currently only one writable parameter which keeps this simple */ +	u8 addr = adis16203_addresses[chan->scan_index]; +	return adis_write_reg_16(st, addr, val & 0x3FFF); +} + +static int adis16203_read_raw(struct iio_dev *indio_dev, +			      struct iio_chan_spec const *chan, +			      int *val, int *val2, +			      long mask) +{ +	struct adis *st = iio_priv(indio_dev); +	int ret; +	int bits; +	u8 addr; +	s16 val16; +	switch (mask) { +	case IIO_CHAN_INFO_RAW: +		return adis_single_conversion(indio_dev, chan, +				ADIS16203_ERROR_ACTIVE, val); +	case IIO_CHAN_INFO_SCALE: +		switch (chan->type) { +		case IIO_VOLTAGE: +			if (chan->channel == 0) { +				*val = 1; +				*val2 = 220000; /* 1.22 mV */ +			} else { +				*val = 0; +				*val2 = 610000; /* 0.61 mV */ +			} +			return IIO_VAL_INT_PLUS_MICRO; +		case IIO_TEMP: +			*val = -470; /* -0.47 C */ +			*val2 = 0; +			return IIO_VAL_INT_PLUS_MICRO; +		case IIO_INCLI: +			*val = 0; +			*val2 = 25000; /* 0.025 degree */ +			return IIO_VAL_INT_PLUS_MICRO; +		default: +			return -EINVAL; +		} +	case IIO_CHAN_INFO_OFFSET: +		*val = 25000 / -470 - 1278; /* 25 C = 1278 */ +		return IIO_VAL_INT; +	case IIO_CHAN_INFO_CALIBBIAS: +		bits = 14; +		mutex_lock(&indio_dev->mlock); +		addr = adis16203_addresses[chan->scan_index]; +		ret = adis_read_reg_16(st, addr, &val16); +		if (ret) { +			mutex_unlock(&indio_dev->mlock); +			return ret; +		} +		val16 &= (1 << bits) - 1; +		val16 = (s16)(val16 << (16 - bits)) >> (16 - bits); +		*val = val16; +		mutex_unlock(&indio_dev->mlock); +		return IIO_VAL_INT; +	default: +		return -EINVAL; +	} +} + +static const struct iio_chan_spec adis16203_channels[] = { +	ADIS_SUPPLY_CHAN(ADIS16203_SUPPLY_OUT, ADIS16203_SCAN_SUPPLY, 12), +	ADIS_AUX_ADC_CHAN(ADIS16203_AUX_ADC, ADIS16203_SCAN_AUX_ADC, 12), +	ADIS_INCLI_CHAN(X, ADIS16203_XINCL_OUT, ADIS16203_SCAN_INCLI_X, +		BIT(IIO_CHAN_INFO_CALIBBIAS), 14), +	/* Fixme: Not what it appears to be - see data sheet */ +	ADIS_INCLI_CHAN(Y, ADIS16203_YINCL_OUT, ADIS16203_SCAN_INCLI_Y, 0, 14), +	ADIS_TEMP_CHAN(ADIS16203_TEMP_OUT, ADIS16203_SCAN_TEMP, 12), +	IIO_CHAN_SOFT_TIMESTAMP(5), +}; + +static const struct iio_info adis16203_info = { +	.read_raw = &adis16203_read_raw, +	.write_raw = &adis16203_write_raw, +	.update_scan_mode = adis_update_scan_mode, +	.driver_module = THIS_MODULE, +}; + +static const char * const adis16203_status_error_msgs[] = { +	[ADIS16203_DIAG_STAT_SELFTEST_FAIL_BIT] = "Self test failure", +	[ADIS16203_DIAG_STAT_SPI_FAIL_BIT] = "SPI failure", +	[ADIS16203_DIAG_STAT_FLASH_UPT_BIT] = "Flash update failed", +	[ADIS16203_DIAG_STAT_POWER_HIGH_BIT] = "Power supply above 3.625V", +	[ADIS16203_DIAG_STAT_POWER_LOW_BIT] = "Power supply below 3.15V", +}; + +static const struct adis_data adis16203_data = { +	.read_delay = 20, +	.msc_ctrl_reg = ADIS16203_MSC_CTRL, +	.glob_cmd_reg = ADIS16203_GLOB_CMD, +	.diag_stat_reg = ADIS16203_DIAG_STAT, + +	.self_test_mask = ADIS16203_MSC_CTRL_SELF_TEST_EN, +	.startup_delay = ADIS16203_STARTUP_DELAY, + +	.status_error_msgs = adis16203_status_error_msgs, +	.status_error_mask = BIT(ADIS16203_DIAG_STAT_SELFTEST_FAIL_BIT) | +		BIT(ADIS16203_DIAG_STAT_SPI_FAIL_BIT) | +		BIT(ADIS16203_DIAG_STAT_FLASH_UPT_BIT) | +		BIT(ADIS16203_DIAG_STAT_POWER_HIGH_BIT) | +		BIT(ADIS16203_DIAG_STAT_POWER_LOW_BIT), +}; + +static int adis16203_probe(struct spi_device *spi) +{ +	int ret; +	struct iio_dev *indio_dev; +	struct adis *st; + +	/* setup the industrialio driver allocated elements */ +	indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); +	if (!indio_dev) +		return -ENOMEM; +	st = iio_priv(indio_dev); +	/* this is only used for removal purposes */ +	spi_set_drvdata(spi, indio_dev); + +	indio_dev->name = spi->dev.driver->name; +	indio_dev->dev.parent = &spi->dev; +	indio_dev->channels = adis16203_channels; +	indio_dev->num_channels = ARRAY_SIZE(adis16203_channels); +	indio_dev->info = &adis16203_info; +	indio_dev->modes = INDIO_DIRECT_MODE; + +	ret = adis_init(st, indio_dev, spi, &adis16203_data); +	if (ret) +		return ret; + +	ret = adis_setup_buffer_and_trigger(st, indio_dev, NULL); +	if (ret) +		return ret; + +	/* Get the device into a sane initial state */ +	ret = adis_initial_startup(st); +	if (ret) +		goto error_cleanup_buffer_trigger; + +	ret = iio_device_register(indio_dev); +	if (ret) +		goto error_cleanup_buffer_trigger; + +	return 0; + +error_cleanup_buffer_trigger: +	adis_cleanup_buffer_and_trigger(st, indio_dev); +	return ret; +} + +static int adis16203_remove(struct spi_device *spi) +{ +	struct iio_dev *indio_dev = spi_get_drvdata(spi); +	struct adis *st = iio_priv(indio_dev); + +	iio_device_unregister(indio_dev); +	adis_cleanup_buffer_and_trigger(st, indio_dev); + +	return 0; +} + +static struct spi_driver adis16203_driver = { +	.driver = { +		.name = "adis16203", +		.owner = THIS_MODULE, +	}, +	.probe = adis16203_probe, +	.remove = adis16203_remove, +}; +module_spi_driver(adis16203_driver); + +MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>"); +MODULE_DESCRIPTION("Analog Devices ADIS16203 Programmable Digital Vibration Sensor driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("spi:adis16203"); diff --git a/drivers/staging/iio/accel/adis16204.h b/drivers/staging/iio/accel/adis16204.h new file mode 100644 index 00000000000..9ff950c1e8d --- /dev/null +++ b/drivers/staging/iio/accel/adis16204.h @@ -0,0 +1,68 @@ +#ifndef SPI_ADIS16204_H_ +#define SPI_ADIS16204_H_ + +#define ADIS16204_STARTUP_DELAY	220 /* ms */ + +#define ADIS16204_FLASH_CNT      0x00 /* Flash memory write count */ +#define ADIS16204_SUPPLY_OUT     0x02 /* Output, power supply */ +#define ADIS16204_XACCL_OUT      0x04 /* Output, x-axis accelerometer */ +#define ADIS16204_YACCL_OUT      0x06 /* Output, y-axis accelerometer */ +#define ADIS16204_AUX_ADC        0x08 /* Output, auxiliary ADC input */ +#define ADIS16204_TEMP_OUT       0x0A /* Output, temperature */ +#define ADIS16204_X_PEAK_OUT     0x0C /* Twos complement */ +#define ADIS16204_Y_PEAK_OUT     0x0E /* Twos complement */ +#define ADIS16204_XACCL_NULL     0x10 /* Calibration, x-axis acceleration offset null */ +#define ADIS16204_YACCL_NULL     0x12 /* Calibration, y-axis acceleration offset null */ +#define ADIS16204_XACCL_SCALE    0x14 /* X-axis scale factor calibration register */ +#define ADIS16204_YACCL_SCALE    0x16 /* Y-axis scale factor calibration register */ +#define ADIS16204_XY_RSS_OUT     0x18 /* XY combined acceleration (RSS) */ +#define ADIS16204_XY_PEAK_OUT    0x1A /* Peak, XY combined output (RSS) */ +#define ADIS16204_CAP_BUF_1      0x1C /* Capture buffer output register 1 */ +#define ADIS16204_CAP_BUF_2      0x1E /* Capture buffer output register 2 */ +#define ADIS16204_ALM_MAG1       0x20 /* Alarm 1 amplitude threshold */ +#define ADIS16204_ALM_MAG2       0x22 /* Alarm 2 amplitude threshold */ +#define ADIS16204_ALM_CTRL       0x28 /* Alarm control */ +#define ADIS16204_CAPT_PNTR      0x2A /* Capture register address pointer */ +#define ADIS16204_AUX_DAC        0x30 /* Auxiliary DAC data */ +#define ADIS16204_GPIO_CTRL      0x32 /* General-purpose digital input/output control */ +#define ADIS16204_MSC_CTRL       0x34 /* Miscellaneous control */ +#define ADIS16204_SMPL_PRD       0x36 /* Internal sample period (rate) control */ +#define ADIS16204_AVG_CNT        0x38 /* Operation, filter configuration */ +#define ADIS16204_SLP_CNT        0x3A /* Operation, sleep mode control */ +#define ADIS16204_DIAG_STAT      0x3C /* Diagnostics, system status register */ +#define ADIS16204_GLOB_CMD       0x3E /* Operation, system command register */ + +/* MSC_CTRL */ +#define ADIS16204_MSC_CTRL_PWRUP_SELF_TEST	(1 << 10) /* Self-test at power-on: 1 = disabled, 0 = enabled */ +#define ADIS16204_MSC_CTRL_SELF_TEST_EN	        (1 << 8)  /* Self-test enable */ +#define ADIS16204_MSC_CTRL_DATA_RDY_EN	        (1 << 2)  /* Data-ready enable: 1 = enabled, 0 = disabled */ +#define ADIS16204_MSC_CTRL_ACTIVE_HIGH	        (1 << 1)  /* Data-ready polarity: 1 = active high, 0 = active low */ +#define ADIS16204_MSC_CTRL_DATA_RDY_DIO2	(1 << 0)  /* Data-ready line selection: 1 = DIO2, 0 = DIO1 */ + +/* DIAG_STAT */ +#define ADIS16204_DIAG_STAT_ALARM2        (1<<9) /* Alarm 2 status: 1 = alarm active, 0 = alarm inactive */ +#define ADIS16204_DIAG_STAT_ALARM1        (1<<8) /* Alarm 1 status: 1 = alarm active, 0 = alarm inactive */ +#define ADIS16204_DIAG_STAT_SELFTEST_FAIL_BIT 5 /* Self-test diagnostic error flag: 1 = error condition, +						0 = normal operation */ +#define ADIS16204_DIAG_STAT_SPI_FAIL_BIT      3 /* SPI communications failure */ +#define ADIS16204_DIAG_STAT_FLASH_UPT_BIT     2 /* Flash update failure */ +#define ADIS16204_DIAG_STAT_POWER_HIGH_BIT    1 /* Power supply above 3.625 V */ +#define ADIS16204_DIAG_STAT_POWER_LOW_BIT     0 /* Power supply below 2.975 V */ + +/* GLOB_CMD */ +#define ADIS16204_GLOB_CMD_SW_RESET	(1<<7) +#define ADIS16204_GLOB_CMD_CLEAR_STAT	(1<<4) +#define ADIS16204_GLOB_CMD_FACTORY_CAL	(1<<1) + +#define ADIS16204_ERROR_ACTIVE          (1<<14) + +enum adis16204_scan { +	ADIS16204_SCAN_ACC_X, +	ADIS16204_SCAN_ACC_Y, +	ADIS16204_SCAN_ACC_XY, +	ADIS16204_SCAN_SUPPLY, +	ADIS16204_SCAN_AUX_ADC, +	ADIS16204_SCAN_TEMP, +}; + +#endif /* SPI_ADIS16204_H_ */ diff --git a/drivers/staging/iio/accel/adis16204_core.c b/drivers/staging/iio/accel/adis16204_core.c new file mode 100644 index 00000000000..b8ea76857cd --- /dev/null +++ b/drivers/staging/iio/accel/adis16204_core.c @@ -0,0 +1,251 @@ +/* + * ADIS16204 Programmable High-g Digital Impact Sensor and Recorder + * + * Copyright 2010 Analog Devices Inc. + * + * Licensed under the GPL-2 or later. + */ + +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/delay.h> +#include <linux/mutex.h> +#include <linux/device.h> +#include <linux/kernel.h> +#include <linux/spi/spi.h> +#include <linux/slab.h> +#include <linux/sysfs.h> +#include <linux/list.h> +#include <linux/module.h> + +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> +#include <linux/iio/buffer.h> +#include <linux/iio/imu/adis.h> + +#include "adis16204.h" + +/* Unique to this driver currently */ + +static const u8 adis16204_addresses[][2] = { +	[ADIS16204_SCAN_ACC_X] = { ADIS16204_XACCL_NULL, ADIS16204_X_PEAK_OUT }, +	[ADIS16204_SCAN_ACC_Y] = { ADIS16204_YACCL_NULL, ADIS16204_Y_PEAK_OUT }, +	[ADIS16204_SCAN_ACC_XY] = { 0, ADIS16204_XY_PEAK_OUT }, +}; + +static int adis16204_read_raw(struct iio_dev *indio_dev, +			      struct iio_chan_spec const *chan, +			      int *val, int *val2, +			      long mask) +{ +	struct adis *st = iio_priv(indio_dev); +	int ret; +	int bits; +	u8 addr; +	s16 val16; +	int addrind; + +	switch (mask) { +	case IIO_CHAN_INFO_RAW: +		return adis_single_conversion(indio_dev, chan, +				ADIS16204_ERROR_ACTIVE, val); +	case IIO_CHAN_INFO_SCALE: +		switch (chan->type) { +		case IIO_VOLTAGE: +			if (chan->channel == 0) { +				*val = 1; +				*val2 = 220000; /* 1.22 mV */ +			} else { +				*val = 0; +				*val2 = 610000; /* 0.61 mV */ +			} +			return IIO_VAL_INT_PLUS_MICRO; +		case IIO_TEMP: +			*val = -470; /* 0.47 C */ +			*val2 = 0; +			return IIO_VAL_INT_PLUS_MICRO; +		case IIO_ACCEL: +			*val = 0; +			switch (chan->channel2) { +			case IIO_MOD_X: +			case IIO_MOD_ROOT_SUM_SQUARED_X_Y: +				*val2 = IIO_G_TO_M_S_2(17125); /* 17.125 mg */ +				break; +			case IIO_MOD_Y: +			case IIO_MOD_Z: +				*val2 = IIO_G_TO_M_S_2(8407); /* 8.407 mg */ +				break; +			} +			return IIO_VAL_INT_PLUS_MICRO; +		default: +			return -EINVAL; +		} +		break; +	case IIO_CHAN_INFO_OFFSET: +		*val = 25000 / -470 - 1278; /* 25 C = 1278 */ +		return IIO_VAL_INT; +	case IIO_CHAN_INFO_CALIBBIAS: +	case IIO_CHAN_INFO_PEAK: +		if (mask == IIO_CHAN_INFO_CALIBBIAS) { +			bits = 12; +			addrind = 0; +		} else { /* PEAK_SEPARATE */ +			bits = 14; +			addrind = 1; +		} +		mutex_lock(&indio_dev->mlock); +		addr = adis16204_addresses[chan->scan_index][addrind]; +		ret = adis_read_reg_16(st, addr, &val16); +		if (ret) { +			mutex_unlock(&indio_dev->mlock); +			return ret; +		} +		val16 &= (1 << bits) - 1; +		val16 = (s16)(val16 << (16 - bits)) >> (16 - bits); +		*val = val16; +		mutex_unlock(&indio_dev->mlock); +		return IIO_VAL_INT; +	} +	return -EINVAL; +} + +static int adis16204_write_raw(struct iio_dev *indio_dev, +			       struct iio_chan_spec const *chan, +			       int val, +			       int val2, +			       long mask) +{ +	struct adis *st = iio_priv(indio_dev); +	int bits; +	s16 val16; +	u8 addr; +	switch (mask) { +	case IIO_CHAN_INFO_CALIBBIAS: +		switch (chan->type) { +		case IIO_ACCEL: +			bits = 12; +			break; +		default: +			return -EINVAL; +		} +		val16 = val & ((1 << bits) - 1); +		addr = adis16204_addresses[chan->scan_index][1]; +		return adis_write_reg_16(st, addr, val16); +	} +	return -EINVAL; +} + +static const struct iio_chan_spec adis16204_channels[] = { +	ADIS_SUPPLY_CHAN(ADIS16204_SUPPLY_OUT, ADIS16204_SCAN_SUPPLY, 12), +	ADIS_AUX_ADC_CHAN(ADIS16204_AUX_ADC, ADIS16204_SCAN_AUX_ADC, 12), +	ADIS_TEMP_CHAN(ADIS16204_TEMP_OUT, ADIS16204_SCAN_TEMP, 12), +	ADIS_ACCEL_CHAN(X, ADIS16204_XACCL_OUT, ADIS16204_SCAN_ACC_X, +		BIT(IIO_CHAN_INFO_CALIBBIAS) | BIT(IIO_CHAN_INFO_PEAK), 14), +	ADIS_ACCEL_CHAN(Y, ADIS16204_YACCL_OUT, ADIS16204_SCAN_ACC_Y, +		BIT(IIO_CHAN_INFO_CALIBBIAS) | BIT(IIO_CHAN_INFO_PEAK), 14), +	ADIS_ACCEL_CHAN(ROOT_SUM_SQUARED_X_Y, ADIS16204_XY_RSS_OUT, +		ADIS16204_SCAN_ACC_XY, BIT(IIO_CHAN_INFO_PEAK), 14), +	IIO_CHAN_SOFT_TIMESTAMP(5), +}; + +static const struct iio_info adis16204_info = { +	.read_raw = &adis16204_read_raw, +	.write_raw = &adis16204_write_raw, +	.update_scan_mode = adis_update_scan_mode, +	.driver_module = THIS_MODULE, +}; + +static const char * const adis16204_status_error_msgs[] = { +	[ADIS16204_DIAG_STAT_SELFTEST_FAIL_BIT] = "Self test failure", +	[ADIS16204_DIAG_STAT_SPI_FAIL_BIT] = "SPI failure", +	[ADIS16204_DIAG_STAT_FLASH_UPT_BIT] = "Flash update failed", +	[ADIS16204_DIAG_STAT_POWER_HIGH_BIT] = "Power supply above 3.625V", +	[ADIS16204_DIAG_STAT_POWER_LOW_BIT] = "Power supply below 2.975V", +}; + +static const struct adis_data adis16204_data = { +	.read_delay = 20, +	.msc_ctrl_reg = ADIS16204_MSC_CTRL, +	.glob_cmd_reg = ADIS16204_GLOB_CMD, +	.diag_stat_reg = ADIS16204_DIAG_STAT, + +	.self_test_mask = ADIS16204_MSC_CTRL_SELF_TEST_EN, +	.startup_delay = ADIS16204_STARTUP_DELAY, + +	.status_error_msgs = adis16204_status_error_msgs, +	.status_error_mask = BIT(ADIS16204_DIAG_STAT_SELFTEST_FAIL_BIT) | +		BIT(ADIS16204_DIAG_STAT_SPI_FAIL_BIT) | +		BIT(ADIS16204_DIAG_STAT_FLASH_UPT_BIT) | +		BIT(ADIS16204_DIAG_STAT_POWER_HIGH_BIT) | +		BIT(ADIS16204_DIAG_STAT_POWER_LOW_BIT), +}; + +static int adis16204_probe(struct spi_device *spi) +{ +	int ret; +	struct adis *st; +	struct iio_dev *indio_dev; + +	/* setup the industrialio driver allocated elements */ +	indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); +	if (!indio_dev) +		return -ENOMEM; +	st = iio_priv(indio_dev); +	/* this is only used for removal purposes */ +	spi_set_drvdata(spi, indio_dev); + +	indio_dev->name = spi->dev.driver->name; +	indio_dev->dev.parent = &spi->dev; +	indio_dev->info = &adis16204_info; +	indio_dev->channels = adis16204_channels; +	indio_dev->num_channels = ARRAY_SIZE(adis16204_channels); +	indio_dev->modes = INDIO_DIRECT_MODE; + +	ret = adis_init(st, indio_dev, spi, &adis16204_data); +	if (ret) +		return ret; + +	ret = adis_setup_buffer_and_trigger(st, indio_dev, NULL); +	if (ret) +		return ret; + +	/* Get the device into a sane initial state */ +	ret = adis_initial_startup(st); +	if (ret) +		goto error_cleanup_buffer_trigger; +	ret = iio_device_register(indio_dev); +	if (ret) +		goto error_cleanup_buffer_trigger; + +	return 0; + +error_cleanup_buffer_trigger: +	adis_cleanup_buffer_and_trigger(st, indio_dev); +	return ret; +} + +static int adis16204_remove(struct spi_device *spi) +{ +	struct iio_dev *indio_dev = spi_get_drvdata(spi); +	struct adis *st = iio_priv(indio_dev); + +	iio_device_unregister(indio_dev); +	adis_cleanup_buffer_and_trigger(st, indio_dev); + +	return 0; +} + +static struct spi_driver adis16204_driver = { +	.driver = { +		.name = "adis16204", +		.owner = THIS_MODULE, +	}, +	.probe = adis16204_probe, +	.remove = adis16204_remove, +}; +module_spi_driver(adis16204_driver); + +MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>"); +MODULE_DESCRIPTION("ADIS16204 High-g Digital Impact Sensor and Recorder"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("spi:adis16204"); diff --git a/drivers/staging/iio/accel/adis16209.h b/drivers/staging/iio/accel/adis16209.h index 4e97596620e..ad3945a0629 100644 --- a/drivers/staging/iio/accel/adis16209.h +++ b/drivers/staging/iio/accel/adis16209.h @@ -3,9 +3,6 @@  #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 */ @@ -61,8 +58,6 @@  /* 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) @@ -81,52 +76,23 @@  /* 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) +#define ADIS16209_DIAG_STAT_SELFTEST_FAIL_BIT	5  /* SPI communications failure */ -#define ADIS16209_DIAG_STAT_SPI_FAIL	  (1<<3) +#define ADIS16209_DIAG_STAT_SPI_FAIL_BIT	3  /* Flash update failure */ -#define ADIS16209_DIAG_STAT_FLASH_UPT	  (1<<2) +#define ADIS16209_DIAG_STAT_FLASH_UPT_BIT	2  /* Power supply above 3.625 V */ -#define ADIS16209_DIAG_STAT_POWER_HIGH	  (1<<1) +#define ADIS16209_DIAG_STAT_POWER_HIGH_BIT	1  /* Power supply below 3.15 V */ -#define ADIS16209_DIAG_STAT_POWER_LOW	  (1<<0) +#define ADIS16209_DIAG_STAT_POWER_LOW_BIT	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 - * @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; -	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 -  #define ADIS16209_SCAN_SUPPLY	0  #define ADIS16209_SCAN_ACC_X	1  #define ADIS16209_SCAN_ACC_Y	2 @@ -136,43 +102,4 @@ int adis16209_set_irq(struct device *dev, bool enable);  #define ADIS16209_SCAN_INCLI_Y	6  #define ADIS16209_SCAN_ROT	7 -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); - -#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) -{ -} - -#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 index e4ac956208a..4492e51d888 100644 --- a/drivers/staging/iio/accel/adis16209_core.c +++ b/drivers/staging/iio/accel/adis16209_core.c @@ -6,9 +6,6 @@   * 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> @@ -17,567 +14,218 @@  #include <linux/slab.h>  #include <linux/sysfs.h>  #include <linux/list.h> +#include <linux/module.h> -#include "../iio.h" -#include "../sysfs.h" -#include "../ring_generic.h" -#include "accel.h" -#include "inclinometer.h" -#include "../gyro/gyro.h" -#include "../adc/adc.h" +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> +#include <linux/iio/buffer.h> +#include <linux/iio/imu/adis.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, -			.delay_usecs = 30, -		}, { -			.tx_buf = st->tx + 2, -			.bits_per_word = 8, -			.len = 2, -			.cs_change = 1, -			.delay_usecs = 30, -		}, -	}; - -	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 = 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] = 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 const u8 adis16209_addresses[8][1] = { +	[ADIS16209_SCAN_SUPPLY] = { }, +	[ADIS16209_SCAN_AUX_ADC] = { }, +	[ADIS16209_SCAN_ACC_X] = { ADIS16209_XACCL_NULL }, +	[ADIS16209_SCAN_ACC_Y] = { ADIS16209_YACCL_NULL }, +	[ADIS16209_SCAN_INCLI_X] = { ADIS16209_XINCL_NULL }, +	[ADIS16209_SCAN_INCLI_Y] = { ADIS16209_YINCL_NULL }, +	[ADIS16209_SCAN_ROT] = { }, +	[ADIS16209_SCAN_TEMP] = { }, +}; -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); +static int adis16209_write_raw(struct iio_dev *indio_dev, +			       struct iio_chan_spec const *chan, +			       int val, +			       int val2, +			       long mask) +{ +	struct adis *st = iio_priv(indio_dev); +	int bits; +	s16 val16; +	u8 addr; +	switch (mask) { +	case IIO_CHAN_INFO_CALIBBIAS: +		switch (chan->type) { +		case IIO_ACCEL: +		case IIO_INCLI: +			bits = 14; +			break; +		default: +			return -EINVAL; +		} +		val16 = val & ((1 << bits) - 1); +		addr = adis16209_addresses[chan->scan_index][0]; +		return adis_write_reg_16(st, addr, val16);  	}  	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) +static int adis16209_read_raw(struct iio_dev *indio_dev, +			      struct iio_chan_spec const *chan, +			      int *val, int *val2, +			      long mask)  { +	struct adis *st = iio_priv(indio_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); +	int bits; +	u8 addr; +	s16 val16; + +	switch (mask) { +	case IIO_CHAN_INFO_RAW: +		return adis_single_conversion(indio_dev, chan, +			ADIS16209_ERROR_ACTIVE, val); +	case IIO_CHAN_INFO_SCALE: +		switch (chan->type) { +		case IIO_VOLTAGE: +			*val = 0; +			if (chan->channel == 0) +				*val2 = 305180; /* 0.30518 mV */ +			else +				*val2 = 610500; /* 0.6105 mV */ +			return IIO_VAL_INT_PLUS_MICRO; +		case IIO_TEMP: +			*val = -470; /* -0.47 C */ +			*val2 = 0; +			return IIO_VAL_INT_PLUS_MICRO; +		case IIO_ACCEL: +			*val = 0; +			*val2 = IIO_G_TO_M_S_2(244140); /* 0.244140 mg */ +			return IIO_VAL_INT_PLUS_NANO; +		case IIO_INCLI: +		case IIO_ROT: +			*val = 0; +			*val2 = 25000; /* 0.025 degree */ +			return IIO_VAL_INT_PLUS_MICRO; +		default: +			return -EINVAL; +		} +		break; +	case IIO_CHAN_INFO_OFFSET: +		*val = 25000 / -470 - 0x4FE; /* 25 C = 0x4FE */ +		return IIO_VAL_INT; +	case IIO_CHAN_INFO_CALIBBIAS: +		switch (chan->type) { +		case IIO_ACCEL: +			bits = 14; +			break; +		default: +			return -EINVAL; +		} +		mutex_lock(&indio_dev->mlock); +		addr = adis16209_addresses[chan->scan_index][0]; +		ret = adis_read_reg_16(st, addr, &val16);  		if (ret) { -			dev_err(dev, "giving up"); -			goto err_ret; +			mutex_unlock(&indio_dev->mlock); +			return ret;  		} +		val16 &= (1 << bits) - 1; +		val16 = (s16)(val16 << (16 - bits)) >> (16 - bits); +		*val = val16; +		mutex_unlock(&indio_dev->mlock); +		return IIO_VAL_INT;  	} - -	printk(KERN_INFO DRIVER_NAME ": at CS%d (irq %d)\n", -			st->us->chip_select, st->us->irq); - -err_ret: -	return ret; +	return -EINVAL;  } -static IIO_DEV_ATTR_IN_NAMED_RAW(0, supply, adis16209_read_14bit_unsigned, -		ADIS16209_SUPPLY_OUT); -static IIO_CONST_ATTR_IN_NAMED_SCALE(0, supply, "0.30518"); -static IIO_DEV_ATTR_IN_RAW(1, adis16209_read_12bit_unsigned, -		ADIS16209_AUX_ADC); -static IIO_CONST_ATTR(in1_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_CALIBBIAS(S_IWUSR | S_IRUGO, -		adis16209_read_14bit_signed, -		adis16209_write_16bit, -		ADIS16209_XACCL_NULL); -static IIO_DEV_ATTR_ACCEL_Y_CALIBBIAS(S_IWUSR | S_IRUGO, -		adis16209_read_14bit_signed, -		adis16209_write_16bit, -		ADIS16209_YACCL_NULL); -static IIO_CONST_ATTR_ACCEL_SCALE("0.002394195531"); - -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_CONST_ATTR(incli_scale, "0.00043633231"); - -static IIO_DEVICE_ATTR(rot_raw, S_IRUGO, adis16209_read_14bit_signed, -		       NULL, ADIS16209_ROT_OUT); - -static IIO_DEV_ATTR_TEMP_RAW(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 const struct iio_chan_spec adis16209_channels[] = { +	ADIS_SUPPLY_CHAN(ADIS16209_SUPPLY_OUT, ADIS16209_SCAN_SUPPLY, 14), +	ADIS_TEMP_CHAN(ADIS16209_TEMP_OUT, ADIS16209_SCAN_TEMP, 12), +	ADIS_ACCEL_CHAN(X, ADIS16209_XACCL_OUT, ADIS16209_SCAN_ACC_X, +		BIT(IIO_CHAN_INFO_CALIBBIAS), 14), +	ADIS_ACCEL_CHAN(Y, ADIS16209_YACCL_OUT, ADIS16209_SCAN_ACC_Y, +		BIT(IIO_CHAN_INFO_CALIBBIAS), 14), +	ADIS_AUX_ADC_CHAN(ADIS16209_AUX_ADC, ADIS16209_SCAN_AUX_ADC, 12), +	ADIS_INCLI_CHAN(X, ADIS16209_XINCL_OUT, ADIS16209_SCAN_INCLI_X, 0, 14), +	ADIS_INCLI_CHAN(Y, ADIS16209_YINCL_OUT, ADIS16209_SCAN_INCLI_Y, 0, 14), +	ADIS_ROT_CHAN(X, ADIS16209_ROT_OUT, ADIS16209_SCAN_ROT, 0, 14), +	IIO_CHAN_SOFT_TIMESTAMP(8)  }; -static struct attribute_group adis16209_event_attribute_group = { -	.attrs = adis16209_event_attributes, +static const struct iio_info adis16209_info = { +	.read_raw = &adis16209_read_raw, +	.write_raw = &adis16209_write_raw, +	.update_scan_mode = adis_update_scan_mode, +	.driver_module = THIS_MODULE,  }; -static struct attribute *adis16209_attributes[] = { -	&iio_dev_attr_in0_supply_raw.dev_attr.attr, -	&iio_const_attr_in0_supply_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_reset.dev_attr.attr, -	&iio_const_attr_name.dev_attr.attr, -	&iio_dev_attr_in1_raw.dev_attr.attr, -	&iio_const_attr_in1_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_calibbias.dev_attr.attr, -	&iio_dev_attr_accel_y_calibbias.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_rot_raw.dev_attr.attr, -	NULL +static const char * const adis16209_status_error_msgs[] = { +	[ADIS16209_DIAG_STAT_SELFTEST_FAIL_BIT] = "Self test failure", +	[ADIS16209_DIAG_STAT_SPI_FAIL_BIT] = "SPI failure", +	[ADIS16209_DIAG_STAT_FLASH_UPT_BIT] = "Flash update failed", +	[ADIS16209_DIAG_STAT_POWER_HIGH_BIT] = "Power supply above 3.625V", +	[ADIS16209_DIAG_STAT_POWER_LOW_BIT] = "Power supply below 3.15V",  }; -static const struct attribute_group adis16209_attribute_group = { -	.attrs = adis16209_attributes, +static const struct adis_data adis16209_data = { +	.read_delay = 30, +	.msc_ctrl_reg = ADIS16209_MSC_CTRL, +	.glob_cmd_reg = ADIS16209_GLOB_CMD, +	.diag_stat_reg = ADIS16209_DIAG_STAT, + +	.self_test_mask = ADIS16209_MSC_CTRL_SELF_TEST_EN, +	.startup_delay = ADIS16209_STARTUP_DELAY, + +	.status_error_msgs = adis16209_status_error_msgs, +	.status_error_mask = BIT(ADIS16209_DIAG_STAT_SELFTEST_FAIL_BIT) | +		BIT(ADIS16209_DIAG_STAT_SPI_FAIL_BIT) | +		BIT(ADIS16209_DIAG_STAT_FLASH_UPT_BIT) | +		BIT(ADIS16209_DIAG_STAT_POWER_HIGH_BIT) | +		BIT(ADIS16209_DIAG_STAT_POWER_LOW_BIT),  }; -static int __devinit adis16209_probe(struct spi_device *spi) + +static int 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); +	int ret; +	struct adis *st; +	struct iio_dev *indio_dev; -	/* 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; -	} +	indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); +	if (!indio_dev) +		return -ENOMEM; +	st = iio_priv(indio_dev); +	/* this is only used for removal purposes */ +	spi_set_drvdata(spi, indio_dev); -	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; +	indio_dev->name = spi->dev.driver->name; +	indio_dev->dev.parent = &spi->dev; +	indio_dev->info = &adis16209_info; +	indio_dev->channels = adis16209_channels; +	indio_dev->num_channels = ARRAY_SIZE(adis16209_channels); +	indio_dev->modes = INDIO_DIRECT_MODE; -	ret = adis16209_configure_ring(st->indio_dev); +	ret = adis_init(st, indio_dev, spi, &adis16209_data);  	if (ret) -		goto error_free_dev; - -	ret = iio_device_register(st->indio_dev); +		return ret; +	ret = adis_setup_buffer_and_trigger(st, indio_dev, NULL);  	if (ret) -		goto error_unreg_ring_funcs; -	regdone = 1; - -	ret = iio_ring_buffer_register(st->indio_dev->ring, 0); -	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; -	} +		return ret;  	/* Get the device into a sane initial state */ -	ret = adis16209_initial_setup(st); +	ret = adis_initial_startup(st); +	if (ret) +		goto error_cleanup_buffer_trigger; +	ret = iio_device_register(indio_dev);  	if (ret) -		goto error_remove_trigger; +		goto error_cleanup_buffer_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: -	iio_ring_buffer_unregister(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: +error_cleanup_buffer_trigger: +	adis_cleanup_buffer_and_trigger(st, indio_dev);  	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); +	struct iio_dev *indio_dev = spi_get_drvdata(spi); +	struct adis *st = iio_priv(indio_dev); -	iio_ring_buffer_unregister(indio_dev->ring);  	iio_device_unregister(indio_dev); -	adis16209_unconfigure_ring(indio_dev); -	kfree(st->tx); -	kfree(st->rx); -	kfree(st); +	adis_cleanup_buffer_and_trigger(st, indio_dev);  	return 0;  } @@ -588,21 +236,11 @@ static struct spi_driver adis16209_driver = {  		.owner = THIS_MODULE,  	},  	.probe = adis16209_probe, -	.remove = __devexit_p(adis16209_remove), +	.remove = 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_spi_driver(adis16209_driver);  MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>");  MODULE_DESCRIPTION("Analog Devices ADIS16209 Digital Vibration Sensor driver");  MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("spi:adis16209"); diff --git a/drivers/staging/iio/accel/adis16209_ring.c b/drivers/staging/iio/accel/adis16209_ring.c deleted file mode 100644 index 033135c6f22..00000000000 --- a/drivers/staging/iio/accel/adis16209_ring.c +++ /dev/null @@ -1,216 +0,0 @@ -#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/slab.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" - -static IIO_SCAN_EL_C(in_supply, ADIS16209_SCAN_SUPPLY, -		     ADIS16209_SUPPLY_OUT, NULL); -static IIO_CONST_ATTR_SCAN_EL_TYPE(in_supply, u, 14, 16) -static IIO_SCAN_EL_C(accel_x, ADIS16209_SCAN_ACC_X, ADIS16209_XACCL_OUT, NULL); -static IIO_SCAN_EL_C(accel_y, ADIS16209_SCAN_ACC_Y, ADIS16209_YACCL_OUT, NULL); -static IIO_CONST_ATTR_SCAN_EL_TYPE(accel, s, 14, 16); -static IIO_SCAN_EL_C(in0, ADIS16209_SCAN_AUX_ADC, ADIS16209_AUX_ADC, NULL); -static IIO_CONST_ATTR_SCAN_EL_TYPE(in0, u, 12, 16); -static IIO_SCAN_EL_C(temp, ADIS16209_SCAN_TEMP, ADIS16209_TEMP_OUT, NULL); -static IIO_CONST_ATTR_SCAN_EL_TYPE(temp, u, 12, 16); -static IIO_SCAN_EL_C(incli_x, ADIS16209_SCAN_INCLI_X, -		     ADIS16209_XINCL_OUT, NULL); -static IIO_SCAN_EL_C(incli_y, ADIS16209_SCAN_INCLI_Y, -		     ADIS16209_YINCL_OUT, NULL); -static IIO_CONST_ATTR_SCAN_EL_TYPE(incli, s, 14, 16); -static IIO_SCAN_EL_C(rot, ADIS16209_SCAN_ROT, ADIS16209_ROT_OUT, NULL); -static IIO_CONST_ATTR_SCAN_EL_TYPE(rot, s, 14, 16); -static IIO_SCAN_EL_TIMESTAMP(8); -static IIO_CONST_ATTR_SCAN_EL_TYPE(timestamp, s, 64, 64); - -static struct attribute *adis16209_scan_el_attrs[] = { -	&iio_scan_el_in_supply.dev_attr.attr, -	&iio_const_attr_in_supply_index.dev_attr.attr, -	&iio_const_attr_in_supply_type.dev_attr.attr, -	&iio_scan_el_accel_x.dev_attr.attr, -	&iio_const_attr_accel_x_index.dev_attr.attr, -	&iio_scan_el_accel_y.dev_attr.attr, -	&iio_const_attr_accel_y_index.dev_attr.attr, -	&iio_const_attr_accel_type.dev_attr.attr, -	&iio_scan_el_in0.dev_attr.attr, -	&iio_const_attr_in0_index.dev_attr.attr, -	&iio_const_attr_in0_type.dev_attr.attr, -	&iio_scan_el_temp.dev_attr.attr, -	&iio_const_attr_temp_index.dev_attr.attr, -	&iio_const_attr_temp_type.dev_attr.attr, -	&iio_scan_el_incli_x.dev_attr.attr, -	&iio_const_attr_incli_x_index.dev_attr.attr, -	&iio_scan_el_incli_y.dev_attr.attr, -	&iio_const_attr_incli_y_index.dev_attr.attr, -	&iio_const_attr_incli_type.dev_attr.attr, -	&iio_scan_el_rot.dev_attr.attr, -	&iio_const_attr_rot_index.dev_attr.attr, -	&iio_const_attr_rot_type.dev_attr.attr, -	&iio_scan_el_timestamp.dev_attr.attr, -	&iio_const_attr_timestamp_index.dev_attr.attr, -	&iio_const_attr_timestamp_type.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, s64 time) -{ -	struct adis16209_state *st = iio_dev_get_devdata(indio_dev); -	st->last_timestamp = time; -	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); -	struct iio_ring_buffer *ring = st->indio_dev->ring; - -	int i = 0; -	s16 *data; -	size_t datasize = ring->access.get_bytes_per_datum(ring); - -	data = kmalloc(datasize , GFP_KERNEL); -	if (data == NULL) { -		dev_err(&st->us->dev, "memory alloc failed in ring bh"); -		return; -	} - -	if (ring->scan_count) -		if (adis16209_read_ring_data(&st->indio_dev->dev, st->rx) >= 0) -			for (; i < ring->scan_count; i++) -				data[i] = be16_to_cpup( -					(__be16 *)&(st->rx[i*2])); - -	/* Guaranteed to be aligned with 8 byte boundary */ -	if (ring->scan_timestamp) -		*((s64 *)(data + ((i + 3)/4)*4)) = st->last_timestamp; - -	ring->access.store_to(ring, -			      (u8 *)data, -			      st->last_timestamp); - -	iio_trigger_notify_done(st->indio_dev->trig); -	kfree(data); - -	return; -} - -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); - -	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->bpe = 2; -	ring->scan_el_attrs = &adis16209_scan_el_group; -	ring->scan_timestamp = true; -	ring->preenable = &iio_sw_ring_preenable; -	ring->postenable = &iio_triggered_ring_postenable; -	ring->predisable = &iio_triggered_ring_predisable; -	ring->owner = THIS_MODULE; - -	/* Set default scan mode */ -	iio_scan_mask_set(ring, iio_scan_el_in_supply.number); -	iio_scan_mask_set(ring, iio_scan_el_rot.number); -	iio_scan_mask_set(ring, iio_scan_el_accel_x.number); -	iio_scan_mask_set(ring, iio_scan_el_accel_y.number); -	iio_scan_mask_set(ring, iio_scan_el_temp.number); -	iio_scan_mask_set(ring, iio_scan_el_in0.number); -	iio_scan_mask_set(ring, iio_scan_el_incli_x.number); -	iio_scan_mask_set(ring, iio_scan_el_incli_y.number); - -	ret = iio_alloc_pollfunc(indio_dev, NULL, &adis16209_poll_func_th); -	if (ret) -		goto error_iio_sw_rb_free; - -	indio_dev->modes |= INDIO_RING_TRIGGERED; -	return 0; - -error_iio_sw_rb_free: -	iio_sw_rb_free(indio_dev->ring); -	return ret; -} diff --git a/drivers/staging/iio/accel/adis16209_trigger.c b/drivers/staging/iio/accel/adis16209_trigger.c deleted file mode 100644 index d2980dc7444..00000000000 --- a/drivers/staging/iio/accel/adis16209_trigger.c +++ /dev/null @@ -1,122 +0,0 @@ -#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; - -	iio_trigger_poll(trig, timestamp); - -	return IRQ_HANDLED; -} - -IIO_EVENT_SH(data_rdy_trig, &adis16209_data_rdy_trig_poll); - -static IIO_TRIGGER_NAME_ATTR; - -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 = kasprintf(GFP_KERNEL, -				   "adis16209-dev%d", -				   indio_dev->id); -	if (!st->trig->name) { -		ret = -ENOMEM; -		goto error_free_trig; -	} -	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 index 7013314a9d7..a894ad7fb26 100644 --- a/drivers/staging/iio/accel/adis16220.h +++ b/drivers/staging/iio/accel/adis16220.h @@ -1,10 +1,9 @@  #ifndef SPI_ADIS16220_H_  #define SPI_ADIS16220_H_ -#define ADIS16220_STARTUP_DELAY	220 /* ms */ +#include <linux/iio/imu/adis.h> -#define ADIS16220_READ_REG(a)    a -#define ADIS16220_WRITE_REG(a) ((a) | 0x80) +#define ADIS16220_STARTUP_DELAY	220 /* ms */  /* Flash memory write count */  #define ADIS16220_FLASH_CNT     0x00 @@ -102,15 +101,15 @@  #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) +#define ADIS16220_DIAG_STAT_VIOLATION_BIT	4  /* SPI communications failure */ -#define ADIS16220_DIAG_STAT_SPI_FAIL	(1<<3) +#define ADIS16220_DIAG_STAT_SPI_FAIL_BIT	3  /* Flash update failure */ -#define ADIS16220_DIAG_STAT_FLASH_UPT	(1<<2) +#define ADIS16220_DIAG_STAT_FLASH_UPT_BIT	2  /* Power supply above 3.625 V */ -#define ADIS16220_DIAG_STAT_POWER_HIGH	(1<<1) +#define ADIS16220_DIAG_STAT_POWER_HIGH_BIT	1  /* Power supply below 3.15 V */ -#define ADIS16220_DIAG_STAT_POWER_LOW	(1<<0) +#define ADIS16220_DIAG_STAT_POWER_LOW_BIT	0  /* GLOB_CMD */  #define ADIS16220_GLOB_CMD_SW_RESET	(1<<7) @@ -125,22 +124,17 @@  /**   * struct adis16220_state - device instance specific data - * @us:			actual spi_device - * @work_trigger_to_ring: bh for triggered event handling - * @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 + * @adis:		adis device   * @tx:			transmit buffer - * @rx:			recieve buffer + * @rx:			receive 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; +	struct adis adis; + +	struct mutex		buf_lock; +	u8			tx[ADIS16220_MAX_TX] ____cacheline_aligned; +	u8			rx[ADIS16220_MAX_RX];  };  #endif /* SPI_ADIS16220_H_ */ diff --git a/drivers/staging/iio/accel/adis16220_core.c b/drivers/staging/iio/accel/adis16220_core.c index c86d1498737..6f38ca95f9b 100644 --- a/drivers/staging/iio/accel/adis16220_core.c +++ b/drivers/staging/iio/accel/adis16220_core.c @@ -6,9 +6,6 @@   * 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> @@ -16,185 +13,31 @@  #include <linux/spi/spi.h>  #include <linux/slab.h>  #include <linux/sysfs.h> -#include <linux/list.h> +#include <linux/module.h> -#include "../iio.h" -#include "../sysfs.h" -#include "accel.h" -#include "../adc/adc.h" +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.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 = 35, -		}, { -			.tx_buf = st->tx + 2, -			.bits_per_word = 8, -			.len = 2, -			.cs_change = 1, -			.delay_usecs = 35, -		}, -	}; - -	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 = 35, -		}, { -			.rx_buf = st->rx, -			.bits_per_word = 8, -			.len = 2, -			.cs_change = 1, -			.delay_usecs = 35, -		}, -	}; - -	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); +	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); +	struct iio_dev *indio_dev = dev_to_iio_dev(dev); +	struct adis16220_state *st = iio_priv(indio_dev);  	ssize_t ret; +	s16 val = 0;  	/* Take the iio_dev status lock */  	mutex_lock(&indio_dev->mlock); -	ret =  adis16220_spi_read_signed(dev, attr, buf, 16); +	ret = adis_read_reg_16(&st->adis, this_attr->address, +					(u16 *)&val);  	mutex_unlock(&indio_dev->mlock); - -	return ret; +	if (ret) +		return ret; +	return sprintf(buf, "%d\n", val);  }  static ssize_t adis16220_write_16bit(struct device *dev, @@ -202,159 +45,63 @@ static ssize_t adis16220_write_16bit(struct device *dev,  		const char *buf,  		size_t len)  { +	struct iio_dev *indio_dev = dev_to_iio_dev(dev);  	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); +	struct adis16220_state *st = iio_priv(indio_dev);  	int ret; -	long val; +	u16 val; -	ret = strict_strtol(buf, 10, &val); +	ret = kstrtou16(buf, 10, &val);  	if (ret)  		goto error_ret; -	ret = adis16220_spi_write_reg_16(dev, this_attr->address, val); +	ret = adis_write_reg_16(&st->adis, this_attr->address, val);  error_ret:  	return ret ? ret : len;  } -static int adis16220_capture(struct device *dev) +static int adis16220_capture(struct iio_dev *indio_dev)  { +	struct adis16220_state *st = iio_priv(indio_dev);  	int ret; -	ret = adis16220_spi_write_reg_16(dev, -			ADIS16220_GLOB_CMD, -			0xBF08); /* initiates a manual data capture */ + +	 /* initiates a manual data capture */ +	ret = adis_write_reg_16(&st->adis, ADIS16220_GLOB_CMD, 0xBF08);  	if (ret) -		dev_err(dev, "problem beginning capture"); +		dev_err(&indio_dev->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; +	struct iio_dev *indio_dev = dev_to_iio_dev(dev); +	bool val;  	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 3.625V\n"); -	if (status & ADIS16220_DIAG_STAT_POWER_LOW) -		dev_err(dev, "Power supply below 3.15V\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); +	ret = strtobool(buf, &val); +	if (ret) +		return ret; +	if (!val) +		return -EINVAL; +	ret = adis16220_capture(indio_dev); +	if (ret) +		return ret; -err_ret: -	return ret; +	return len;  } -static ssize_t adis16220_capture_buffer_read(struct adis16220_state *st, +static ssize_t adis16220_capture_buffer_read(struct iio_dev *indio_dev,  					char *buf,  					loff_t off,  					size_t count,  					int addr)  { -	struct spi_message msg; +	struct adis16220_state *st = iio_priv(indio_dev);  	struct spi_transfer xfers[] = {  		{  			.tx_buf = st->tx, @@ -383,7 +130,7 @@ static ssize_t adis16220_capture_buffer_read(struct adis16220_state *st,  		count = ADIS16220_CAPTURE_SIZE - off;  	/* write the begin position of capture buffer */ -	ret = adis16220_spi_write_reg_16(&st->indio_dev->dev, +	ret = adis_write_reg_16(&st->adis,  					ADIS16220_CAPT_PNTR,  					off > 1);  	if (ret) @@ -392,16 +139,14 @@ static ssize_t adis16220_capture_buffer_read(struct adis16220_state *st,  	/* 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] = ADIS_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); +	ret = spi_sync_transfer(st->adis.spi, xfers, ARRAY_SIZE(xfers));  	if (ret) {  		mutex_unlock(&st->buf_lock); @@ -420,11 +165,9 @@ static ssize_t adis16220_accel_bin_read(struct file *filp, struct kobject *kobj,  					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); +	struct iio_dev *indio_dev = dev_to_iio_dev(kobj_to_dev(kobj)); -	return adis16220_capture_buffer_read(st, buf, +	return adis16220_capture_buffer_read(indio_dev, buf,  					off, count,  					ADIS16220_CAPT_BUFA);  } @@ -443,11 +186,9 @@ static ssize_t adis16220_adc1_bin_read(struct file *filp, struct kobject *kobj,  				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); +	struct iio_dev *indio_dev = dev_to_iio_dev(kobj_to_dev(kobj)); -	return adis16220_capture_buffer_read(st, buf, +	return adis16220_capture_buffer_read(indio_dev, buf,  					off, count,  					ADIS16220_CAPT_BUF1);  } @@ -466,11 +207,9 @@ static ssize_t adis16220_adc2_bin_read(struct file *filp, struct kobject *kobj,  				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); +	struct iio_dev *indio_dev = dev_to_iio_dev(kobj_to_dev(kobj)); -	return adis16220_capture_buffer_read(st, buf, +	return adis16220_capture_buffer_read(indio_dev, buf,  					off, count,  					ADIS16220_CAPT_BUF2);  } @@ -485,29 +224,8 @@ static struct bin_attribute adc2_bin = {  	.size = ADIS16220_CAPTURE_SIZE,  }; -static IIO_DEV_ATTR_IN_NAMED_RAW(0, supply, adis16220_read_12bit_unsigned, -		ADIS16220_CAPT_SUPPLY); -static IIO_CONST_ATTR_IN_NAMED_SCALE(0, supply, "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_CONST_ATTR_ACCEL_SCALE("0.18704223545"); -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(1, adis16220_read_16bit, ADIS16220_CAPT_BUF1); -static IIO_DEV_ATTR_IN_RAW(2, 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) +	IIO_DEVICE_ATTR(capture, S_IWUSR, NULL, _store, 0)  static IIO_DEV_ATTR_CAPTURE(adis16220_write_capture); @@ -519,27 +237,147 @@ static IIO_DEV_ATTR_CAPTURE_COUNT(S_IWUSR | S_IRUGO,  		adis16220_write_16bit,  		ADIS16220_CAPT_PNTR); -static IIO_CONST_ATTR_SAMP_FREQ_AVAIL("100200"); +enum adis16220_channel { +	in_supply, in_1, in_2, accel, temp +}; + +struct adis16220_address_spec { +	u8 addr; +	u8 bits; +	bool sign; +}; -static IIO_CONST_ATTR_NAME("adis16220"); +/* Address / bits / signed */ +static const struct adis16220_address_spec adis16220_addresses[][3] = { +	[in_supply] =	{ { ADIS16220_CAPT_SUPPLY,	12, 0 }, }, +	[in_1] =	{ { ADIS16220_CAPT_BUF1,	16, 1 }, +			  { ADIS16220_AIN1_NULL,	16, 1 }, +			  { ADIS16220_CAPT_PEAK1,	16, 1 }, }, +	[in_2] =	{ { ADIS16220_CAPT_BUF2,	16, 1 }, +			  { ADIS16220_AIN2_NULL,	16, 1 }, +			  { ADIS16220_CAPT_PEAK2,	16, 1 }, }, +	[accel] =	{ { ADIS16220_CAPT_BUFA,	16, 1 }, +			  { ADIS16220_ACCL_NULL,	16, 1 }, +			  { ADIS16220_CAPT_PEAKA,	16, 1 }, }, +	[temp] =	{ { ADIS16220_CAPT_TEMP,	12, 0 }, } +}; + +static int adis16220_read_raw(struct iio_dev *indio_dev, +			      struct iio_chan_spec const *chan, +			      int *val, int *val2, +			      long mask) +{ +	struct adis16220_state *st = iio_priv(indio_dev); +	const struct adis16220_address_spec *addr; +	int ret = -EINVAL; +	int addrind = 0; +	u16 uval; +	s16 sval; +	u8 bits; + +	switch (mask) { +	case IIO_CHAN_INFO_RAW: +		addrind = 0; +		break; +	case IIO_CHAN_INFO_OFFSET: +		if (chan->type == IIO_TEMP) { +			*val = 25000 / -470 - 1278; /* 25 C = 1278 */ +			return IIO_VAL_INT; +		} +		addrind = 1; +		break; +	case IIO_CHAN_INFO_PEAK: +		addrind = 2; +		break; +	case IIO_CHAN_INFO_SCALE: +		switch (chan->type) { +		case IIO_TEMP: +			*val = -470; /* -0.47 C */ +			*val2 = 0; +			return IIO_VAL_INT_PLUS_MICRO; +		case IIO_ACCEL: +			*val2 = IIO_G_TO_M_S_2(19073); /* 19.073 g */ +			return IIO_VAL_INT_PLUS_MICRO; +		case IIO_VOLTAGE: +			if (chan->channel == 0) { +				*val = 1; +				*val2 = 220700; /* 1.2207 mV */ +			} else { +				/* Should really be dependent on VDD */ +				*val2 = 305180; /* 305.18 uV */ +			} +			return IIO_VAL_INT_PLUS_MICRO; +		default: +			return -EINVAL; +		} +	default: +		return -EINVAL; +	} +	addr = &adis16220_addresses[chan->address][addrind]; +	if (addr->sign) { +		ret = adis_read_reg_16(&st->adis, addr->addr, &sval); +		if (ret) +			return ret; +		bits = addr->bits; +		sval &= (1 << bits) - 1; +		sval = (s16)(sval << (16 - bits)) >> (16 - bits); +		*val = sval; +		return IIO_VAL_INT; +	} else { +		ret = adis_read_reg_16(&st->adis, addr->addr, &uval); +		if (ret) +			return ret; +		bits = addr->bits; +		uval &= (1 << bits) - 1; +		*val = uval; +		return IIO_VAL_INT; +	} +} + +static const struct iio_chan_spec adis16220_channels[] = { +	{ +		.type = IIO_VOLTAGE, +		.indexed = 1, +		.channel = 0, +		.extend_name = "supply", +		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | +			BIT(IIO_CHAN_INFO_SCALE), +		.address = in_supply, +	}, { +		.type = IIO_ACCEL, +		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | +			BIT(IIO_CHAN_INFO_OFFSET) | +			BIT(IIO_CHAN_INFO_SCALE) | +			BIT(IIO_CHAN_INFO_PEAK), +		.address = accel, +	}, { +		.type = IIO_TEMP, +		.indexed = 1, +		.channel = 0, +		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | +			BIT(IIO_CHAN_INFO_OFFSET) | +			BIT(IIO_CHAN_INFO_SCALE), +		.address = temp, +	}, { +		.type = IIO_VOLTAGE, +		.indexed = 1, +		.channel = 1, +		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | +			BIT(IIO_CHAN_INFO_OFFSET) | +			BIT(IIO_CHAN_INFO_SCALE), +		.address = in_1, +	}, { +		.type = IIO_VOLTAGE, +		.indexed = 1, +		.channel = 2, +		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), +		.address = in_2, +	} +};  static struct attribute *adis16220_attributes[] = { -	&iio_dev_attr_in0_supply_raw.dev_attr.attr, -	&iio_const_attr_in0_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_const_attr_accel_scale.dev_attr.attr, -	&iio_dev_attr_temp_raw.dev_attr.attr, -	&iio_dev_attr_in1_raw.dev_attr.attr, -	&iio_dev_attr_in2_raw.dev_attr.attr, -	&iio_const_attr_temp_offset.dev_attr.attr, -	&iio_const_attr_temp_scale.dev_attr.attr, -	&iio_const_attr_sampling_frequency_available.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  }; @@ -547,101 +385,101 @@ static const struct attribute_group adis16220_attribute_group = {  	.attrs = adis16220_attributes,  }; -static int __devinit adis16220_probe(struct spi_device *spi) +static const struct iio_info adis16220_info = { +	.attrs = &adis16220_attribute_group, +	.driver_module = THIS_MODULE, +	.read_raw = &adis16220_read_raw, +}; + +static const char * const adis16220_status_error_msgs[] = { +	[ADIS16220_DIAG_STAT_VIOLATION_BIT] = "Capture period violation/interruption", +	[ADIS16220_DIAG_STAT_SPI_FAIL_BIT] = "SPI failure", +	[ADIS16220_DIAG_STAT_FLASH_UPT_BIT] = "Flash update failed", +	[ADIS16220_DIAG_STAT_POWER_HIGH_BIT] = "Power supply above 3.625V", +	[ADIS16220_DIAG_STAT_POWER_LOW_BIT] = "Power supply below 3.15V", +}; + +static const struct adis_data adis16220_data = { +	.read_delay = 35, +	.write_delay = 35, +	.msc_ctrl_reg = ADIS16220_MSC_CTRL, +	.glob_cmd_reg = ADIS16220_GLOB_CMD, +	.diag_stat_reg = ADIS16220_DIAG_STAT, + +	.self_test_mask = ADIS16220_MSC_CTRL_SELF_TEST_EN, +	.startup_delay = ADIS16220_STARTUP_DELAY, + +	.status_error_msgs = adis16220_status_error_msgs, +	.status_error_mask = BIT(ADIS16220_DIAG_STAT_VIOLATION_BIT) | +		BIT(ADIS16220_DIAG_STAT_SPI_FAIL_BIT) | +		BIT(ADIS16220_DIAG_STAT_FLASH_UPT_BIT) | +		BIT(ADIS16220_DIAG_STAT_POWER_HIGH_BIT) | +		BIT(ADIS16220_DIAG_STAT_POWER_LOW_BIT), +}; + +static int 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); +	int ret; +	struct adis16220_state *st; +	struct iio_dev *indio_dev; -	/* 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; -	} +	indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); +	if (!indio_dev) +		return -ENOMEM; -	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; +	st = iio_priv(indio_dev); +	/* this is only used for removal purposes */ +	spi_set_drvdata(spi, indio_dev); + +	indio_dev->name = spi->dev.driver->name; +	indio_dev->dev.parent = &spi->dev; +	indio_dev->info = &adis16220_info; +	indio_dev->modes = INDIO_DIRECT_MODE; +	indio_dev->channels = adis16220_channels; +	indio_dev->num_channels = ARRAY_SIZE(adis16220_channels); -	ret = iio_device_register(st->indio_dev); +	ret = devm_iio_device_register(&spi->dev, indio_dev);  	if (ret) -		goto error_free_dev; -	regdone = 1; +		return ret; -	ret = sysfs_create_bin_file(&st->indio_dev->dev.kobj, &accel_bin); +	ret = sysfs_create_bin_file(&indio_dev->dev.kobj, &accel_bin);  	if (ret) -		goto error_free_dev; +		return ret; -	ret = sysfs_create_bin_file(&st->indio_dev->dev.kobj, &adc1_bin); +	ret = sysfs_create_bin_file(&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); +	ret = sysfs_create_bin_file(&indio_dev->dev.kobj, &adc2_bin);  	if (ret)  		goto error_rm_adc1_bin; +	ret = adis_init(&st->adis, indio_dev, spi, &adis16220_data); +	if (ret) +		goto error_rm_adc2_bin;  	/* Get the device into a sane initial state */ -	ret = adis16220_initial_setup(st); +	ret = adis_initial_startup(&st->adis);  	if (ret)  		goto error_rm_adc2_bin;  	return 0;  error_rm_adc2_bin: -	sysfs_remove_bin_file(&st->indio_dev->dev.kobj, &adc2_bin); +	sysfs_remove_bin_file(&indio_dev->dev.kobj, &adc2_bin);  error_rm_adc1_bin: -	sysfs_remove_bin_file(&st->indio_dev->dev.kobj, &adc1_bin); +	sysfs_remove_bin_file(&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: +	sysfs_remove_bin_file(&indio_dev->dev.kobj, &accel_bin);  	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(); +	struct iio_dev *indio_dev = spi_get_drvdata(spi); -	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); +	sysfs_remove_bin_file(&indio_dev->dev.kobj, &adc2_bin); +	sysfs_remove_bin_file(&indio_dev->dev.kobj, &adc1_bin); +	sysfs_remove_bin_file(&indio_dev->dev.kobj, &accel_bin);  	return 0;  } @@ -652,21 +490,11 @@ static struct spi_driver adis16220_driver = {  		.owner = THIS_MODULE,  	},  	.probe = adis16220_probe, -	.remove = __devexit_p(adis16220_remove), +	.remove = 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_spi_driver(adis16220_driver);  MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>");  MODULE_DESCRIPTION("Analog Devices ADIS16220 Digital Vibration Sensor");  MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("spi:adis16220"); diff --git a/drivers/staging/iio/accel/adis16240.h b/drivers/staging/iio/accel/adis16240.h index 51a807dde27..d442d49f51f 100644 --- a/drivers/staging/iio/accel/adis16240.h +++ b/drivers/staging/iio/accel/adis16240.h @@ -3,9 +3,6 @@  #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 */ @@ -75,8 +72,6 @@  /* 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) @@ -101,17 +96,17 @@  /* 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) +#define ADIS16240_DIAG_STAT_PWRON_FAIL_BIT  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) +#define ADIS16240_DIAG_STAT_SPI_FAIL_BIT	3  /* Flash update failure */ -#define ADIS16240_DIAG_STAT_FLASH_UPT	(1<<2) +#define ADIS16240_DIAG_STAT_FLASH_UPT_BIT	2  /* Power supply above 3.625 V */ -#define ADIS16240_DIAG_STAT_POWER_HIGH	(1<<1) +#define ADIS16240_DIAG_STAT_POWER_HIGH_BIT	1   /* Power supply below 3.15 V */ -#define ADIS16240_DIAG_STAT_POWER_LOW	(1<<0) +#define ADIS16240_DIAG_STAT_POWER_LOW_BIT	0  /* GLOB_CMD */  #define ADIS16240_GLOB_CMD_RESUME	(1<<8) @@ -120,84 +115,15 @@  #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 - * @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; -	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!   */ -#define ADIS16240_SCAN_SUPPLY	0 -#define ADIS16240_SCAN_ACC_X	1 -#define ADIS16240_SCAN_ACC_Y	2 -#define ADIS16240_SCAN_ACC_Z	3 +#define ADIS16240_SCAN_ACC_X	0 +#define ADIS16240_SCAN_ACC_Y	1 +#define ADIS16240_SCAN_ACC_Z	2 +#define ADIS16240_SCAN_SUPPLY	3  #define ADIS16240_SCAN_AUX_ADC	4  #define ADIS16240_SCAN_TEMP	5 -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); - -#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) -{ -} - -#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 index d11d164207e..3a303a03d02 100644 --- a/drivers/staging/iio/accel/adis16240_core.c +++ b/drivers/staging/iio/accel/adis16240_core.c @@ -17,203 +17,45 @@  #include <linux/slab.h>  #include <linux/sysfs.h>  #include <linux/list.h> +#include <linux/module.h> -#include "../iio.h" -#include "../sysfs.h" -#include "../ring_generic.h" -#include "accel.h" -#include "../adc/adc.h" +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> +#include <linux/iio/buffer.h> +#include <linux/iio/imu/adis.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 = 35, -		}, { -			.tx_buf = st->tx + 2, -			.bits_per_word = 8, -			.len = 2, -			.cs_change = 1, -			.delay_usecs = 35, -		}, -	}; - -	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 = 35, -		}, { -			.rx_buf = st->rx, -			.bits_per_word = 8, -			.len = 2, -			.cs_change = 1, -			.delay_usecs = 35, -		}, -	}; - -	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)  { +	struct iio_dev *indio_dev = dev_to_iio_dev(dev); +	struct adis *st = iio_priv(indio_dev);  	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); +	ret = adis_read_reg_16(st, +					this_attr->address, (u16 *)&val);  	if (ret)  		return ret;  	if (val & ADIS16240_ERROR_ACTIVE) -		adis16240_check_status(dev); +		adis_check_status(st);  	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; +	struct iio_dev *indio_dev = dev_to_iio_dev(dev);  	/* Take the iio_dev status lock */  	mutex_lock(&indio_dev->mlock); @@ -223,236 +65,129 @@ static ssize_t adis16240_read_12bit_signed(struct device *dev,  	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; +static IIO_DEVICE_ATTR(in_accel_xyz_squared_peak_raw, S_IRUGO, +		       adis16240_read_12bit_signed, NULL, +		       ADIS16240_XYZPEAK_OUT); -	ret = strict_strtol(buf, 10, &val); -	if (ret) -		goto error_ret; -	ret = adis16240_spi_write_reg_16(dev, this_attr->address, val); +static IIO_CONST_ATTR_SAMP_FREQ_AVAIL("4096"); -error_ret: -	return ret ? ret : len; -} +static const u8 adis16240_addresses[][2] = { +	[ADIS16240_SCAN_ACC_X] = { ADIS16240_XACCL_OFF, ADIS16240_XPEAK_OUT }, +	[ADIS16240_SCAN_ACC_Y] = { ADIS16240_YACCL_OFF, ADIS16240_YPEAK_OUT }, +	[ADIS16240_SCAN_ACC_Z] = { ADIS16240_ZACCL_OFF, ADIS16240_ZPEAK_OUT }, +}; -static int adis16240_reset(struct device *dev) +static int adis16240_read_raw(struct iio_dev *indio_dev, +			      struct iio_chan_spec const *chan, +			      int *val, int *val2, +			      long mask)  { +	struct adis *st = iio_priv(indio_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); +	int bits; +	u8 addr; +	s16 val16; + +	switch (mask) { +	case IIO_CHAN_INFO_RAW: +		return adis_single_conversion(indio_dev, chan, +				ADIS16240_ERROR_ACTIVE, val); +	case IIO_CHAN_INFO_SCALE: +		switch (chan->type) { +		case IIO_VOLTAGE: +			if (chan->channel == 0) { +				*val = 4; +				*val2 = 880000; /* 4.88 mV */ +				return IIO_VAL_INT_PLUS_MICRO; +			} else { +				return -EINVAL; +			} +		case IIO_TEMP: +			*val = 244; /* 0.244 C */ +			*val2 = 0; +			return IIO_VAL_INT_PLUS_MICRO; +		case IIO_ACCEL: +			*val = 0; +			*val2 = IIO_G_TO_M_S_2(51400); /* 51.4 mg */ +			return IIO_VAL_INT_PLUS_MICRO; +		default: +			return -EINVAL; +		} +		break; +	case IIO_CHAN_INFO_PEAK_SCALE: +		*val = 0; +		*val2 = IIO_G_TO_M_S_2(51400); /* 51.4 mg */ +		return IIO_VAL_INT_PLUS_MICRO; +	case IIO_CHAN_INFO_OFFSET: +		*val = 25000 / 244 - 0x133; /* 25 C = 0x133 */ +		return IIO_VAL_INT; +	case IIO_CHAN_INFO_CALIBBIAS: +		bits = 10; +		mutex_lock(&indio_dev->mlock); +		addr = adis16240_addresses[chan->scan_index][0]; +		ret = adis_read_reg_16(st, addr, &val16); +		if (ret) { +			mutex_unlock(&indio_dev->mlock); +			return ret; +		} +		val16 &= (1 << bits) - 1; +		val16 = (s16)(val16 << (16 - bits)) >> (16 - bits); +		*val = val16; +		mutex_unlock(&indio_dev->mlock); +		return IIO_VAL_INT; +	case IIO_CHAN_INFO_PEAK: +		bits = 10; +		mutex_lock(&indio_dev->mlock); +		addr = adis16240_addresses[chan->scan_index][1]; +		ret = adis_read_reg_16(st, addr, &val16); +		if (ret) { +			mutex_unlock(&indio_dev->mlock); +			return ret; +		} +		val16 &= (1 << bits) - 1; +		val16 = (s16)(val16 << (16 - bits)) >> (16 - bits); +		*val = val16; +		mutex_unlock(&indio_dev->mlock); +		return IIO_VAL_INT;  	}  	return -EINVAL;  } -int adis16240_set_irq(struct device *dev, bool enable) +static int adis16240_write_raw(struct iio_dev *indio_dev, +			       struct iio_chan_spec const *chan, +			       int val, +			       int val2, +			       long mask)  { -	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; +	struct adis *st = iio_priv(indio_dev); +	int bits = 10; +	s16 val16; +	u8 addr; +	switch (mask) { +	case IIO_CHAN_INFO_CALIBBIAS: +		val16 = val & ((1 << bits) - 1); +		addr = adis16240_addresses[chan->scan_index][0]; +		return adis_write_reg_16(st, addr, val16);  	} - -	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; +	return -EINVAL;  } -static IIO_DEV_ATTR_IN_NAMED_RAW(0, supply, adis16240_read_10bit_unsigned, -		ADIS16240_SUPPLY_OUT); -static IIO_DEV_ATTR_IN_RAW(1, adis16240_read_10bit_signed, -		ADIS16240_AUX_ADC); -static IIO_CONST_ATTR_IN_NAMED_SCALE(0, supply, "0.00488"); - -static IIO_CONST_ATTR_ACCEL_SCALE("0.50406181"); -static IIO_CONST_ATTR(accel_peak_scale, "6.6292954"); -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_CALIBBIAS(S_IWUSR | S_IRUGO, -		adis16240_read_10bit_signed, -		adis16240_write_16bit, -		ADIS16240_XACCL_OFF); -static IIO_DEV_ATTR_ACCEL_Y_CALIBBIAS(S_IWUSR | S_IRUGO, -		adis16240_read_10bit_signed, -		adis16240_write_16bit, -		ADIS16240_YACCL_OFF); -static IIO_DEV_ATTR_ACCEL_Z_CALIBBIAS(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_SAMP_FREQ_AVAIL("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 const struct iio_chan_spec adis16240_channels[] = { +	ADIS_SUPPLY_CHAN(ADIS16240_SUPPLY_OUT, ADIS16240_SCAN_SUPPLY, 10), +	ADIS_AUX_ADC_CHAN(ADIS16240_AUX_ADC, ADIS16240_SCAN_AUX_ADC, 10), +	ADIS_ACCEL_CHAN(X, ADIS16240_XACCL_OUT, ADIS16240_SCAN_ACC_X, +		BIT(IIO_CHAN_INFO_CALIBBIAS) | BIT(IIO_CHAN_INFO_PEAK), 10), +	ADIS_ACCEL_CHAN(Y, ADIS16240_YACCL_OUT, ADIS16240_SCAN_ACC_Y, +		BIT(IIO_CHAN_INFO_CALIBBIAS) | BIT(IIO_CHAN_INFO_PEAK), 10), +	ADIS_ACCEL_CHAN(Z, ADIS16240_ZACCL_OUT, ADIS16240_SCAN_ACC_Z, +		BIT(IIO_CHAN_INFO_CALIBBIAS) | BIT(IIO_CHAN_INFO_PEAK), 10), +	ADIS_TEMP_CHAN(ADIS16240_TEMP_OUT, ADIS16240_SCAN_TEMP, 10), +	IIO_CHAN_SOFT_TIMESTAMP(6)  };  static struct attribute *adis16240_attributes[] = { -	&iio_dev_attr_in0_supply_raw.dev_attr.attr, -	&iio_const_attr_in0_supply_scale.dev_attr.attr, -	&iio_dev_attr_in1_raw.dev_attr.attr, -	&iio_const_attr_accel_scale.dev_attr.attr, -	&iio_const_attr_accel_peak_scale.dev_attr.attr, -	&iio_dev_attr_accel_x_raw.dev_attr.attr, -	&iio_dev_attr_accel_x_calibbias.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_calibbias.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_calibbias.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_dev_attr_in_accel_xyz_squared_peak_raw.dev_attr.attr,  	&iio_const_attr_sampling_frequency_available.dev_attr.attr, -	&iio_dev_attr_reset.dev_attr.attr, -	&iio_const_attr_name.dev_attr.attr,  	NULL  }; @@ -460,121 +195,89 @@ static const struct attribute_group adis16240_attribute_group = {  	.attrs = adis16240_attributes,  }; -static int __devinit adis16240_probe(struct spi_device *spi) +static const struct iio_info adis16240_info = { +	.attrs = &adis16240_attribute_group, +	.read_raw = &adis16240_read_raw, +	.write_raw = &adis16240_write_raw, +	.update_scan_mode = adis_update_scan_mode, +	.driver_module = THIS_MODULE, +}; + +static const char * const adis16240_status_error_msgs[] = { +	[ADIS16240_DIAG_STAT_PWRON_FAIL_BIT] = "Power on, self-test failed", +	[ADIS16240_DIAG_STAT_SPI_FAIL_BIT] = "SPI failure", +	[ADIS16240_DIAG_STAT_FLASH_UPT_BIT] = "Flash update failed", +	[ADIS16240_DIAG_STAT_POWER_HIGH_BIT] = "Power supply above 3.625V", +	[ADIS16240_DIAG_STAT_POWER_LOW_BIT] = "Power supply below 2.225V", +}; + +static const struct adis_data adis16240_data = { +	.write_delay = 35, +	.read_delay = 35, +	.msc_ctrl_reg = ADIS16240_MSC_CTRL, +	.glob_cmd_reg = ADIS16240_GLOB_CMD, +	.diag_stat_reg = ADIS16240_DIAG_STAT, + +	.self_test_mask = ADIS16240_MSC_CTRL_SELF_TEST_EN, +	.startup_delay = ADIS16240_STARTUP_DELAY, + +	.status_error_msgs = adis16240_status_error_msgs, +	.status_error_mask = BIT(ADIS16240_DIAG_STAT_PWRON_FAIL_BIT) | +		BIT(ADIS16240_DIAG_STAT_SPI_FAIL_BIT) | +		BIT(ADIS16240_DIAG_STAT_FLASH_UPT_BIT) | +		BIT(ADIS16240_DIAG_STAT_POWER_HIGH_BIT) | +		BIT(ADIS16240_DIAG_STAT_POWER_LOW_BIT), +}; + +static int 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); +	int ret; +	struct adis *st; +	struct iio_dev *indio_dev; -	/* 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; -	} +	indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); +	if (!indio_dev) +		return -ENOMEM; +	st = iio_priv(indio_dev); +	/* this is only used for removal purposes */ +	spi_set_drvdata(spi, indio_dev); -	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; +	indio_dev->name = spi->dev.driver->name; +	indio_dev->dev.parent = &spi->dev; +	indio_dev->info = &adis16240_info; +	indio_dev->channels = adis16240_channels; +	indio_dev->num_channels = ARRAY_SIZE(adis16240_channels); +	indio_dev->modes = INDIO_DIRECT_MODE; -	ret = adis16240_configure_ring(st->indio_dev); +	ret = adis_init(st, indio_dev, spi, &adis16240_data);  	if (ret) -		goto error_free_dev; - -	ret = iio_device_register(st->indio_dev); +		return ret; +	ret = adis_setup_buffer_and_trigger(st, indio_dev, NULL);  	if (ret) -		goto error_unreg_ring_funcs; -	regdone = 1; - -	ret = iio_ring_buffer_register(st->indio_dev->ring, 0); -	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; -	} +		return ret;  	/* Get the device into a sane initial state */ -	ret = adis16240_initial_setup(st); +	ret = adis_initial_startup(st);  	if (ret) -		goto error_remove_trigger; +		goto error_cleanup_buffer_trigger; +	ret = iio_device_register(indio_dev); +	if (ret) +		goto error_cleanup_buffer_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: -	iio_ring_buffer_unregister(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: +error_cleanup_buffer_trigger: +	adis_cleanup_buffer_and_trigger(st, indio_dev);  	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); +	struct iio_dev *indio_dev = spi_get_drvdata(spi); +	struct adis *st = iio_priv(indio_dev); -	iio_ring_buffer_unregister(indio_dev->ring);  	iio_device_unregister(indio_dev); -	adis16240_unconfigure_ring(indio_dev); -	kfree(st->tx); -	kfree(st->rx); -	kfree(st); +	adis_cleanup_buffer_and_trigger(st, indio_dev);  	return 0;  } @@ -585,21 +288,11 @@ static struct spi_driver adis16240_driver = {  		.owner = THIS_MODULE,  	},  	.probe = adis16240_probe, -	.remove = __devexit_p(adis16240_remove), +	.remove = 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_spi_driver(adis16240_driver);  MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>");  MODULE_DESCRIPTION("Analog Devices Programmable Impact Sensor and Recorder");  MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("spi:adis16240"); diff --git a/drivers/staging/iio/accel/adis16240_ring.c b/drivers/staging/iio/accel/adis16240_ring.c deleted file mode 100644 index f882e9c150e..00000000000 --- a/drivers/staging/iio/accel/adis16240_ring.c +++ /dev/null @@ -1,201 +0,0 @@ -#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/slab.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" - -static IIO_SCAN_EL_C(in_supply, ADIS16240_SCAN_SUPPLY, -		ADIS16240_SUPPLY_OUT, NULL); -static IIO_CONST_ATTR_SCAN_EL_TYPE(in_supply, u, 10, 16); -static IIO_SCAN_EL_C(accel_x, ADIS16240_SCAN_ACC_X, ADIS16240_XACCL_OUT, NULL); -static IIO_SCAN_EL_C(accel_y, ADIS16240_SCAN_ACC_Y, ADIS16240_YACCL_OUT, NULL); -static IIO_SCAN_EL_C(accel_z, ADIS16240_SCAN_ACC_Z, ADIS16240_ZACCL_OUT, NULL); -static IIO_CONST_ATTR_SCAN_EL_TYPE(accel, s, 10, 16); -static IIO_SCAN_EL_C(in0, ADIS16240_SCAN_AUX_ADC, ADIS16240_AUX_ADC, NULL); -static IIO_CONST_ATTR_SCAN_EL_TYPE(in0, u, 10, 16); -static IIO_SCAN_EL_C(temp, ADIS16240_SCAN_TEMP, ADIS16240_TEMP_OUT, NULL); -static IIO_CONST_ATTR_SCAN_EL_TYPE(temp, u, 10, 16); -static IIO_SCAN_EL_TIMESTAMP(6); -static IIO_CONST_ATTR_SCAN_EL_TYPE(timestamp, s, 64, 64); - -static struct attribute *adis16240_scan_el_attrs[] = { -	&iio_scan_el_in_supply.dev_attr.attr, -	&iio_const_attr_in_supply_index.dev_attr.attr, -	&iio_const_attr_in_supply_type.dev_attr.attr, -	&iio_scan_el_accel_x.dev_attr.attr, -	&iio_const_attr_accel_x_index.dev_attr.attr, -	&iio_scan_el_accel_y.dev_attr.attr, -	&iio_const_attr_accel_y_index.dev_attr.attr, -	&iio_scan_el_accel_z.dev_attr.attr, -	&iio_const_attr_accel_z_index.dev_attr.attr, -	&iio_const_attr_accel_type.dev_attr.attr, -	&iio_scan_el_in0.dev_attr.attr, -	&iio_const_attr_in0_index.dev_attr.attr, -	&iio_const_attr_in0_type.dev_attr.attr, -	&iio_scan_el_temp.dev_attr.attr, -	&iio_const_attr_temp_index.dev_attr.attr, -	&iio_const_attr_temp_type.dev_attr.attr, -	&iio_scan_el_timestamp.dev_attr.attr, -	&iio_const_attr_timestamp_index.dev_attr.attr, -	&iio_const_attr_timestamp_type.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, s64 time) -{ -	struct adis16240_state *st = iio_dev_get_devdata(indio_dev); -	st->last_timestamp = time; -	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); -	struct iio_ring_buffer *ring = st->indio_dev->ring; - -	int i = 0; -	s16 *data; -	size_t datasize = ring->access.get_bytes_per_datum(ring); - -	data = kmalloc(datasize , GFP_KERNEL); -	if (data == NULL) { -		dev_err(&st->us->dev, "memory alloc failed in ring bh"); -		return; -	} - -	if (ring->scan_count) -		if (adis16240_read_ring_data(&st->indio_dev->dev, st->rx) >= 0) -			for (; i < ring->scan_count; i++) -				data[i] = be16_to_cpup( -					(__be16 *)&(st->rx[i*2])); - -	/* Guaranteed to be aligned with 8 byte boundary */ -	if (ring->scan_timestamp) -		*((s64 *)(data + ((i + 3)/4)*4)) = st->last_timestamp; - -	ring->access.store_to(ring, -			(u8 *)data, -			st->last_timestamp); - -	iio_trigger_notify_done(st->indio_dev->trig); -	kfree(data); - -	return; -} - -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); - -	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->bpe = 2; -	ring->scan_el_attrs = &adis16240_scan_el_group; -	ring->scan_timestamp = true; -	ring->preenable = &iio_sw_ring_preenable; -	ring->postenable = &iio_triggered_ring_postenable; -	ring->predisable = &iio_triggered_ring_predisable; -	ring->owner = THIS_MODULE; - -	/* Set default scan mode */ -	iio_scan_mask_set(ring, iio_scan_el_in_supply.number); -	iio_scan_mask_set(ring, iio_scan_el_accel_x.number); -	iio_scan_mask_set(ring, iio_scan_el_accel_y.number); -	iio_scan_mask_set(ring, iio_scan_el_accel_z.number); -	iio_scan_mask_set(ring, iio_scan_el_temp.number); -	iio_scan_mask_set(ring, iio_scan_el_in0.number); - -	ret = iio_alloc_pollfunc(indio_dev, NULL, &adis16240_poll_func_th); -	if (ret) -		goto error_iio_sw_rb_free; - -	indio_dev->modes |= INDIO_RING_TRIGGERED; -	return 0; - -error_iio_sw_rb_free: -	iio_sw_rb_free(indio_dev->ring); -	return ret; -} - diff --git a/drivers/staging/iio/accel/adis16240_trigger.c b/drivers/staging/iio/accel/adis16240_trigger.c deleted file mode 100644 index 6cb8681f285..00000000000 --- a/drivers/staging/iio/accel/adis16240_trigger.c +++ /dev/null @@ -1,122 +0,0 @@ -#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; - -	iio_trigger_poll(trig, timestamp); - -	return IRQ_HANDLED; -} - -IIO_EVENT_SH(data_rdy_trig, &adis16240_data_rdy_trig_poll); - -static IIO_TRIGGER_NAME_ATTR; - -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 = kasprintf(GFP_KERNEL, -				   "adis16240-dev%d", -				   indio_dev->id); -	if (!st->trig->name) { -		ret = -ENOMEM; -		goto error_free_trig; -	} -	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 deleted file mode 100644 index faf73d7892e..00000000000 --- a/drivers/staging/iio/accel/inclinometer.h +++ /dev/null @@ -1,25 +0,0 @@ -/* - * 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) - -#define IIO_CONST_ATTR_INCLI_SCALE(_string) \ -	IIO_CONST_ATTR(incli_scale, _string) diff --git a/drivers/staging/iio/accel/kxsd9.c b/drivers/staging/iio/accel/kxsd9.c deleted file mode 100644 index 79f57950ebe..00000000000 --- a/drivers/staging/iio/accel/kxsd9.c +++ /dev/null @@ -1,404 +0,0 @@ -/* - * kxsd9.c	simple support for the Kionix KXSD9 3D - *		accelerometer. - * - * Copyright (c) 2008-2009 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. - * - * The i2c interface is very similar, so shouldn't be a problem once - * I have a suitable wire made up. - * - * TODO:	Support the motion detector - *		Uses register address incrementing so could have a - *		heavily optimized ring buffer access function. - */ - -#include <linux/device.h> -#include <linux/kernel.h> -#include <linux/spi/spi.h> -#include <linux/sysfs.h> -#include <linux/slab.h> - -#include "../iio.h" -#include "../sysfs.h" -#include "../adc/adc.h" -#include "accel.h" - -#define KXSD9_REG_X		0x00 -#define KXSD9_REG_Y		0x02 -#define KXSD9_REG_Z		0x04 -#define KXSD9_REG_AUX		0x06 -#define KXSD9_REG_RESET		0x0a -#define KXSD9_REG_CTRL_C	0x0c - -#define KXSD9_FS_8		0x00 -#define KXSD9_FS_6		0x01 -#define KXSD9_FS_4		0x02 -#define KXSD9_FS_2		0x03 -#define KXSD9_FS_MASK		0x03 - -#define KXSD9_REG_CTRL_B	0x0d -#define KXSD9_REG_CTRL_A	0x0e - -#define KXSD9_READ(a) (0x80 | (a)) -#define KXSD9_WRITE(a) (a) - -#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 -/** - * struct kxsd9_state - device related storage - * @buf_lock:	protect the rx and tx buffers. - * @indio_dev:	associated industrial IO device - * @us:		spi device - * @rx:		single rx buffer storage - * @tx:		single tx buffer storage - **/ -struct kxsd9_state { -	struct mutex buf_lock; -	struct iio_dev *indio_dev; -	struct spi_device *us; -	u8 *rx; -	u8 *tx; -}; - -/* This may want to move to mili g to allow for non integer ranges */ -static ssize_t kxsd9_read_scale(struct device *dev, -				struct device_attribute *attr, -				char *buf) -{ -	int ret; -	ssize_t len = 0; -	struct iio_dev *indio_dev = dev_get_drvdata(dev); -	struct kxsd9_state *st = indio_dev->dev_data; -	struct spi_transfer xfer = { -		.bits_per_word = 8, -		.len = 2, -		.cs_change = 1, -		.tx_buf = st->tx, -		.rx_buf = st->rx, -	}; -	struct spi_message msg; - -	mutex_lock(&st->buf_lock); -	st->tx[0] = KXSD9_READ(KXSD9_REG_CTRL_C); -	st->tx[1] = 0; -	spi_message_init(&msg); -	spi_message_add_tail(&xfer, &msg); -	ret = spi_sync(st->us, &msg); -	if (ret) -		goto error_ret; - -	switch (st->rx[1] & KXSD9_FS_MASK) { -	case KXSD9_FS_8: -		len += sprintf(buf, "%s\n", KXSD9_SCALE_8G); -		break; -	case KXSD9_FS_6: -		len += sprintf(buf, "%s\n", KXSD9_SCALE_6G); -		break; -	case KXSD9_FS_4: -		len += sprintf(buf, "%s\n", KXSD9_SCALE_4G); -		break; -	case KXSD9_FS_2: -		len += sprintf(buf, "%s\n", KXSD9_SCALE_2G); -		break; -	} - -error_ret: -	mutex_unlock(&st->buf_lock); - -	return ret ? ret : len; -} -static ssize_t kxsd9_write_scale(struct device *dev, -				 struct device_attribute *attr, -				 const char *buf, -				 size_t len) -{ - -	struct spi_message msg; -	int ret; -	struct iio_dev *indio_dev = dev_get_drvdata(dev); -	struct kxsd9_state *st = indio_dev->dev_data; -	u8 val; -	struct spi_transfer xfers[] = { -		{ -			.bits_per_word = 8, -			.len = 2, -			.cs_change = 1, -			.tx_buf = st->tx, -			.rx_buf = st->rx, -		}, { -			.bits_per_word = 8, -			.len = 2, -			.cs_change = 1, -			.tx_buf = st->tx, -		}, -	}; - -	if (!strncmp(buf, KXSD9_SCALE_8G, -		     strlen(buf) < strlen(KXSD9_SCALE_8G) -		     ? strlen(buf) : strlen(KXSD9_SCALE_8G))) -		val = KXSD9_FS_8; -	else if (!strncmp(buf, KXSD9_SCALE_6G, -			  strlen(buf) < strlen(KXSD9_SCALE_6G) -			  ? strlen(buf) : strlen(KXSD9_SCALE_6G))) -		val = KXSD9_FS_6; -	else if (!strncmp(buf, KXSD9_SCALE_4G, -			  strlen(buf) < strlen(KXSD9_SCALE_4G) -			  ? strlen(buf) : strlen(KXSD9_SCALE_4G))) -		val = KXSD9_FS_4; -	else if (!strncmp(buf, KXSD9_SCALE_2G, -			  strlen(buf) < strlen(KXSD9_SCALE_2G) -			  ? strlen(buf) : strlen(KXSD9_SCALE_2G))) -		val = KXSD9_FS_2; -	else -		return -EINVAL; - -	mutex_lock(&st->buf_lock); -	st->tx[0] = KXSD9_READ(KXSD9_REG_CTRL_C); -	st->tx[1] = 0; -	spi_message_init(&msg); -	spi_message_add_tail(&xfers[0], &msg); -	ret = spi_sync(st->us, &msg); -	if (ret) -		goto error_ret; -	st->tx[0] = KXSD9_WRITE(KXSD9_REG_CTRL_C); -	st->tx[1] = (st->rx[1] & ~KXSD9_FS_MASK) | val; - -	spi_message_init(&msg); -	spi_message_add_tail(&xfers[1], &msg); -	ret = spi_sync(st->us, &msg); -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) -{ -	struct spi_message msg; -	int ret; -	ssize_t len = 0; -	u16 val; -	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); -	struct iio_dev *indio_dev = dev_get_drvdata(dev); -	struct kxsd9_state *st = indio_dev->dev_data; -	struct spi_transfer xfers[] = { -		{ -			.bits_per_word = 8, -			.len = 1, -			.cs_change = 0, -			.delay_usecs = 200, -			.tx_buf = st->tx, -		}, { -			.bits_per_word = 8, -			.len = 2, -			.cs_change = 1, -			.rx_buf = st->rx, -		}, -	}; - -	mutex_lock(&st->buf_lock); -	st->tx[0] = KXSD9_READ(this_attr->address); -	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) -		goto error_ret; -	val = (((u16)(st->rx[0])) << 8) | (st->rx[1] & 0xF0); -	len = sprintf(buf, "%d\n", val); -error_ret: -	mutex_unlock(&st->buf_lock); - -	return ret ? ret : len; -} - -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_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_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, -}; - -static const struct attribute_group kxsd9_attribute_group = { -	.attrs = kxsd9_attributes, -}; - -static int __devinit kxsd9_power_up(struct spi_device *spi) -{ -	int ret; -	struct spi_transfer xfers[2] = { -		{ -			.bits_per_word = 8, -			.len = 2, -			.cs_change = 1, -		}, { -			.bits_per_word = 8, -			.len = 2, -			.cs_change = 1, -		}, -	}; -	struct spi_message msg; -	u8 *tx2; -	u8 *tx = kmalloc(2, GFP_KERNEL); - -	if (tx == NULL) { -		ret = -ENOMEM; -		goto error_ret; -	} -	tx2 = kmalloc(2, GFP_KERNEL); -	if (tx2 == NULL) { -		ret = -ENOMEM; -		goto error_free_tx; -	} -	tx[0] = 0x0d; -	tx[1] = 0x40; - -	tx2[0] = 0x0c; -	tx2[1] = 0x9b; - -	xfers[0].tx_buf = tx; -	xfers[1].tx_buf = tx2; -	spi_message_init(&msg); -	spi_message_add_tail(&xfers[0], &msg); -	spi_message_add_tail(&xfers[1], &msg); -	ret = spi_sync(spi, &msg); - -	kfree(tx2); -error_free_tx: -	kfree(tx); -error_ret: -	return ret; - -}; - -static int __devinit kxsd9_probe(struct spi_device *spi) -{ - -	struct kxsd9_state *st; -	int ret = 0; - -	st = kzalloc(sizeof(*st), GFP_KERNEL); -	if (st == NULL) { -		ret = -ENOMEM; -		goto error_ret; -	} -	spi_set_drvdata(spi, st); - -	st->rx = kmalloc(sizeof(*st->rx)*KXSD9_STATE_RX_SIZE, -			 GFP_KERNEL); -	if (st->rx == NULL) { -		ret = -ENOMEM; -		goto error_free_st; -	} -	st->tx = kmalloc(sizeof(*st->tx)*KXSD9_STATE_TX_SIZE, -			 GFP_KERNEL); -	if (st->tx == NULL) { -		ret = -ENOMEM; -		goto error_free_rx; -	} - -	st->us = spi; -	mutex_init(&st->buf_lock); -	st->indio_dev = iio_allocate_device(); -	if (st->indio_dev == NULL) { -		ret = -ENOMEM; -		goto error_free_tx; -	} -	st->indio_dev->dev.parent = &spi->dev; -	/* for now */ -	st->indio_dev->num_interrupt_lines = 0; -	st->indio_dev->event_attrs = NULL; - -	st->indio_dev->attrs = &kxsd9_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; - -	spi->mode = SPI_MODE_0; -	spi_setup(spi); -	kxsd9_power_up(spi); - -	return 0; - -error_free_dev: -	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 __devexit kxsd9_remove(struct spi_device *spi) -{ -	struct kxsd9_state *st = spi_get_drvdata(spi); - -	iio_device_unregister(st->indio_dev); -	kfree(st->tx); -	kfree(st->rx); -	kfree(st); - -	return 0; -} - -static struct spi_driver kxsd9_driver = { -	.driver = { -		.name = "kxsd9", -		.owner = THIS_MODULE, -	}, -	.probe = kxsd9_probe, -	.remove = __devexit_p(kxsd9_remove), -}; - -static __init int kxsd9_spi_init(void) -{ -	return spi_register_driver(&kxsd9_driver); -} -module_init(kxsd9_spi_init); - -static __exit void kxsd9_spi_exit(void) -{ -	spi_unregister_driver(&kxsd9_driver); -} -module_exit(kxsd9_spi_exit); - -MODULE_AUTHOR("Jonathan Cameron <jic23@cam.ac.uk>"); -MODULE_DESCRIPTION("Kionix KXSD9 SPI driver"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/iio/accel/lis3l02dq.h b/drivers/staging/iio/accel/lis3l02dq.h index 6e730553fca..0a8ea827086 100644 --- a/drivers/staging/iio/accel/lis3l02dq.h +++ b/drivers/staging/iio/accel/lis3l02dq.h @@ -2,7 +2,7 @@   * LISL02DQ.h -- support STMicroelectronics LISD02DQ   *               3d 2g Linear Accelerometers via SPI   * - * Copyright (c) 2007 Jonathan Cameron <jic23@cam.ac.uk> + * Copyright (c) 2007 Jonathan Cameron <jic23@kernel.org>   *   * Loosely based upon tle62x0.c   * @@ -28,7 +28,7 @@  /* Control Register (1 of 2) */  #define LIS3L02DQ_REG_CTRL_1_ADDR		0x20  /* Power ctrl - either bit set corresponds to on*/ -#define LIS3L02DQ_REG_CTRL_1_PD_ON	0xC0 +#define LIS3L02DQ_REG_CTRL_1_PD_ON		0xC0  /* Decimation Factor  */  #define LIS3L02DQ_DEC_MASK			0x30 @@ -57,7 +57,7 @@  /* Reboot memory content */  #define LIS3L02DQ_REG_CTRL_2_REBOOT_MEMORY	0x10 -/* Interupt Enable - applies data ready to the RDY pad */ +/* Interrupt Enable - applies data ready to the RDY pad */  #define LIS3L02DQ_REG_CTRL_2_ENABLE_INTERRUPT	0x08  /* Enable Data Ready Generation - relationship with previous unclear in docs */ @@ -70,34 +70,34 @@   * - option for 16 bit left justified */  #define LIS3L02DQ_REG_CTRL_2_DATA_ALIGNMENT_16_BIT_LEFT_JUSTIFIED	0x01 -/* Interupt related stuff */ +/* Interrupt related stuff */  #define LIS3L02DQ_REG_WAKE_UP_CFG_ADDR			0x23 -/* Switch from or combination fo conditions to and */ +/* Switch from or combination of conditions to and */  #define LIS3L02DQ_REG_WAKE_UP_CFG_BOOLEAN_AND		0x80 -/* Latch interupt request, +/* Latch interrupt request,   * if on ack must be given by reading the ack register */  #define LIS3L02DQ_REG_WAKE_UP_CFG_LATCH_SRC		0x40 -/* Z Interupt on High (above threshold)*/ +/* Z Interrupt on High (above threshold) */  #define LIS3L02DQ_REG_WAKE_UP_CFG_INTERRUPT_Z_HIGH	0x20 -/* Z Interupt on Low */ +/* Z Interrupt on Low */  #define LIS3L02DQ_REG_WAKE_UP_CFG_INTERRUPT_Z_LOW	0x10 -/* Y Interupt on High */ +/* Y Interrupt on High */  #define LIS3L02DQ_REG_WAKE_UP_CFG_INTERRUPT_Y_HIGH	0x08 -/* Y Interupt on Low */ +/* Y Interrupt on Low */  #define LIS3L02DQ_REG_WAKE_UP_CFG_INTERRUPT_Y_LOW	0x04 -/* X Interupt on High */ +/* X Interrupt on High */  #define LIS3L02DQ_REG_WAKE_UP_CFG_INTERRUPT_X_HIGH	0x02 -/* X Interupt on Low */ +/* X Interrupt on Low */  #define LIS3L02DQ_REG_WAKE_UP_CFG_INTERRUPT_X_LOW 0x01 -/* Register that gives description of what caused interupt +/* Register that gives description of what caused interrupt   * - latched if set in CFG_ADDRES */  #define LIS3L02DQ_REG_WAKE_UP_SRC_ADDR			0x24  /* top bit ignored */ -/* Interupt Active */ +/* Interrupt Active */  #define LIS3L02DQ_REG_WAKE_UP_SRC_INTERRUPT_ACTIVATED	0x40  /* Interupts that have been triggered */  #define LIS3L02DQ_REG_WAKE_UP_SRC_INTERRUPT_Z_HIGH	0x20 @@ -117,13 +117,13 @@  #define LIS3L02DQ_REG_STATUS_Y_OVERRUN			0x20  #define LIS3L02DQ_REG_STATUS_X_OVERRUN			0x10  /* XYZ new data available - first is all 3 available? */ -#define LIS3L02DQ_REG_STATUS_XYZ_NEW_DATA 0x08 +#define LIS3L02DQ_REG_STATUS_XYZ_NEW_DATA		0x08  #define LIS3L02DQ_REG_STATUS_Z_NEW_DATA			0x04  #define LIS3L02DQ_REG_STATUS_Y_NEW_DATA			0x02  #define LIS3L02DQ_REG_STATUS_X_NEW_DATA			0x01  /* The accelerometer readings - low and high bytes. -Form of high byte dependant on justification set in ctrl reg */ + * Form of high byte dependent on justification set in ctrl reg */  #define LIS3L02DQ_REG_OUT_X_L_ADDR			0x28  #define LIS3L02DQ_REG_OUT_X_H_ADDR			0x29  #define LIS3L02DQ_REG_OUT_Y_L_ADDR			0x2A @@ -148,55 +148,48 @@ Form of high byte dependant on justification set in ctrl reg */  #define LIS3L02DQ_MAX_RX 12  /**   * struct lis3l02dq_state - device instance specific data - * @helper:		data and func pointer allowing generic functions   * @us:			actual spi_device - * @work_thresh:	bh for threshold events - * @thresh_timestamp:	timestamp for threshold interrupts. - * @inter:		used to check if new interrupt has been triggered   * @trig:		data ready trigger registered with iio - * @tx:			transmit buffer - * @rx:			recieve buffer   * @buf_lock:		mutex to protect tx and rx + * @tx:			transmit buffer + * @rx:			receive buffer   **/  struct lis3l02dq_state { -	struct iio_sw_ring_helper_state	help;  	struct spi_device		*us; -	struct work_struct		work_thresh; -	s64				thresh_timestamp; -	bool				inter;  	struct iio_trigger		*trig; -	u8				*tx; -	u8				*rx;  	struct mutex			buf_lock; -}; +	int				gpio; +	bool				trigger_on; -#define lis3l02dq_h_to_s(_h)				\ -	container_of(_h, struct lis3l02dq_state, help) +	u8	tx[LIS3L02DQ_MAX_RX] ____cacheline_aligned; +	u8	rx[LIS3L02DQ_MAX_RX] ____cacheline_aligned; +}; -int lis3l02dq_spi_read_reg_8(struct device *dev, +int lis3l02dq_spi_read_reg_8(struct iio_dev *indio_dev,  			     u8 reg_address,  			     u8 *val); -int lis3l02dq_spi_write_reg_8(struct device *dev, +int lis3l02dq_spi_write_reg_8(struct iio_dev *indio_dev,  			      u8 reg_address, -			      u8 *val); +			      u8 val); + +int lis3l02dq_disable_all_events(struct iio_dev *indio_dev); -#ifdef CONFIG_IIO_RING_BUFFER -/* At the moment triggers are only used for ring buffer +#ifdef CONFIG_IIO_BUFFER +/* At the moment triggers are only used for buffer   * filling. This may change!   */  void lis3l02dq_remove_trigger(struct iio_dev *indio_dev);  int lis3l02dq_probe_trigger(struct iio_dev *indio_dev); -ssize_t lis3l02dq_read_accel_from_ring(struct device *dev, -				       struct device_attribute *attr, -				       char *buf); +int lis3l02dq_configure_buffer(struct iio_dev *indio_dev); +void lis3l02dq_unconfigure_buffer(struct iio_dev *indio_dev); +irqreturn_t lis3l02dq_data_rdy_trig_poll(int irq, void *private); +#define lis3l02dq_th lis3l02dq_data_rdy_trig_poll -int lis3l02dq_configure_ring(struct iio_dev *indio_dev); -void lis3l02dq_unconfigure_ring(struct iio_dev *indio_dev); - -#else /* CONFIG_IIO_RING_BUFFER */ +#else /* CONFIG_IIO_BUFFER */ +#define lis3l02dq_th lis3l02dq_nobuffer  static inline void lis3l02dq_remove_trigger(struct iio_dev *indio_dev)  { @@ -206,20 +199,12 @@ static inline int lis3l02dq_probe_trigger(struct iio_dev *indio_dev)  	return 0;  } -static inline ssize_t -lis3l02dq_read_accel_from_ring(struct device *dev, -			       struct device_attribute *attr, -			       char *buf) -{ -	return 0; -} - -static int lis3l02dq_configure_ring(struct iio_dev *indio_dev) +static int lis3l02dq_configure_buffer(struct iio_dev *indio_dev)  {  	return 0;  } -static inline void lis3l02dq_unconfigure_ring(struct iio_dev *indio_dev) +static inline void lis3l02dq_unconfigure_buffer(struct iio_dev *indio_dev)  {  } -#endif /* CONFIG_IIO_RING_BUFFER */ +#endif /* CONFIG_IIO_BUFFER */  #endif /* SPI_LIS3L02DQ_H_ */ diff --git a/drivers/staging/iio/accel/lis3l02dq_core.c b/drivers/staging/iio/accel/lis3l02dq_core.c index c4b4ab7e642..898653c0927 100644 --- a/drivers/staging/iio/accel/lis3l02dq_core.c +++ b/drivers/staging/iio/accel/lis3l02dq_core.c @@ -2,7 +2,7 @@   * lis3l02dq.c	support STMicroelectronics LISD02DQ   *		3d 2g Linear Accelerometers via SPI   * - * Copyright (c) 2007 Jonathan Cameron <jic23@cam.ac.uk> + * Copyright (c) 2007 Jonathan Cameron <jic23@kernel.org>   *   * 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 @@ -15,22 +15,19 @@  #include <linux/interrupt.h>  #include <linux/irq.h>  #include <linux/gpio.h> -#include <linux/workqueue.h> +#include <linux/of_gpio.h>  #include <linux/mutex.h>  #include <linux/device.h>  #include <linux/kernel.h>  #include <linux/spi/spi.h>  #include <linux/slab.h> -  #include <linux/sysfs.h> -#include <linux/list.h> - -#include "../iio.h" -#include "../sysfs.h" -#include "../ring_generic.h" -#include "../ring_sw.h" +#include <linux/module.h> -#include "accel.h" +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> +#include <linux/iio/events.h> +#include <linux/iio/buffer.h>  #include "lis3l02dq.h" @@ -38,36 +35,37 @@   * 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.   */ +/* direct copy of the irq_default_primary_handler */ +#ifndef CONFIG_IIO_BUFFER +static irqreturn_t lis3l02dq_nobuffer(int irq, void *private) +{ +	return IRQ_WAKE_THREAD; +} +#endif  /**   * lis3l02dq_spi_read_reg_8() - read single byte from a single register - * @dev: device asosciated with child of actual device (iio_dev or iio_trig) + * @indio_dev: iio_dev for this actual device   * @reg_address: the address of the register to be read   * @val: pass back the resulting value   **/ -int lis3l02dq_spi_read_reg_8(struct device *dev, u8 reg_address, u8 *val) +int lis3l02dq_spi_read_reg_8(struct iio_dev *indio_dev, +			     u8 reg_address, u8 *val)  { +	struct lis3l02dq_state *st = iio_priv(indio_dev);  	int ret; -	struct spi_message msg; -	struct iio_dev *indio_dev = dev_get_drvdata(dev); -	struct iio_sw_ring_helper_state *h = iio_dev_get_devdata(indio_dev); -	struct lis3l02dq_state *st = lis3l02dq_h_to_s(h); -  	struct spi_transfer xfer = {  		.tx_buf = st->tx,  		.rx_buf = st->rx,  		.bits_per_word = 8,  		.len = 2, -		.cs_change = 1,  	};  	mutex_lock(&st->buf_lock);  	st->tx[0] = LIS3L02DQ_READ_REG(reg_address);  	st->tx[1] = 0; -	spi_message_init(&msg); -	spi_message_add_tail(&xfer, &msg); -	ret = spi_sync(st->us, &msg); +	ret = spi_sync_transfer(st->us, &xfer, 1);  	*val = st->rx[1];  	mutex_unlock(&st->buf_lock); @@ -76,34 +74,21 @@ int lis3l02dq_spi_read_reg_8(struct device *dev, u8 reg_address, u8 *val)  /**   * lis3l02dq_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 writen + * @indio_dev: iio_dev for this device + * @reg_address: the address of the register to be written   * @val: the value to write   **/ -int lis3l02dq_spi_write_reg_8(struct device *dev, +int lis3l02dq_spi_write_reg_8(struct iio_dev *indio_dev,  			      u8 reg_address, -			      u8 *val) +			      u8 val)  {  	int ret; -	struct spi_message msg; -	struct iio_dev *indio_dev = dev_get_drvdata(dev); -	struct iio_sw_ring_helper_state *h -		= iio_dev_get_devdata(indio_dev); -	struct lis3l02dq_state *st = lis3l02dq_h_to_s(h); -	struct spi_transfer xfer = { -		.tx_buf = st->tx, -		.bits_per_word = 8, -		.len = 2, -		.cs_change = 1, -	}; +	struct lis3l02dq_state *st = iio_priv(indio_dev);  	mutex_lock(&st->buf_lock);  	st->tx[0] = LIS3L02DQ_WRITE_REG(reg_address); -	st->tx[1] = *val; - -	spi_message_init(&msg); -	spi_message_add_tail(&xfer, &msg); -	ret = spi_sync(st->us, &msg); +	st->tx[1] = val; +	ret = spi_write(st->us, st->tx, 2);  	mutex_unlock(&st->buf_lock);  	return ret; @@ -111,21 +96,17 @@ int lis3l02dq_spi_write_reg_8(struct device *dev,  /**   * lisl302dq_spi_write_reg_s16() - 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 + * @indio_dev: iio_dev for this device + * @lower_reg_address: the address of the lower of the two registers. + *               Second register is assumed to have address one greater. + * @value: value to be written   **/ -static int lis3l02dq_spi_write_reg_s16(struct device *dev, +static int lis3l02dq_spi_write_reg_s16(struct iio_dev *indio_dev,  				       u8 lower_reg_address,  				       s16 value)  {  	int ret; -	struct spi_message msg; -	struct iio_dev *indio_dev = dev_get_drvdata(dev); -	struct iio_sw_ring_helper_state *h -		= iio_dev_get_devdata(indio_dev); -	struct lis3l02dq_state *st = lis3l02dq_h_to_s(h); +	struct lis3l02dq_state *st = iio_priv(indio_dev);  	struct spi_transfer xfers[] = { {  			.tx_buf = st->tx,  			.bits_per_word = 8, @@ -135,7 +116,6 @@ static int lis3l02dq_spi_write_reg_s16(struct device *dev,  			.tx_buf = st->tx + 2,  			.bits_per_word = 8,  			.len = 2, -			.cs_change = 1,  		},  	}; @@ -145,32 +125,19 @@ static int lis3l02dq_spi_write_reg_s16(struct device *dev,  	st->tx[2] = LIS3L02DQ_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); +	ret = spi_sync_transfer(st->us, xfers, ARRAY_SIZE(xfers));  	mutex_unlock(&st->buf_lock);  	return ret;  } -/** - * lisl302dq_spi_read_reg_s16() - 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: somewhere to pass back the value read - **/ -static int lis3l02dq_spi_read_reg_s16(struct device *dev, -				      u8 lower_reg_address, -				      s16 *val) +static int lis3l02dq_read_reg_s16(struct iio_dev *indio_dev, +				  u8 lower_reg_address, +				  int *val)  { -	struct spi_message msg; -	struct iio_dev *indio_dev = dev_get_drvdata(dev); -	struct iio_sw_ring_helper_state *h -		= iio_dev_get_devdata(indio_dev); -	struct lis3l02dq_state *st = lis3l02dq_h_to_s(h); +	struct lis3l02dq_state *st = iio_priv(indio_dev);  	int ret; +	s16 tempval;  	struct spi_transfer xfers[] = { {  			.tx_buf = st->tx,  			.rx_buf = st->rx, @@ -182,163 +149,160 @@ static int lis3l02dq_spi_read_reg_s16(struct device *dev,  			.rx_buf = st->rx + 2,  			.bits_per_word = 8,  			.len = 2, -			.cs_change = 1, -  		},  	};  	mutex_lock(&st->buf_lock);  	st->tx[0] = LIS3L02DQ_READ_REG(lower_reg_address);  	st->tx[1] = 0; -	st->tx[2] = LIS3L02DQ_READ_REG(lower_reg_address+1); +	st->tx[2] = LIS3L02DQ_READ_REG(lower_reg_address + 1);  	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); +	ret = spi_sync_transfer(st->us, xfers, ARRAY_SIZE(xfers));  	if (ret) {  		dev_err(&st->us->dev, "problem when reading 16 bit register");  		goto error_ret;  	} -	*val = (s16)(st->rx[1]) | ((s16)(st->rx[3]) << 8); +	tempval = (s16)(st->rx[1]) | ((s16)(st->rx[3]) << 8); +	*val = tempval;  error_ret:  	mutex_unlock(&st->buf_lock);  	return ret;  } -/** - * lis3l02dq_read_signed() - attribute function used for 8 bit signed values - * @dev: the child device associated with the iio_dev or iio_trigger - * @attr: the attribute being processed - * @buf: buffer into which put the output string - **/ -static ssize_t lis3l02dq_read_signed(struct device *dev, -				     struct device_attribute *attr, -				     char *buf) -{ -	int ret; -	s8 val; -	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); - -	ret = lis3l02dq_spi_read_reg_8(dev, this_attr->address, (u8 *)&val); - -	return ret ? ret : sprintf(buf, "%d\n", val); -} - -static ssize_t lis3l02dq_read_unsigned(struct device *dev, -				       struct device_attribute *attr, -				       char *buf) -{ -	int ret; -	u8 val; -	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); - -	ret = lis3l02dq_spi_read_reg_8(dev, this_attr->address, &val); +enum lis3l02dq_rm_ind { +	LIS3L02DQ_ACCEL, +	LIS3L02DQ_GAIN, +	LIS3L02DQ_BIAS, +}; -	return ret ? ret : sprintf(buf, "%d\n", val); -} +static u8 lis3l02dq_axis_map[3][3] = { +	[LIS3L02DQ_ACCEL] = { LIS3L02DQ_REG_OUT_X_L_ADDR, +			      LIS3L02DQ_REG_OUT_Y_L_ADDR, +			      LIS3L02DQ_REG_OUT_Z_L_ADDR }, +	[LIS3L02DQ_GAIN] = { LIS3L02DQ_REG_GAIN_X_ADDR, +			     LIS3L02DQ_REG_GAIN_Y_ADDR, +			     LIS3L02DQ_REG_GAIN_Z_ADDR }, +	[LIS3L02DQ_BIAS] = { LIS3L02DQ_REG_OFFSET_X_ADDR, +			     LIS3L02DQ_REG_OFFSET_Y_ADDR, +			     LIS3L02DQ_REG_OFFSET_Z_ADDR } +}; -static ssize_t lis3l02dq_write_signed(struct device *dev, -				      struct device_attribute *attr, -				      const char *buf, -				      size_t len) +static int lis3l02dq_read_thresh(struct iio_dev *indio_dev, +				 const struct iio_chan_spec *chan, +				 enum iio_event_type type, +				 enum iio_event_direction dir, +				 enum iio_event_info info, +				 int *val, int *val2)  { -	long valin; -	s8 val;  	int ret; -	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); -	ret = strict_strtol(buf, 10, &valin); +	ret = lis3l02dq_read_reg_s16(indio_dev, LIS3L02DQ_REG_THS_L_ADDR, val);  	if (ret) -		goto error_ret; -	val = valin; -	ret = lis3l02dq_spi_write_reg_8(dev, this_attr->address, (u8 *)&val); - -error_ret: -	return ret ? ret : len; +		return ret; +	return IIO_VAL_INT;  } -static ssize_t lis3l02dq_write_unsigned(struct device *dev, -					struct device_attribute *attr, -					const char *buf, -					size_t len) +static int lis3l02dq_write_thresh(struct iio_dev *indio_dev, +				  const struct iio_chan_spec *chan, +				  enum iio_event_type type, +				  enum iio_event_direction dir, +				  enum iio_event_info info, +				  int val, int val2)  { -	int ret; -	ulong valin; -	u8 val; -	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); - -	ret = strict_strtoul(buf, 10, &valin); -	if (ret) -		goto err_ret; -	val = valin; -	ret = lis3l02dq_spi_write_reg_8(dev, this_attr->address, &val); - -err_ret: -	return ret ? ret : len; +	u16 value = val; +	return lis3l02dq_spi_write_reg_s16(indio_dev, +					   LIS3L02DQ_REG_THS_L_ADDR, +					   value);  } -static ssize_t lis3l02dq_read_16bit_signed(struct device *dev, -					   struct device_attribute *attr, -					   char *buf) +static int lis3l02dq_write_raw(struct iio_dev *indio_dev, +			       struct iio_chan_spec const *chan, +			       int val, +			       int val2, +			       long mask)  { -	int ret; -	s16 val = 0; -	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); - -	ret = lis3l02dq_spi_read_reg_s16(dev, this_attr->address, &val); - -	if (ret) -		return ret; - -	return sprintf(buf, "%d\n", val); -} - -static ssize_t lis3l02dq_read_accel(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); -	if (indio_dev->currentmode == INDIO_RING_TRIGGERED) -		ret = lis3l02dq_read_accel_from_ring(dev, attr, buf); -	else -		ret =  lis3l02dq_read_16bit_signed(dev, attr, buf); -	mutex_unlock(&indio_dev->mlock); - +	int ret = -EINVAL, reg; +	u8 uval; +	s8 sval; +	switch (mask) { +	case IIO_CHAN_INFO_CALIBBIAS: +		if (val > 255 || val < -256) +			return -EINVAL; +		sval = val; +		reg = lis3l02dq_axis_map[LIS3L02DQ_BIAS][chan->address]; +		ret = lis3l02dq_spi_write_reg_8(indio_dev, reg, sval); +		break; +	case IIO_CHAN_INFO_CALIBSCALE: +		if (val & ~0xFF) +			return -EINVAL; +		uval = val; +		reg = lis3l02dq_axis_map[LIS3L02DQ_GAIN][chan->address]; +		ret = lis3l02dq_spi_write_reg_8(indio_dev, reg, uval); +		break; +	}  	return ret;  } -static ssize_t lis3l02dq_write_16bit_signed(struct device *dev, -					    struct device_attribute *attr, -					    const char *buf, -					    size_t len) +static int lis3l02dq_read_raw(struct iio_dev *indio_dev, +			      struct iio_chan_spec const *chan, +			      int *val, +			      int *val2, +			      long mask)  { -	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 = lis3l02dq_spi_write_reg_s16(dev, this_attr->address, val); - +	u8 utemp; +	s8 stemp; +	ssize_t ret = 0; +	u8 reg; + +	switch (mask) { +	case IIO_CHAN_INFO_RAW: +		/* Take the iio_dev status lock */ +		mutex_lock(&indio_dev->mlock); +		if (indio_dev->currentmode == INDIO_BUFFER_TRIGGERED) { +			ret = -EBUSY; +		} else { +			reg = lis3l02dq_axis_map +				[LIS3L02DQ_ACCEL][chan->address]; +			ret = lis3l02dq_read_reg_s16(indio_dev, reg, val); +		} +		mutex_unlock(&indio_dev->mlock); +		if (ret < 0) +			goto error_ret; +		return IIO_VAL_INT; +	case IIO_CHAN_INFO_SCALE: +		*val = 0; +		*val2 = 9580; +		return IIO_VAL_INT_PLUS_MICRO; +	case IIO_CHAN_INFO_CALIBSCALE: +		reg = lis3l02dq_axis_map[LIS3L02DQ_GAIN][chan->address]; +		ret = lis3l02dq_spi_read_reg_8(indio_dev, reg, &utemp); +		if (ret) +			goto error_ret; +		/* to match with what previous code does */ +		*val = utemp; +		return IIO_VAL_INT; + +	case IIO_CHAN_INFO_CALIBBIAS: +		reg = lis3l02dq_axis_map[LIS3L02DQ_BIAS][chan->address]; +		ret = lis3l02dq_spi_read_reg_8(indio_dev, reg, (u8 *)&stemp); +		/* to match with what previous code does */ +		*val = stemp; +		return IIO_VAL_INT; +	}  error_ret: -	return ret ? ret : len; +	return ret;  }  static ssize_t lis3l02dq_read_frequency(struct device *dev,  					struct device_attribute *attr,  					char *buf)  { +	struct iio_dev *indio_dev = dev_to_iio_dev(dev);  	int ret, len = 0;  	s8 t; -	ret = lis3l02dq_spi_read_reg_8(dev, +	ret = lis3l02dq_spi_read_reg_8(indio_dev,  				       LIS3L02DQ_REG_CTRL_1_ADDR,  				       (u8 *)&t);  	if (ret) @@ -366,17 +330,17 @@ static ssize_t lis3l02dq_write_frequency(struct device *dev,  					 const char *buf,  					 size_t len)  { -	struct iio_dev *indio_dev = dev_get_drvdata(dev); -	long val; +	struct iio_dev *indio_dev = dev_to_iio_dev(dev); +	unsigned long val;  	int ret;  	u8 t; -	ret = strict_strtol(buf, 10, &val); +	ret = kstrtoul(buf, 10, &val);  	if (ret)  		return ret;  	mutex_lock(&indio_dev->mlock); -	ret = lis3l02dq_spi_read_reg_8(dev, +	ret = lis3l02dq_spi_read_reg_8(indio_dev,  				       LIS3L02DQ_REG_CTRL_1_ADDR,  				       &t);  	if (ret) @@ -399,11 +363,11 @@ static ssize_t lis3l02dq_write_frequency(struct device *dev,  	default:  		ret = -EINVAL;  		goto error_ret_mutex; -	}; +	} -	ret = lis3l02dq_spi_write_reg_8(dev, +	ret = lis3l02dq_spi_write_reg_8(indio_dev,  					LIS3L02DQ_REG_CTRL_1_ADDR, -					&t); +					t);  error_ret_mutex:  	mutex_unlock(&indio_dev->mlock); @@ -411,8 +375,9 @@ error_ret_mutex:  	return ret ? ret : len;  } -static int lis3l02dq_initial_setup(struct lis3l02dq_state *st) +static int lis3l02dq_initial_setup(struct iio_dev *indio_dev)  { +	struct lis3l02dq_state *st = iio_priv(indio_dev);  	int ret;  	u8 val, valtest; @@ -422,17 +387,17 @@ static int lis3l02dq_initial_setup(struct lis3l02dq_state *st)  	val = LIS3L02DQ_DEFAULT_CTRL1;  	/* Write suitable defaults to ctrl1 */ -	ret = lis3l02dq_spi_write_reg_8(&st->help.indio_dev->dev, +	ret = lis3l02dq_spi_write_reg_8(indio_dev,  					LIS3L02DQ_REG_CTRL_1_ADDR, -					&val); +					val);  	if (ret) {  		dev_err(&st->us->dev, "problem with setup control register 1");  		goto err_ret;  	} -	/* Repeat as sometimes doesn't work first time?*/ -	ret = lis3l02dq_spi_write_reg_8(&st->help.indio_dev->dev, +	/* Repeat as sometimes doesn't work first time? */ +	ret = lis3l02dq_spi_write_reg_8(indio_dev,  					LIS3L02DQ_REG_CTRL_1_ADDR, -					&val); +					val);  	if (ret) {  		dev_err(&st->us->dev, "problem with setup control register 1");  		goto err_ret; @@ -440,28 +405,29 @@ static int lis3l02dq_initial_setup(struct lis3l02dq_state *st)  	/* Read back to check this has worked acts as loose test of correct  	 * chip */ -	ret = lis3l02dq_spi_read_reg_8(&st->help.indio_dev->dev, +	ret = lis3l02dq_spi_read_reg_8(indio_dev,  				       LIS3L02DQ_REG_CTRL_1_ADDR,  				       &valtest);  	if (ret || (valtest != val)) { -		dev_err(&st->help.indio_dev->dev, "device not playing ball"); +		dev_err(&indio_dev->dev, +			"device not playing ball %d %d\n", valtest, val);  		ret = -EINVAL;  		goto err_ret;  	}  	val = LIS3L02DQ_DEFAULT_CTRL2; -	ret = lis3l02dq_spi_write_reg_8(&st->help.indio_dev->dev, +	ret = lis3l02dq_spi_write_reg_8(indio_dev,  					LIS3L02DQ_REG_CTRL_2_ADDR, -					&val); +					val);  	if (ret) {  		dev_err(&st->us->dev, "problem with setup control register 2");  		goto err_ret;  	}  	val = LIS3L02DQ_REG_WAKE_UP_CFG_LATCH_SRC; -	ret = lis3l02dq_spi_write_reg_8(&st->help.indio_dev->dev, +	ret = lis3l02dq_spi_write_reg_8(indio_dev,  					LIS3L02DQ_REG_WAKE_UP_CFG_ADDR, -					&val); +					val);  	if (ret)  		dev_err(&st->us->dev, "problem with interrupt cfg register");  err_ret: @@ -469,309 +435,237 @@ err_ret:  	return ret;  } -#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_raw_mag_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 - * driver where it is the drivers problem to manage this? - */ - -static IIO_DEV_ATTR_ACCEL_X(lis3l02dq_read_accel, -			    LIS3L02DQ_REG_OUT_X_L_ADDR); - -static IIO_DEV_ATTR_ACCEL_Y(lis3l02dq_read_accel, -			    LIS3L02DQ_REG_OUT_Y_L_ADDR); - -static IIO_DEV_ATTR_ACCEL_Z(lis3l02dq_read_accel, -			    LIS3L02DQ_REG_OUT_Z_L_ADDR); -  static IIO_DEV_ATTR_SAMP_FREQ(S_IWUSR | S_IRUGO,  			      lis3l02dq_read_frequency,  			      lis3l02dq_write_frequency);  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, -					       char *buf) -{ -	int ret; -	s8 val; -	struct iio_event_attr *this_attr = to_iio_event_attr(attr); - -	ret = lis3l02dq_spi_read_reg_8(dev->parent, -				       LIS3L02DQ_REG_WAKE_UP_CFG_ADDR, -				       (u8 *)&val); - -	return ret ? ret : sprintf(buf, "%d\n", !!(val & this_attr->mask)); -} - -static ssize_t lis3l02dq_write_interrupt_config(struct device *dev, -						struct device_attribute *attr, -						const char *buf, -						size_t len) -{ -	struct iio_event_attr *this_attr = to_iio_event_attr(attr); -	struct iio_dev *indio_dev = dev_get_drvdata(dev); -	int ret, currentlyset, changed = 0; -	u8 valold, controlold; -	bool val; - -	val = !(buf[0] == '0'); - -	mutex_lock(&indio_dev->mlock); -	/* read current value */ -	ret = lis3l02dq_spi_read_reg_8(dev->parent, -				       LIS3L02DQ_REG_WAKE_UP_CFG_ADDR, -				       &valold); -	if (ret) -		goto error_mutex_unlock; - -	/* read current control */ -	ret = lis3l02dq_spi_read_reg_8(dev, -				       LIS3L02DQ_REG_CTRL_2_ADDR, -				       &controlold); -	if (ret) -		goto error_mutex_unlock; -	currentlyset = !!(valold & this_attr->mask); -	if (val == false && currentlyset) { -		valold &= ~this_attr->mask; -		changed = 1; -		iio_remove_event_from_list(this_attr->listel, -						 &indio_dev->interrupts[0] -						 ->ev_list); -	} else if (val == true && !currentlyset) { -		changed = 1; -		valold |= this_attr->mask; -		iio_add_event_to_list(this_attr->listel, -					    &indio_dev->interrupts[0]->ev_list); -	} - -	if (changed) { -		ret = lis3l02dq_spi_write_reg_8(dev, -						LIS3L02DQ_REG_WAKE_UP_CFG_ADDR, -						&valold); -		if (ret) -			goto error_mutex_unlock; -		/* This always enables the interrupt, even if we've remove the -		 * last thing using it. For this device we can use the reference -		 * count on the handler to tell us if anyone wants the interrupt -		 */ -		controlold = this_attr->listel->refcount ? -			(controlold | LIS3L02DQ_REG_CTRL_2_ENABLE_INTERRUPT) : -			(controlold & ~LIS3L02DQ_REG_CTRL_2_ENABLE_INTERRUPT); -		ret = lis3l02dq_spi_write_reg_8(dev, -						LIS3L02DQ_REG_CTRL_2_ADDR, -						&controlold); -		if (ret) -			goto error_mutex_unlock; -	} -error_mutex_unlock: -	mutex_unlock(&indio_dev->mlock); - -	return ret ? ret : len; -} - - -static int lis3l02dq_thresh_handler_th(struct iio_dev *indio_dev, -				       int index, -				       s64 timestamp, -				       int no_test) +static irqreturn_t lis3l02dq_event_handler(int irq, void *private)  { -	struct iio_sw_ring_helper_state *h -		= iio_dev_get_devdata(indio_dev); -	struct lis3l02dq_state *st = lis3l02dq_h_to_s(h); - -	/* Stash the timestamp somewhere convenient for the bh */ -	st->thresh_timestamp = timestamp; -	schedule_work(&st->work_thresh); - -	return 0; -} - - -/* Unforunately it appears the interrupt won't clear unless you read from the - * src register. - */ -static void lis3l02dq_thresh_handler_bh_no_check(struct work_struct *work_s) -{ -       struct lis3l02dq_state *st -	       = container_of(work_s, -		       struct lis3l02dq_state, work_thresh); - +	struct iio_dev *indio_dev = private;  	u8 t; -	lis3l02dq_spi_read_reg_8(&st->help.indio_dev->dev, +	s64 timestamp = iio_get_time_ns(); + +	lis3l02dq_spi_read_reg_8(indio_dev,  				 LIS3L02DQ_REG_WAKE_UP_SRC_ADDR,  				 &t);  	if (t & LIS3L02DQ_REG_WAKE_UP_SRC_INTERRUPT_Z_HIGH) -		iio_push_event(st->help.indio_dev, 0, -			       IIO_MOD_EVENT_CODE(IIO_EV_CLASS_ACCEL, +		iio_push_event(indio_dev, +			       IIO_MOD_EVENT_CODE(IIO_ACCEL,  						  0, -						  IIO_EV_MOD_Z, +						  IIO_MOD_Z,  						  IIO_EV_TYPE_THRESH,  						  IIO_EV_DIR_RISING), -			       st->thresh_timestamp); +			       timestamp);  	if (t & LIS3L02DQ_REG_WAKE_UP_SRC_INTERRUPT_Z_LOW) -		iio_push_event(st->help.indio_dev, 0, -			       IIO_MOD_EVENT_CODE(IIO_EV_CLASS_ACCEL, +		iio_push_event(indio_dev, +			       IIO_MOD_EVENT_CODE(IIO_ACCEL,  						  0, -						  IIO_EV_MOD_Z, +						  IIO_MOD_Z,  						  IIO_EV_TYPE_THRESH,  						  IIO_EV_DIR_FALLING), -			       st->thresh_timestamp); +			       timestamp);  	if (t & LIS3L02DQ_REG_WAKE_UP_SRC_INTERRUPT_Y_HIGH) -		iio_push_event(st->help.indio_dev, 0, -			       IIO_MOD_EVENT_CODE(IIO_EV_CLASS_ACCEL, +		iio_push_event(indio_dev, +			       IIO_MOD_EVENT_CODE(IIO_ACCEL,  						  0, -						  IIO_EV_MOD_Y, +						  IIO_MOD_Y,  						  IIO_EV_TYPE_THRESH,  						  IIO_EV_DIR_RISING), -			       st->thresh_timestamp); +			       timestamp);  	if (t & LIS3L02DQ_REG_WAKE_UP_SRC_INTERRUPT_Y_LOW) -		iio_push_event(st->help.indio_dev, 0, -			       IIO_MOD_EVENT_CODE(IIO_EV_CLASS_ACCEL, +		iio_push_event(indio_dev, +			       IIO_MOD_EVENT_CODE(IIO_ACCEL,  						  0, -						  IIO_EV_MOD_Y, +						  IIO_MOD_Y,  						  IIO_EV_TYPE_THRESH,  						  IIO_EV_DIR_FALLING), -			       st->thresh_timestamp); +			       timestamp);  	if (t & LIS3L02DQ_REG_WAKE_UP_SRC_INTERRUPT_X_HIGH) -		iio_push_event(st->help.indio_dev, 0, -			       IIO_MOD_EVENT_CODE(IIO_EV_CLASS_ACCEL, +		iio_push_event(indio_dev, +			       IIO_MOD_EVENT_CODE(IIO_ACCEL,  						  0, -						  IIO_EV_MOD_X, +						  IIO_MOD_X,  						  IIO_EV_TYPE_THRESH,  						  IIO_EV_DIR_RISING), -			       st->thresh_timestamp); +			       timestamp);  	if (t & LIS3L02DQ_REG_WAKE_UP_SRC_INTERRUPT_X_LOW) -		iio_push_event(st->help.indio_dev, 0, -			       IIO_MOD_EVENT_CODE(IIO_EV_CLASS_ACCEL, +		iio_push_event(indio_dev, +			       IIO_MOD_EVENT_CODE(IIO_ACCEL,  						  0, -						  IIO_EV_MOD_X, +						  IIO_MOD_X,  						  IIO_EV_TYPE_THRESH,  						  IIO_EV_DIR_FALLING), -			       st->thresh_timestamp); -	/* reenable the irq */ -	enable_irq(st->us->irq); +			       timestamp); +  	/* Ack and allow for new interrupts */ -	lis3l02dq_spi_read_reg_8(&st->help.indio_dev->dev, +	lis3l02dq_spi_read_reg_8(indio_dev,  				 LIS3L02DQ_REG_WAKE_UP_ACK_ADDR,  				 &t); -	return; +	return IRQ_HANDLED;  } -/* A shared handler for a number of threshold types */ -IIO_EVENT_SH(threshold, &lis3l02dq_thresh_handler_th); - -IIO_EVENT_ATTR_SH(accel_x_thresh_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_thresh_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_thresh_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_thresh_falling_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_thresh_falling_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_thresh_falling_en, -		  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_thresh_rising_en.dev_attr.attr, -	&iio_event_attr_accel_y_thresh_rising_en.dev_attr.attr, -	&iio_event_attr_accel_z_thresh_rising_en.dev_attr.attr, -	&iio_event_attr_accel_x_thresh_falling_en.dev_attr.attr, -	&iio_event_attr_accel_y_thresh_falling_en.dev_attr.attr, -	&iio_event_attr_accel_z_thresh_falling_en.dev_attr.attr, -	&iio_dev_attr_accel_raw_mag_value.dev_attr.attr, -	NULL +static const struct iio_event_spec lis3l02dq_event[] = { +	{ +		.type = IIO_EV_TYPE_THRESH, +		.dir = IIO_EV_DIR_RISING, +		.mask_separate = BIT(IIO_EV_INFO_ENABLE), +		.mask_shared_by_type = BIT(IIO_EV_INFO_VALUE), +	}, { +		.type = IIO_EV_TYPE_THRESH, +		.dir = IIO_EV_DIR_FALLING, +		.mask_separate = BIT(IIO_EV_INFO_ENABLE), +		.mask_shared_by_type = BIT(IIO_EV_INFO_VALUE), +	}  }; -static struct attribute_group lis3l02dq_event_attribute_group = { -	.attrs = lis3l02dq_event_attributes, +#define LIS3L02DQ_CHAN(index, mod)				\ +	{							\ +		.type = IIO_ACCEL,				\ +		.modified = 1,					\ +		.channel2 = mod,				\ +		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |	\ +			BIT(IIO_CHAN_INFO_CALIBSCALE) |		\ +			BIT(IIO_CHAN_INFO_CALIBBIAS),		\ +		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ +		.address = index,				\ +		.scan_index = index,				\ +		.scan_type = {					\ +			.sign = 's',				\ +			.realbits = 12,				\ +			.storagebits = 16,			\ +		},						\ +		.event_spec = lis3l02dq_event,			\ +		.num_event_specs = ARRAY_SIZE(lis3l02dq_event),	\ +	 } + +static const struct iio_chan_spec lis3l02dq_channels[] = { +	LIS3L02DQ_CHAN(0, IIO_MOD_X), +	LIS3L02DQ_CHAN(1, IIO_MOD_Y), +	LIS3L02DQ_CHAN(2, IIO_MOD_Z), +	IIO_CHAN_SOFT_TIMESTAMP(3)  }; -static IIO_CONST_ATTR_NAME("lis3l02dq"); -static IIO_CONST_ATTR(accel_scale, "0.00958"); + +static int lis3l02dq_read_event_config(struct iio_dev *indio_dev, +				       const struct iio_chan_spec *chan, +				       enum iio_event_type type, +				       enum iio_event_direction dir) +{ + +	u8 val; +	int ret; +	u8 mask = (1 << (chan->channel2*2 + (dir == IIO_EV_DIR_RISING))); +	ret = lis3l02dq_spi_read_reg_8(indio_dev, +				       LIS3L02DQ_REG_WAKE_UP_CFG_ADDR, +				       &val); +	if (ret < 0) +		return ret; + +	return !!(val & mask); +} + +int lis3l02dq_disable_all_events(struct iio_dev *indio_dev) +{ +	int ret; +	u8 control, val; + +	ret = lis3l02dq_spi_read_reg_8(indio_dev, +				       LIS3L02DQ_REG_CTRL_2_ADDR, +				       &control); + +	control &= ~LIS3L02DQ_REG_CTRL_2_ENABLE_INTERRUPT; +	ret = lis3l02dq_spi_write_reg_8(indio_dev, +					LIS3L02DQ_REG_CTRL_2_ADDR, +					control); +	if (ret) +		goto error_ret; +	/* Also for consistency clear the mask */ +	ret = lis3l02dq_spi_read_reg_8(indio_dev, +				       LIS3L02DQ_REG_WAKE_UP_CFG_ADDR, +				       &val); +	if (ret) +		goto error_ret; +	val &= ~0x3f; + +	ret = lis3l02dq_spi_write_reg_8(indio_dev, +					LIS3L02DQ_REG_WAKE_UP_CFG_ADDR, +					val); +	if (ret) +		goto error_ret; + +	ret = control; +error_ret: +	return ret; +} + +static int lis3l02dq_write_event_config(struct iio_dev *indio_dev, +					const struct iio_chan_spec *chan, +					enum iio_event_type type, +					enum iio_event_direction dir, +					int state) +{ +	int ret = 0; +	u8 val, control; +	u8 currentlyset; +	bool changed = false; +	u8 mask = (1 << (chan->channel2*2 + (dir == IIO_EV_DIR_RISING))); + +	mutex_lock(&indio_dev->mlock); +	/* read current control */ +	ret = lis3l02dq_spi_read_reg_8(indio_dev, +				       LIS3L02DQ_REG_CTRL_2_ADDR, +				       &control); +	if (ret) +		goto error_ret; +	ret = lis3l02dq_spi_read_reg_8(indio_dev, +				       LIS3L02DQ_REG_WAKE_UP_CFG_ADDR, +				       &val); +	if (ret < 0) +		goto error_ret; +	currentlyset = val & mask; + +	if (!currentlyset && state) { +		changed = true; +		val |= mask; +	} else if (currentlyset && !state) { +		changed = true; +		val &= ~mask; +	} + +	if (changed) { +		ret = lis3l02dq_spi_write_reg_8(indio_dev, +						LIS3L02DQ_REG_WAKE_UP_CFG_ADDR, +						val); +		if (ret) +			goto error_ret; +		control = val & 0x3f ? +			(control | LIS3L02DQ_REG_CTRL_2_ENABLE_INTERRUPT) : +			(control & ~LIS3L02DQ_REG_CTRL_2_ENABLE_INTERRUPT); +		ret = lis3l02dq_spi_write_reg_8(indio_dev, +					       LIS3L02DQ_REG_CTRL_2_ADDR, +					       control); +		if (ret) +			goto error_ret; +	} + +error_ret: +	mutex_unlock(&indio_dev->mlock); +	return ret; +}  static struct attribute *lis3l02dq_attributes[] = { -	&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_sampling_frequency_available.dev_attr.attr, -	&iio_const_attr_name.dev_attr.attr,  	NULL  }; @@ -779,104 +673,89 @@ static const struct attribute_group lis3l02dq_attribute_group = {  	.attrs = lis3l02dq_attributes,  }; -static int __devinit lis3l02dq_probe(struct spi_device *spi) +static const struct iio_info lis3l02dq_info = { +	.read_raw = &lis3l02dq_read_raw, +	.write_raw = &lis3l02dq_write_raw, +	.read_event_value = &lis3l02dq_read_thresh, +	.write_event_value = &lis3l02dq_write_thresh, +	.write_event_config = &lis3l02dq_write_event_config, +	.read_event_config = &lis3l02dq_read_event_config, +	.driver_module = THIS_MODULE, +	.attrs = &lis3l02dq_attribute_group, +}; + +static int lis3l02dq_probe(struct spi_device *spi)  { -	int ret, regdone = 0; -	struct lis3l02dq_state *st = kzalloc(sizeof *st, GFP_KERNEL); -	if (!st) { -		ret =  -ENOMEM; -		goto error_ret; -	} -	INIT_WORK(&st->work_thresh, lis3l02dq_thresh_handler_bh_no_check); -	/* this is only used tor removal purposes */ -	spi_set_drvdata(spi, st); - -	/* Allocate the comms buffers */ -	st->rx = kzalloc(sizeof(*st->rx)*LIS3L02DQ_MAX_RX, GFP_KERNEL); -	if (st->rx == NULL) { -		ret = -ENOMEM; -		goto error_free_st; -	} -	st->tx = kzalloc(sizeof(*st->tx)*LIS3L02DQ_MAX_TX, GFP_KERNEL); -	if (st->tx == NULL) { -		ret = -ENOMEM; -		goto error_free_rx; -	} +	int ret; +	struct lis3l02dq_state *st; +	struct iio_dev *indio_dev; + +	indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); +	if (!indio_dev) +		return -ENOMEM; +	st = iio_priv(indio_dev); +	/* this is only used for removal purposes */ +	spi_set_drvdata(spi, indio_dev); +  	st->us = spi; +	st->gpio = of_get_gpio(spi->dev.of_node, 0);  	mutex_init(&st->buf_lock); -	/* setup the industrialio driver allocated elements */ -	st->help.indio_dev = iio_allocate_device(); -	if (st->help.indio_dev == NULL) { -		ret = -ENOMEM; -		goto error_free_tx; -	} +	indio_dev->name = spi->dev.driver->name; +	indio_dev->dev.parent = &spi->dev; +	indio_dev->info = &lis3l02dq_info; +	indio_dev->channels = lis3l02dq_channels; +	indio_dev->num_channels = ARRAY_SIZE(lis3l02dq_channels); -	st->help.indio_dev->dev.parent = &spi->dev; -	st->help.indio_dev->num_interrupt_lines = 1; -	st->help.indio_dev->event_attrs = &lis3l02dq_event_attribute_group; -	st->help.indio_dev->attrs = &lis3l02dq_attribute_group; -	st->help.indio_dev->dev_data = (void *)(&st->help); -	st->help.indio_dev->driver_module = THIS_MODULE; -	st->help.indio_dev->modes = INDIO_DIRECT_MODE; +	indio_dev->modes = INDIO_DIRECT_MODE; -	ret = lis3l02dq_configure_ring(st->help.indio_dev); +	ret = lis3l02dq_configure_buffer(indio_dev);  	if (ret) -		goto error_free_dev; - -	ret = iio_device_register(st->help.indio_dev); -	if (ret) -		goto error_unreg_ring_funcs; -	regdone = 1; +		return ret; -	ret = iio_ring_buffer_register(st->help.indio_dev->ring, 0); +	ret = iio_buffer_register(indio_dev, +				  lis3l02dq_channels, +				  ARRAY_SIZE(lis3l02dq_channels));  	if (ret) { -		printk(KERN_ERR "failed to initialize the ring\n"); -		goto error_unreg_ring_funcs; +		dev_err(&spi->dev, "failed to initialize the buffer\n"); +		goto error_unreg_buffer_funcs;  	} -	if (spi->irq && gpio_is_valid(irq_to_gpio(spi->irq)) > 0) { -		st->inter = 0; -		ret = iio_register_interrupt_line(spi->irq, -						  st->help.indio_dev, -						  0, -						  IRQF_TRIGGER_RISING, -						  "lis3l02dq"); +	if (spi->irq) { +		ret = request_threaded_irq(st->us->irq, +					   &lis3l02dq_th, +					   &lis3l02dq_event_handler, +					   IRQF_TRIGGER_RISING, +					   "lis3l02dq", +					   indio_dev);  		if (ret) -			goto error_uninitialize_ring; +			goto error_uninitialize_buffer; -		ret = lis3l02dq_probe_trigger(st->help.indio_dev); +		ret = lis3l02dq_probe_trigger(indio_dev);  		if (ret) -			goto error_unregister_line; +			goto error_free_interrupt;  	}  	/* Get the device into a sane initial state */ -	ret = lis3l02dq_initial_setup(st); +	ret = lis3l02dq_initial_setup(indio_dev); +	if (ret) +		goto error_remove_trigger; + +	ret = iio_device_register(indio_dev);  	if (ret)  		goto error_remove_trigger; +  	return 0;  error_remove_trigger: -	if (st->help.indio_dev->modes & INDIO_RING_TRIGGERED) -		lis3l02dq_remove_trigger(st->help.indio_dev); -error_unregister_line: -	if (st->help.indio_dev->modes & INDIO_RING_TRIGGERED) -		iio_unregister_interrupt_line(st->help.indio_dev, 0); -error_uninitialize_ring: -	iio_ring_buffer_unregister(st->help.indio_dev->ring); -error_unreg_ring_funcs: -	lis3l02dq_unconfigure_ring(st->help.indio_dev); -error_free_dev: -	if (regdone) -		iio_device_unregister(st->help.indio_dev); -	else -		iio_free_device(st->help.indio_dev); -error_free_tx: -	kfree(st->tx); -error_free_rx: -	kfree(st->rx); -error_free_st: -	kfree(st); -error_ret: +	if (spi->irq) +		lis3l02dq_remove_trigger(indio_dev); +error_free_interrupt: +	if (spi->irq) +		free_irq(st->us->irq, indio_dev); +error_uninitialize_buffer: +	iio_buffer_unregister(indio_dev); +error_unreg_buffer_funcs: +	lis3l02dq_unconfigure_buffer(indio_dev);  	return ret;  } @@ -884,23 +763,21 @@ error_ret:  static int lis3l02dq_stop_device(struct iio_dev *indio_dev)  {  	int ret; -	struct iio_sw_ring_helper_state *h -		= iio_dev_get_devdata(indio_dev); -	struct lis3l02dq_state *st = lis3l02dq_h_to_s(h); +	struct lis3l02dq_state *st = iio_priv(indio_dev);  	u8 val = 0;  	mutex_lock(&indio_dev->mlock); -	ret = lis3l02dq_spi_write_reg_8(&indio_dev->dev, +	ret = lis3l02dq_spi_write_reg_8(indio_dev,  					LIS3L02DQ_REG_CTRL_1_ADDR, -					&val); +					val);  	if (ret) {  		dev_err(&st->us->dev, "problem with turning device off: ctrl1");  		goto err_ret;  	} -	ret = lis3l02dq_spi_write_reg_8(&indio_dev->dev, +	ret = lis3l02dq_spi_write_reg_8(indio_dev,  					LIS3L02DQ_REG_CTRL_2_ADDR, -					&val); +					val);  	if (ret)  		dev_err(&st->us->dev, "problem with turning device off: ctrl2");  err_ret: @@ -911,31 +788,22 @@ err_ret:  /* fixme, confirm ordering in this function */  static int lis3l02dq_remove(struct spi_device *spi)  { -	int ret; -	struct lis3l02dq_state *st = spi_get_drvdata(spi); -	struct iio_dev *indio_dev = st->help.indio_dev; +	struct iio_dev *indio_dev = spi_get_drvdata(spi); +	struct lis3l02dq_state *st = iio_priv(indio_dev); -	ret = lis3l02dq_stop_device(indio_dev); -	if (ret) -		goto err_ret; +	iio_device_unregister(indio_dev); -	flush_scheduled_work(); +	lis3l02dq_disable_all_events(indio_dev); +	lis3l02dq_stop_device(indio_dev); -	lis3l02dq_remove_trigger(indio_dev); -	if (spi->irq && gpio_is_valid(irq_to_gpio(spi->irq)) > 0) -		iio_unregister_interrupt_line(indio_dev, 0); +	if (spi->irq) +		free_irq(st->us->irq, indio_dev); -	iio_ring_buffer_unregister(indio_dev->ring); -	lis3l02dq_unconfigure_ring(indio_dev); -	iio_device_unregister(indio_dev); -	kfree(st->tx); -	kfree(st->rx); -	kfree(st); +	lis3l02dq_remove_trigger(indio_dev); +	iio_buffer_unregister(indio_dev); +	lis3l02dq_unconfigure_buffer(indio_dev);  	return 0; - -err_ret: -	return ret;  }  static struct spi_driver lis3l02dq_driver = { @@ -944,21 +812,11 @@ static struct spi_driver lis3l02dq_driver = {  		.owner = THIS_MODULE,  	},  	.probe = lis3l02dq_probe, -	.remove = __devexit_p(lis3l02dq_remove), +	.remove = lis3l02dq_remove,  }; +module_spi_driver(lis3l02dq_driver); -static __init int lis3l02dq_init(void) -{ -	return spi_register_driver(&lis3l02dq_driver); -} -module_init(lis3l02dq_init); - -static __exit void lis3l02dq_exit(void) -{ -	spi_unregister_driver(&lis3l02dq_driver); -} -module_exit(lis3l02dq_exit); - -MODULE_AUTHOR("Jonathan Cameron <jic23@cam.ac.uk>"); +MODULE_AUTHOR("Jonathan Cameron <jic23@kernel.org>");  MODULE_DESCRIPTION("ST LIS3L02DQ Accelerometer SPI driver");  MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("spi:lis3l02dq"); diff --git a/drivers/staging/iio/accel/lis3l02dq_ring.c b/drivers/staging/iio/accel/lis3l02dq_ring.c index 330d5d6dbba..79cefe0a516 100644 --- a/drivers/staging/iio/accel/lis3l02dq_ring.c +++ b/drivers/staging/iio/accel/lis3l02dq_ring.c @@ -1,24 +1,19 @@  #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 <linux/slab.h> +#include <linux/export.h> -#include "../iio.h" -#include "../sysfs.h" -#include "../ring_sw.h" -#include "accel.h" -#include "../trigger.h" +#include <linux/iio/iio.h> +#include <linux/iio/kfifo_buf.h> +#include <linux/iio/trigger.h> +#include <linux/iio/trigger_consumer.h>  #include "lis3l02dq.h"  /** - * combine_8_to_16() utility function to munge to u8s into u16 + * combine_8_to_16() utility function to munge two u8s into u16   **/  static inline u16 combine_8_to_16(u8 lower, u8 upper)  { @@ -28,180 +23,18 @@ static inline u16 combine_8_to_16(u8 lower, u8 upper)  }  /** - * lis3l02dq_scan_el_set_state() set whether a scan contains a given channel - * @scan_el:	associtate iio scan element attribute - * @indio_dev:	the device structure - * @bool:	desired state - * - * mlock already held when this is called. - **/ -static int lis3l02dq_scan_el_set_state(struct iio_scan_el *scan_el, -				       struct iio_dev *indio_dev, -				       bool state) -{ -	u8 t, mask; -	int ret; - -	ret = lis3l02dq_spi_read_reg_8(&indio_dev->dev, -				       LIS3L02DQ_REG_CTRL_1_ADDR, -				       &t); -	if (ret) -		goto error_ret; -	switch (scan_el->label) { -	case LIS3L02DQ_REG_OUT_X_L_ADDR: -		mask = LIS3L02DQ_REG_CTRL_1_AXES_X_ENABLE; -		break; -	case LIS3L02DQ_REG_OUT_Y_L_ADDR: -		mask = LIS3L02DQ_REG_CTRL_1_AXES_Y_ENABLE; -		break; -	case LIS3L02DQ_REG_OUT_Z_L_ADDR: -		mask = LIS3L02DQ_REG_CTRL_1_AXES_Z_ENABLE; -		break; -	default: -		ret = -EINVAL; -		goto error_ret; -	} - -	if (!(mask & t) == state) { -		if (state) -			t |= mask; -		else -			t &= ~mask; -		ret = lis3l02dq_spi_write_reg_8(&indio_dev->dev, -						LIS3L02DQ_REG_CTRL_1_ADDR, -						&t); -	} -error_ret: -	return ret; - -} -static IIO_SCAN_EL_C(accel_x, 0, -		     LIS3L02DQ_REG_OUT_X_L_ADDR, -		     &lis3l02dq_scan_el_set_state); -static IIO_SCAN_EL_C(accel_y, 1, -		     LIS3L02DQ_REG_OUT_Y_L_ADDR, -		     &lis3l02dq_scan_el_set_state); -static IIO_SCAN_EL_C(accel_z, 2, -		     LIS3L02DQ_REG_OUT_Z_L_ADDR, -		     &lis3l02dq_scan_el_set_state); -static IIO_CONST_ATTR_SCAN_EL_TYPE(accel, s, 12, 16); -static IIO_SCAN_EL_TIMESTAMP(3); -static IIO_CONST_ATTR_SCAN_EL_TYPE(timestamp, s, 64, 64); - -static struct attribute *lis3l02dq_scan_el_attrs[] = { -	&iio_scan_el_accel_x.dev_attr.attr, -	&iio_const_attr_accel_x_index.dev_attr.attr, -	&iio_scan_el_accel_y.dev_attr.attr, -	&iio_const_attr_accel_y_index.dev_attr.attr, -	&iio_scan_el_accel_z.dev_attr.attr, -	&iio_const_attr_accel_z_index.dev_attr.attr, -	&iio_const_attr_accel_type.dev_attr.attr, -	&iio_scan_el_timestamp.dev_attr.attr, -	&iio_const_attr_timestamp_index.dev_attr.attr, -	&iio_const_attr_timestamp_type.dev_attr.attr, -	NULL, -}; - -static struct attribute_group lis3l02dq_scan_el_group = { -	.attrs = lis3l02dq_scan_el_attrs, -	.name = "scan_elements", -}; - -/** - * lis3l02dq_poll_func_th() top half interrupt handler called by trigger - * @private_data:	iio_dev - **/ -static void lis3l02dq_poll_func_th(struct iio_dev *indio_dev, s64 time) -{ -	struct iio_sw_ring_helper_state *h -		= iio_dev_get_devdata(indio_dev); -	struct lis3l02dq_state *st = lis3l02dq_h_to_s(h); -	/* in this case we need to slightly extend the helper function */ -	iio_sw_poll_func_th(indio_dev, time); - -	/* Indicate that this interrupt is being handled */ -	/* Technically this is trigger related, but without this -	 * handler running there is currently now way for the interrupt -	 * to clear. -	 */ -	st->inter = 1; -} - -/**   * lis3l02dq_data_rdy_trig_poll() the event handler for the data rdy trig   **/ -static int lis3l02dq_data_rdy_trig_poll(struct iio_dev *indio_dev, -				       int index, -				       s64 timestamp, -				       int no_test) +irqreturn_t lis3l02dq_data_rdy_trig_poll(int irq, void *private)  { -	struct iio_sw_ring_helper_state *h -		= iio_dev_get_devdata(indio_dev); -	struct lis3l02dq_state *st = lis3l02dq_h_to_s(h); - -	iio_trigger_poll(st->trig, timestamp); - -	return IRQ_HANDLED; -} - -/* This is an event as it is a response to a physical interrupt */ -IIO_EVENT_SH(data_rdy_trig, &lis3l02dq_data_rdy_trig_poll); - -/** - * lis3l02dq_read_accel_from_ring() individual acceleration read from ring - **/ -ssize_t lis3l02dq_read_accel_from_ring(struct device *dev, -				       struct device_attribute *attr, -				       char *buf) -{ -	struct iio_scan_el *el = NULL; -	int ret, len = 0, i = 0; -	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); -	struct iio_dev *dev_info = dev_get_drvdata(dev); -	struct iio_ring_buffer *ring = dev_info->ring; -	struct attribute_group *scan_el_attrs = ring->scan_el_attrs; -	s16 *data; - -	while (scan_el_attrs->attrs[i]) { -		el = to_iio_scan_el((struct device_attribute *) -				    (scan_el_attrs->attrs[i])); -		/* label is in fact the address */ -		if (el->label == this_attr->address) -			break; -		i++; -	} -	if (!scan_el_attrs->attrs[i]) { -		ret = -EINVAL; -		goto error_ret; -	} -	/* If this element is in the scan mask */ -	ret = iio_scan_mask_query(ring, el->number); -	if (ret < 0) -		goto error_ret; -	if (ret) { -		data = kmalloc(ring->access.get_bytes_per_datum(ring), -			       GFP_KERNEL); -		if (data == NULL) -			return -ENOMEM; -		ret = ring->access.read_last(ring, -					(u8 *)data); -		if (ret) -			goto error_free_data; -	} else { -		ret = -EINVAL; -		goto error_ret; -	} -	len = iio_scan_mask_count_to_right(ring, el->number); -	if (len < 0) { -		ret = len; -		goto error_free_data; -	} -	len = sprintf(buf, "ring %d\n", data[len]); -error_free_data: -	kfree(data); -error_ret: -	return ret ? ret : len; - +	struct iio_dev *indio_dev = private; +	struct lis3l02dq_state *st = iio_priv(indio_dev); + +	if (st->trigger_on) { +		iio_trigger_poll(st->trig, iio_get_time_ns()); +		return IRQ_HANDLED; +	} else +		return IRQ_WAKE_THREAD;  }  static const u8 read_all_tx_array[] = { @@ -215,26 +48,27 @@ static const u8 read_all_tx_array[] = {  /**   * lis3l02dq_read_all() Reads all channels currently selected - * @st:		device specific state - * @rx_array:	(dma capable) recieve array, must be at least + * @indio_dev:	IIO device state + * @rx_array:	(dma capable) receive array, must be at least   *		4*number of channels   **/ -static int lis3l02dq_read_all(struct lis3l02dq_state *st, u8 *rx_array) +static int lis3l02dq_read_all(struct iio_dev *indio_dev, u8 *rx_array)  { -	struct iio_ring_buffer *ring = st->help.indio_dev->ring; +	struct lis3l02dq_state *st = iio_priv(indio_dev);  	struct spi_transfer *xfers;  	struct spi_message msg;  	int ret, i, j = 0; -	xfers = kzalloc((ring->scan_count) * 2 -			* sizeof(*xfers), GFP_KERNEL); +	xfers = kcalloc(bitmap_weight(indio_dev->active_scan_mask, +				      indio_dev->masklength) * 2, +			sizeof(*xfers), GFP_KERNEL);  	if (!xfers)  		return -ENOMEM;  	mutex_lock(&st->buf_lock); -	for (i = 0; i < ARRAY_SIZE(read_all_tx_array)/4; i++) { -		if (ring->scan_mask & (1 << i)) { +	for (i = 0; i < ARRAY_SIZE(read_all_tx_array)/4; i++) +		if (test_bit(i, indio_dev->active_scan_mask)) {  			/* lower byte */  			xfers[j].tx_buf = st->tx + 2*j;  			st->tx[2*j] = read_all_tx_array[i*4]; @@ -257,12 +91,13 @@ static int lis3l02dq_read_all(struct lis3l02dq_state *st, u8 *rx_array)  			xfers[j].cs_change = 1;  			j++;  		} -	} +  	/* After these are transmitted, the rx_buff should have  	 * values in alternate bytes  	 */  	spi_message_init(&msg); -	for (j = 0; j < ring->scan_count * 2; j++) +	for (j = 0; j < bitmap_weight(indio_dev->active_scan_mask, +				      indio_dev->masklength) * 2; j++)  		spi_message_add_tail(&xfers[j], &msg);  	ret = spi_sync(st->us, &msg); @@ -272,31 +107,24 @@ static int lis3l02dq_read_all(struct lis3l02dq_state *st, u8 *rx_array)  	return ret;  } -static void lis3l02dq_trigger_bh_to_ring(struct work_struct *work_s) -{ -	struct iio_sw_ring_helper_state *h -		= container_of(work_s, struct iio_sw_ring_helper_state, -			work_trigger_to_ring); -	struct lis3l02dq_state *st = lis3l02dq_h_to_s(h); - -	st->inter = 0; -	iio_sw_trigger_bh_to_ring(work_s); -} - -static int lis3l02dq_get_ring_element(struct iio_sw_ring_helper_state *h, +static int lis3l02dq_get_buffer_element(struct iio_dev *indio_dev,  				u8 *buf)  {  	int ret, i; -	u8 *rx_array ; +	u8 *rx_array;  	s16 *data = (s16 *)buf; +	int scan_count = bitmap_weight(indio_dev->active_scan_mask, +				       indio_dev->masklength); -	rx_array = kzalloc(4 * (h->indio_dev->ring->scan_count), GFP_KERNEL); +	rx_array = kzalloc(4 * scan_count, GFP_KERNEL);  	if (rx_array == NULL)  		return -ENOMEM; -	ret = lis3l02dq_read_all(lis3l02dq_h_to_s(h), rx_array); -	if (ret < 0) +	ret = lis3l02dq_read_all(indio_dev, rx_array); +	if (ret < 0) { +		kfree(rx_array);  		return ret; -	for (i = 0; i < h->indio_dev->ring->scan_count; i++) +	} +	for (i = 0; i < scan_count; i++)  		data[i] = combine_8_to_16(rx_array[i*4+1],  					rx_array[i*4+3]);  	kfree(rx_array); @@ -304,55 +132,79 @@ static int lis3l02dq_get_ring_element(struct iio_sw_ring_helper_state *h,  	return i*sizeof(data[0]);  } +static irqreturn_t lis3l02dq_trigger_handler(int irq, void *p) +{ +	struct iio_poll_func *pf = p; +	struct iio_dev *indio_dev = pf->indio_dev; +	int len = 0; +	char *data; + +	data = kmalloc(indio_dev->scan_bytes, GFP_KERNEL); +	if (data == NULL) +		goto done; + +	if (!bitmap_empty(indio_dev->active_scan_mask, indio_dev->masklength)) +		len = lis3l02dq_get_buffer_element(indio_dev, data); + +	iio_push_to_buffers_with_timestamp(indio_dev, data, pf->timestamp); + +	kfree(data); +done: +	iio_trigger_notify_done(indio_dev->trig); +	return IRQ_HANDLED; +} +  /* Caller responsible for locking as necessary. */  static int -__lis3l02dq_write_data_ready_config(struct device *dev, -				    struct iio_event_handler_list *list, -				    bool state) +__lis3l02dq_write_data_ready_config(struct iio_dev *indio_dev, bool state)  {  	int ret;  	u8 valold;  	bool currentlyset; -	struct iio_dev *indio_dev = dev_get_drvdata(dev); +	struct lis3l02dq_state *st = iio_priv(indio_dev); -/* Get the current event mask register */ -	ret = lis3l02dq_spi_read_reg_8(dev, +	/* Get the current event mask register */ +	ret = lis3l02dq_spi_read_reg_8(indio_dev,  				       LIS3L02DQ_REG_CTRL_2_ADDR,  				       &valold);  	if (ret)  		goto error_ret; -/* Find out if data ready is already on */ +	/* Find out if data ready is already on */  	currentlyset  		= valold & LIS3L02DQ_REG_CTRL_2_ENABLE_DATA_READY_GENERATION; -/* Disable requested */ +	/* Disable requested */  	if (!state && currentlyset) { - +		/* Disable the data ready signal */  		valold &= ~LIS3L02DQ_REG_CTRL_2_ENABLE_DATA_READY_GENERATION; -		/* The double write is to overcome a hardware bug?*/ -		ret = lis3l02dq_spi_write_reg_8(dev, + +		/* The double write is to overcome a hardware bug? */ +		ret = lis3l02dq_spi_write_reg_8(indio_dev,  						LIS3L02DQ_REG_CTRL_2_ADDR, -						&valold); +						valold);  		if (ret)  			goto error_ret; -		ret = lis3l02dq_spi_write_reg_8(dev, +		ret = lis3l02dq_spi_write_reg_8(indio_dev,  						LIS3L02DQ_REG_CTRL_2_ADDR, -						&valold); +						valold);  		if (ret)  			goto error_ret; +		st->trigger_on = false; +	/* Enable requested */ +	} else if (state && !currentlyset) { +		/* If not set, enable requested +		 * first disable all events */ +		ret = lis3l02dq_disable_all_events(indio_dev); +		if (ret < 0) +			goto error_ret; -		iio_remove_event_from_list(list, -					   &indio_dev->interrupts[0] -					   ->ev_list); +		valold = ret | +			LIS3L02DQ_REG_CTRL_2_ENABLE_DATA_READY_GENERATION; -/* Enable requested */ -	} else if (state && !currentlyset) { -		/* if not set, enable requested */ -		valold |= LIS3L02DQ_REG_CTRL_2_ENABLE_DATA_READY_GENERATION; -		iio_add_event_to_list(list, &indio_dev->interrupts[0]->ev_list); -		ret = lis3l02dq_spi_write_reg_8(dev, +		st->trigger_on = true; +		ret = lis3l02dq_spi_write_reg_8(indio_dev,  						LIS3L02DQ_REG_CTRL_2_ADDR, -						&valold); +						valold);  		if (ret)  			goto error_ret;  	} @@ -367,161 +219,204 @@ error_ret:   *   * If disabling the interrupt also does a final read to ensure it is clear.   * This is only important in some cases where the scan enable elements are - * switched before the ring is reenabled. + * switched before the buffer is reenabled.   **/  static int lis3l02dq_data_rdy_trigger_set_state(struct iio_trigger *trig,  						bool state)  { -	struct lis3l02dq_state *st = trig->private_data; +	struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig);  	int ret = 0;  	u8 t; -	__lis3l02dq_write_data_ready_config(&st->help.indio_dev->dev, -					    &iio_event_data_rdy_trig, -					    state); -	if (state == false) { -		/* possible quirk with handler currently worked around -		   by ensuring the work queue is empty */ -		flush_scheduled_work(); -		/* Clear any outstanding ready events */ -		ret = lis3l02dq_read_all(st, NULL); + +	__lis3l02dq_write_data_ready_config(indio_dev, state); +	if (!state) { +		/* +		 * A possible quirk with the handler is currently worked around +		 * by ensuring outstanding read events are cleared. +		 */ +		ret = lis3l02dq_read_all(indio_dev, NULL);  	} -	lis3l02dq_spi_read_reg_8(&st->help.indio_dev->dev, +	lis3l02dq_spi_read_reg_8(indio_dev,  				 LIS3L02DQ_REG_WAKE_UP_SRC_ADDR,  				 &t);  	return ret;  } -static IIO_TRIGGER_NAME_ATTR; - -static struct attribute *lis3l02dq_trigger_attrs[] = { -	&dev_attr_name.attr, -	NULL, -}; - -static const struct attribute_group lis3l02dq_trigger_attr_group = { -	.attrs = lis3l02dq_trigger_attrs, -}; -  /** - * lis3l02dq_trig_try_reen() try renabling irq for data rdy trigger + * lis3l02dq_trig_try_reen() try reenabling irq for data rdy trigger   * @trig:	the datardy trigger - * - * As the trigger may occur on any data element being updated it is - * really rather likely to occur during the read from the previous - * trigger event.  The only way to discover if this has occured on - * boards not supporting level interrupts is to take a look at the line. - * If it is indicating another interrupt and we don't seem to have a - * handler looking at it, then we need to notify the core that we need - * to tell the triggering core to try reading all these again. - **/ + */  static int lis3l02dq_trig_try_reen(struct iio_trigger *trig)  { -	struct lis3l02dq_state *st = trig->private_data; -	enable_irq(st->us->irq); -	/* If gpio still high (or high again) */ -	if (gpio_get_value(irq_to_gpio(st->us->irq))) -		if (st->inter == 0) { -			/* already interrupt handler dealing with it */ -			disable_irq_nosync(st->us->irq); -			if (st->inter == 1) { -				/* interrupt handler snuck in between test -				 * and disable */ -				enable_irq(st->us->irq); -				return 0; -			} -			return -EAGAIN; -		} +	struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig); +	struct lis3l02dq_state *st = iio_priv(indio_dev); +	int i; + +	/* If gpio still high (or high again) +	 * In theory possible we will need to do this several times */ +	for (i = 0; i < 5; i++) +		if (gpio_get_value(st->gpio)) +			lis3l02dq_read_all(indio_dev, NULL); +		else +			break; +	if (i == 5) +		pr_info("Failed to clear the interrupt for lis3l02dq\n"); +  	/* irq reenabled so success! */  	return 0;  } +static const struct iio_trigger_ops lis3l02dq_trigger_ops = { +	.owner = THIS_MODULE, +	.set_trigger_state = &lis3l02dq_data_rdy_trigger_set_state, +	.try_reenable = &lis3l02dq_trig_try_reen, +}; +  int lis3l02dq_probe_trigger(struct iio_dev *indio_dev)  {  	int ret; -	struct lis3l02dq_state *state = indio_dev->dev_data; - -	state->trig = iio_allocate_trigger(); -	if (!state->trig) -		return -ENOMEM; +	struct lis3l02dq_state *st = iio_priv(indio_dev); -	state->trig->name = kasprintf(GFP_KERNEL, -				      "lis3l02dq-dev%d", -				      indio_dev->id); -	if (!state->trig->name) { +	st->trig = iio_trigger_alloc("lis3l02dq-dev%d", indio_dev->id); +	if (!st->trig) {  		ret = -ENOMEM; -		goto error_free_trig; +		goto error_ret;  	} -	state->trig->dev.parent = &state->us->dev; -	state->trig->owner = THIS_MODULE; -	state->trig->private_data = state; -	state->trig->set_trigger_state = &lis3l02dq_data_rdy_trigger_set_state; -	state->trig->try_reenable = &lis3l02dq_trig_try_reen; -	state->trig->control_attrs = &lis3l02dq_trigger_attr_group; -	ret = iio_trigger_register(state->trig); +	st->trig->dev.parent = &st->us->dev; +	st->trig->ops = &lis3l02dq_trigger_ops; +	iio_trigger_set_drvdata(st->trig, indio_dev); +	ret = iio_trigger_register(st->trig);  	if (ret) -		goto error_free_trig_name; +		goto error_free_trig;  	return 0; -error_free_trig_name: -	kfree(state->trig->name);  error_free_trig: -	iio_free_trigger(state->trig); - +	iio_trigger_free(st->trig); +error_ret:  	return ret;  }  void lis3l02dq_remove_trigger(struct iio_dev *indio_dev)  { -	struct lis3l02dq_state *state = indio_dev->dev_data; +	struct lis3l02dq_state *st = iio_priv(indio_dev); -	iio_trigger_unregister(state->trig); -	kfree(state->trig->name); -	iio_free_trigger(state->trig); +	iio_trigger_unregister(st->trig); +	iio_trigger_free(st->trig);  } -void lis3l02dq_unconfigure_ring(struct iio_dev *indio_dev) +void lis3l02dq_unconfigure_buffer(struct iio_dev *indio_dev)  { -	kfree(indio_dev->pollfunc); -	iio_sw_rb_free(indio_dev->ring); +	iio_dealloc_pollfunc(indio_dev->pollfunc); +	iio_kfifo_free(indio_dev->buffer);  } -int lis3l02dq_configure_ring(struct iio_dev *indio_dev) +static int lis3l02dq_buffer_postenable(struct iio_dev *indio_dev)  { +	/* Disable unwanted channels otherwise the interrupt will not clear */ +	u8 t;  	int ret; -	struct iio_sw_ring_helper_state *h = iio_dev_get_devdata(indio_dev); -	struct iio_ring_buffer *ring; -	INIT_WORK(&h->work_trigger_to_ring, lis3l02dq_trigger_bh_to_ring); -	h->get_ring_element = &lis3l02dq_get_ring_element; +	bool oneenabled = false; -	ring = iio_sw_rb_allocate(indio_dev); -	if (!ring) -		return -ENOMEM; +	ret = lis3l02dq_spi_read_reg_8(indio_dev, +				       LIS3L02DQ_REG_CTRL_1_ADDR, +				       &t); +	if (ret) +		goto error_ret; -	indio_dev->ring = ring; -	/* Effectively select the ring buffer implementation */ -	iio_ring_sw_register_funcs(&ring->access); -	ring->bpe = 2; -	ring->scan_el_attrs = &lis3l02dq_scan_el_group; -	ring->scan_timestamp = true; -	ring->preenable = &iio_sw_ring_preenable; -	ring->postenable = &iio_triggered_ring_postenable; -	ring->predisable = &iio_triggered_ring_predisable; -	ring->owner = THIS_MODULE; - -	/* Set default scan mode */ -	iio_scan_mask_set(ring, iio_scan_el_accel_x.number); -	iio_scan_mask_set(ring, iio_scan_el_accel_y.number); -	iio_scan_mask_set(ring, iio_scan_el_accel_z.number); - -	ret = iio_alloc_pollfunc(indio_dev, NULL, &lis3l02dq_poll_func_th); +	if (test_bit(0, indio_dev->active_scan_mask)) { +		t |= LIS3L02DQ_REG_CTRL_1_AXES_X_ENABLE; +		oneenabled = true; +	} else +		t &= ~LIS3L02DQ_REG_CTRL_1_AXES_X_ENABLE; +	if (test_bit(1, indio_dev->active_scan_mask)) { +		t |= LIS3L02DQ_REG_CTRL_1_AXES_Y_ENABLE; +		oneenabled = true; +	} else +		t &= ~LIS3L02DQ_REG_CTRL_1_AXES_Y_ENABLE; +	if (test_bit(2, indio_dev->active_scan_mask)) { +		t |= LIS3L02DQ_REG_CTRL_1_AXES_Z_ENABLE; +		oneenabled = true; +	} else +		t &= ~LIS3L02DQ_REG_CTRL_1_AXES_Z_ENABLE; + +	if (!oneenabled) /* what happens in this case is unknown */ +		return -EINVAL; +	ret = lis3l02dq_spi_write_reg_8(indio_dev, +					LIS3L02DQ_REG_CTRL_1_ADDR, +					t);  	if (ret) -		goto error_iio_sw_rb_free;; -	indio_dev->modes |= INDIO_RING_TRIGGERED; +		goto error_ret; + +	return iio_triggered_buffer_postenable(indio_dev); +error_ret: +	return ret; +} + +/* Turn all channels on again */ +static int lis3l02dq_buffer_predisable(struct iio_dev *indio_dev) +{ +	u8 t; +	int ret; + +	ret = iio_triggered_buffer_predisable(indio_dev); +	if (ret) +		goto error_ret; + +	ret = lis3l02dq_spi_read_reg_8(indio_dev, +				       LIS3L02DQ_REG_CTRL_1_ADDR, +				       &t); +	if (ret) +		goto error_ret; +	t |= LIS3L02DQ_REG_CTRL_1_AXES_X_ENABLE | +		LIS3L02DQ_REG_CTRL_1_AXES_Y_ENABLE | +		LIS3L02DQ_REG_CTRL_1_AXES_Z_ENABLE; + +	ret = lis3l02dq_spi_write_reg_8(indio_dev, +					LIS3L02DQ_REG_CTRL_1_ADDR, +					t); + +error_ret: +	return ret; +} + +static const struct iio_buffer_setup_ops lis3l02dq_buffer_setup_ops = { +	.postenable = &lis3l02dq_buffer_postenable, +	.predisable = &lis3l02dq_buffer_predisable, +}; + +int lis3l02dq_configure_buffer(struct iio_dev *indio_dev) +{ +	int ret; +	struct iio_buffer *buffer; + +	buffer = iio_kfifo_allocate(indio_dev); +	if (!buffer) +		return -ENOMEM; + +	iio_device_attach_buffer(indio_dev, buffer); + +	buffer->scan_timestamp = true; +	indio_dev->setup_ops = &lis3l02dq_buffer_setup_ops; + +	/* Functions are NULL as we set handler below */ +	indio_dev->pollfunc = iio_alloc_pollfunc(&iio_pollfunc_store_time, +						 &lis3l02dq_trigger_handler, +						 0, +						 indio_dev, +						 "lis3l02dq_consumer%d", +						 indio_dev->id); + +	if (indio_dev->pollfunc == NULL) { +		ret = -ENOMEM; +		goto error_iio_sw_rb_free; +	} + +	indio_dev->modes |= INDIO_BUFFER_TRIGGERED;  	return 0;  error_iio_sw_rb_free: -	iio_sw_rb_free(indio_dev->ring); +	iio_kfifo_free(indio_dev->buffer);  	return ret;  } diff --git a/drivers/staging/iio/accel/sca3000.h b/drivers/staging/iio/accel/sca3000.h index 23892848f5a..b284e5a6cac 100644 --- a/drivers/staging/iio/accel/sca3000.h +++ b/drivers/staging/iio/accel/sca3000.h @@ -2,7 +2,7 @@   * sca3000.c -- support VTI sca3000 series accelerometers   *              via SPI   * - * Copyright (c) 2007 Jonathan Cameron <jic23@cam.ac.uk> + * Copyright (c) 2007 Jonathan Cameron <jic23@kernel.org>   *   * Partly based upon tle62x0.c   * @@ -65,7 +65,8 @@  #define SCA3000_RING_BUF_ENABLE			0x80  #define SCA3000_RING_BUF_8BIT			0x40 -/* Free fall detection triggers an interrupt if the acceleration +/* + * Free fall detection triggers an interrupt if the acceleration   * is below a threshold for equivalent of 25cm drop   */  #define SCA3000_FREE_FALL_DETECT		0x10 @@ -73,8 +74,9 @@  #define SCA3000_MEAS_MODE_OP_1			0x01  #define SCA3000_MEAS_MODE_OP_2			0x02 -/* In motion detection mode the accelerations are band pass filtered - * (aprox 1 - 25Hz) and then a programmable threshold used to trigger +/* + * In motion detection mode the accelerations are band pass filtered + * (approx 1 - 25Hz) and then a programmable threshold used to trigger   * and interrupt.   */  #define SCA3000_MEAS_MODE_MOT_DET		0x03 @@ -99,8 +101,10 @@  #define SCA3000_REG_CTRL_SEL_MD_Y_TH		0x03  #define SCA3000_REG_CTRL_SEL_MD_X_TH		0x04  #define SCA3000_REG_CTRL_SEL_MD_Z_TH		0x05 -/* BE VERY CAREFUL WITH THIS, IF 3 BITS ARE NOT SET the device -   will not function */ +/* + * BE VERY CAREFUL WITH THIS, IF 3 BITS ARE NOT SET the device + * will not function + */  #define SCA3000_REG_CTRL_SEL_OUT_CTRL		0x0B  #define SCA3000_OUT_CTRL_PROT_MASK		0xE0  #define SCA3000_OUT_CTRL_BUF_X_EN		0x10 @@ -109,8 +113,9 @@  #define SCA3000_OUT_CTRL_BUF_DIV_4		0x02  #define SCA3000_OUT_CTRL_BUF_DIV_2		0x01 -/* Control which motion detector interrupts are on. - * For now only OR combinations are supported.x +/* + * Control which motion detector interrupts are on. + * For now only OR combinations are supported.   */  #define SCA3000_MD_CTRL_PROT_MASK		0xC0  #define SCA3000_MD_CTRL_OR_Y			0x01 @@ -121,7 +126,8 @@  #define SCA3000_MD_CTRL_AND_X			0x10  #define SAC3000_MD_CTRL_AND_Z			0x20 -/* Some control registers of complex access methods requiring this register to +/* + * Some control registers of complex access methods requiring this register to   * be used to remove a lock.   */  #define SCA3000_REG_ADDR_UNLOCK			0x1e @@ -136,10 +142,11 @@  #define SCA3000_INT_MASK_ACTIVE_HIGH		0x01  #define SCA3000_INT_MASK_ACTIVE_LOW		0x00 -/* Values of mulipexed registers (write to ctrl_data after select) */ +/* Values of multiplexed registers (write to ctrl_data after select) */  #define SCA3000_REG_ADDR_CTRL_DATA		0x22 -/* Measurement modes available on some sca3000 series chips. Code assumes others +/* + * Measurement modes available on some sca3000 series chips. Code assumes others   * may become available in the future.   *   * Bypass - Bypass the low-pass filter in the signal channel so as to increase @@ -158,36 +165,33 @@  /**   * struct sca3000_state - device instance state information - * @us: 	 		the associated spi device - * @info: 	  		chip variant information - * @indio_dev: 	 		device information used by the IIO core - * @interrupt_handler_ws: 	event interrupt handler for all events - * @last_timestamp: 		the timestamp of the last event - * @mo_det_use_count: 		reference counter for the motion detection unit - * @lock: 		 	lock used to protect elements of sca3000_state - * 	 			and the underlying device state. - * @bpse: 		 	number of bits per scan element - * @tx: 		 	dma-able transmit buffer - * @rx: 		 	dma-able receive buffer + * @us:			the associated spi device + * @info:			chip variant information + * @interrupt_handler_ws:	event interrupt handler for all events + * @last_timestamp:		the timestamp of the last event + * @mo_det_use_count:		reference counter for the motion detection unit + * @lock:			lock used to protect elements of sca3000_state + *				and the underlying device state. + * @bpse:			number of bits per scan element + * @tx:			dma-able transmit buffer + * @rx:			dma-able receive buffer   **/  struct sca3000_state {  	struct spi_device		*us;  	const struct sca3000_chip_info	*info; -	struct iio_dev			*indio_dev;  	struct work_struct		interrupt_handler_ws;  	s64				last_timestamp;  	int				mo_det_use_count;  	struct mutex			lock;  	int				bpse; -	u8				*tx; -	/* not used during a ring buffer read */ -	u8				*rx; +	/* Can these share a cacheline ? */ +	u8				rx[2] ____cacheline_aligned; +	u8				tx[6] ____cacheline_aligned;  };  /** - * struct sca3000_chip_info - model dependant parameters - * @name: 			model identification - * @scale:			string containing floating point scale factor + * struct sca3000_chip_info - model dependent parameters + * @scale:			scale * 10^-6   * @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,30 +203,20 @@ struct sca3000_state {   * sca3000 variant.   **/  struct sca3000_chip_info { -	const char		*name; -	const char		*scale; +	unsigned int		scale;  	bool			temp_output;  	int			measurement_mode_freq;  	int			option_mode_1;  	int			option_mode_1_freq;  	int			option_mode_2;  	int			option_mode_2_freq; +	int			mot_det_mult_xz[6]; +	int			mot_det_mult_y[7];  }; -/** - * sca3000_read_data() read a series of values from the device - * @dev:		device - * @reg_address_high:	start address (decremented read) - * @rx:			pointer where recieved data is placed. Callee - *			responsible for freeing this. - * @len:		number of bytes to read - * - * The main lock must be held. - **/ -int sca3000_read_data(struct sca3000_state *st, -		      u8 reg_address_high, -		      u8 **rx_p, -		      int len); +int sca3000_read_data_short(struct sca3000_state *st, +			    u8 reg_address_high, +			    int len);  /**   * sca3000_write_reg() write a single register @@ -233,30 +227,7 @@ int sca3000_read_data(struct sca3000_state *st,   **/  int sca3000_write_reg(struct sca3000_state *st, u8 address, u8 val); -/* Conversion function for use with the ring buffer when in 11bit mode */ -static inline int sca3000_11bit_convert(uint8_t msb, uint8_t lsb) -{ -	int16_t val; - -	val = ((lsb >> 3) & 0x1C) | (msb << 5); -	val |= (val & (1 << 12)) ? 0xE000 : 0; - -	return val; -} - -static inline int sca3000_13bit_convert(uint8_t msb, uint8_t lsb) -{ -	s16 val; - -	val = ((lsb >> 3) & 0x1F) | (msb << 5); -	/* sign fill */ -	val |= (val & (1 << 12)) ? 0xE000 : 0; - -	return val; -} - - -#ifdef CONFIG_IIO_RING_BUFFER +#ifdef CONFIG_IIO_BUFFER  /**   * sca3000_register_ring_funcs() setup the ring state change functions   **/ @@ -283,7 +254,7 @@ void sca3000_unconfigure_ring(struct iio_dev *indio_dev);   * sca3000_ring_int_process() handles ring related event pushing and escalation   * @val:	the event code   **/ -void sca3000_ring_int_process(u8 val, struct iio_ring_buffer *ring); +void sca3000_ring_int_process(u8 val, struct iio_buffer *ring);  #else  static inline void sca3000_register_ring_funcs(struct iio_dev *indio_dev) diff --git a/drivers/staging/iio/accel/sca3000_core.c b/drivers/staging/iio/accel/sca3000_core.c index 5b06dea6af2..ed30e32e60d 100644 --- a/drivers/staging/iio/accel/sca3000_core.c +++ b/drivers/staging/iio/accel/sca3000_core.c @@ -5,24 +5,24 @@   * under the terms of the GNU General Public License version 2 as published by   * the Free Software Foundation.   * - * Copyright (c) 2009 Jonathan Cameron <jic23@cam.ac.uk> + * Copyright (c) 2009 Jonathan Cameron <jic23@kernel.org>   *   * See industrialio/accels/sca3000.h for comments.   */  #include <linux/interrupt.h> -#include <linux/gpio.h>  #include <linux/fs.h>  #include <linux/device.h>  #include <linux/slab.h>  #include <linux/kernel.h>  #include <linux/spi/spi.h>  #include <linux/sysfs.h> -#include "../iio.h" -#include "../sysfs.h" -#include "../ring_generic.h" +#include <linux/module.h> +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> +#include <linux/iio/events.h> +#include <linux/iio/buffer.h> -#include "accel.h"  #include "sca3000.h"  enum sca3000_variant { @@ -32,7 +32,8 @@ enum sca3000_variant {  	e05,  }; -/* Note where option modes are not defined, the chip simply does not +/* + * Note where option modes are not defined, the chip simply does not   * support any.   * Other chips in the sca3000 series use i2c and are not included here.   * @@ -40,95 +41,70 @@ enum sca3000_variant {   * do not actually appear to be available.   */  static const struct sca3000_chip_info sca3000_spi_chip_info_tbl[] = { -	{ -		.name = "sca3000-d01", -		.scale = " 0.0073575", +	[d01] = { +		.scale = 7357,  		.temp_output = true,  		.measurement_mode_freq = 250,  		.option_mode_1 = SCA3000_OP_MODE_BYPASS,  		.option_mode_1_freq = 250, -	}, { -		.name = "sca3000-e02", -		.scale = "0.00981", +		.mot_det_mult_xz = {50, 100, 200, 350, 650, 1300}, +		.mot_det_mult_y = {50, 100, 150, 250, 450, 850, 1750}, +	}, +	[e02] = { +		.scale = 9810,  		.measurement_mode_freq = 125,  		.option_mode_1 = SCA3000_OP_MODE_NARROW,  		.option_mode_1_freq = 63, -	}, { -		.name = "sca3000-e04", -		.scale = "0.01962", +		.mot_det_mult_xz = {100, 150, 300, 550, 1050, 2050}, +		.mot_det_mult_y = {50, 100, 200, 350, 700, 1350, 2700}, +	}, +	[e04] = { +		.scale = 19620,  		.measurement_mode_freq = 100,  		.option_mode_1 = SCA3000_OP_MODE_NARROW,  		.option_mode_1_freq = 50,  		.option_mode_2 = SCA3000_OP_MODE_WIDE,  		.option_mode_2_freq = 400, -	}, { -		.name = "sca3000-e05", -		.scale = "0.0613125", +		.mot_det_mult_xz = {200, 300, 600, 1100, 2100, 4100}, +		.mot_det_mult_y = {100, 200, 400, 7000, 1400, 2700, 54000}, +	}, +	[e05] = { +		.scale = 61313,  		.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, +		.mot_det_mult_xz = {600, 900, 1700, 3200, 6100, 11900}, +		.mot_det_mult_y = {300, 600, 1200, 2000, 4100, 7800, 15600},  	},  }; -  int sca3000_write_reg(struct sca3000_state *st, u8 address, u8 val)  { -	struct spi_transfer xfer = { -		.bits_per_word = 8, -		.len = 2, -		.cs_change = 1, -		.tx_buf = st->tx, -	}; -	struct spi_message msg; -  	st->tx[0] = SCA3000_WRITE_REG(address);  	st->tx[1] = val; -	spi_message_init(&msg); -	spi_message_add_tail(&xfer, &msg); - -	return spi_sync(st->us, &msg); +	return spi_write(st->us, st->tx, 2);  } -int sca3000_read_data(struct sca3000_state *st, -		      uint8_t reg_address_high, -		      u8 **rx_p, -		      int len) +int sca3000_read_data_short(struct sca3000_state *st, +			    uint8_t reg_address_high, +			    int len)  { -	int ret; -	struct spi_message msg; -	struct spi_transfer xfer = { -		.bits_per_word = 8, -		.len = len + 1, -		.cs_change = 1, -		.tx_buf = st->tx, +	struct spi_transfer xfer[2] = { +		{ +			.len = 1, +			.tx_buf = st->tx, +		}, { +			.len = len, +			.rx_buf = st->rx, +		}  	}; - -	*rx_p = kmalloc(len + 1, GFP_KERNEL); -	if (*rx_p == NULL) { -		ret = -ENOMEM; -		goto error_ret; -	} -	xfer.rx_buf = *rx_p;  	st->tx[0] = SCA3000_READ_REG(reg_address_high); -	spi_message_init(&msg); -	spi_message_add_tail(&xfer, &msg); - -	ret = spi_sync(st->us, &msg); - -	if (ret) { -		dev_err(get_device(&st->us->dev), "problem reading register"); -		goto error_free_rx; -	} - -	return 0; -error_free_rx: -	kfree(*rx_p); -error_ret: -	return ret; +	return spi_sync_transfer(st->us, xfer, ARRAY_SIZE(xfer));  } +  /**   * sca3000_reg_lock_on() test if the ctrl register lock is on   * @@ -136,17 +112,13 @@ error_ret:   **/  static int sca3000_reg_lock_on(struct sca3000_state *st)  { -	u8 *rx;  	int ret; -	ret = sca3000_read_data(st, SCA3000_REG_ADDR_STATUS, &rx, 1); - +	ret = sca3000_read_data_short(st, SCA3000_REG_ADDR_STATUS, 1);  	if (ret < 0)  		return ret; -	ret = !(rx[1] & SCA3000_LOCKED); -	kfree(rx); -	return ret; +	return !(st->rx[0] & SCA3000_LOCKED);  }  /** @@ -158,22 +130,17 @@ static int sca3000_reg_lock_on(struct sca3000_state *st)   **/  static int __sca3000_unlock_reg_lock(struct sca3000_state *st)  { -	struct spi_message msg;  	struct spi_transfer xfer[3] = {  		{ -			.bits_per_word = 8,  			.len = 2,  			.cs_change = 1,  			.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,  			.tx_buf = st->tx + 4,  		},  	}; @@ -183,12 +150,8 @@ static int __sca3000_unlock_reg_lock(struct sca3000_state *st)  	st->tx[3] = 0x50;  	st->tx[4] = SCA3000_WRITE_REG(SCA3000_REG_ADDR_UNLOCK);  	st->tx[5] = 0xA0; -	spi_message_init(&msg); -	spi_message_add_tail(&xfer[0], &msg); -	spi_message_add_tail(&xfer[1], &msg); -	spi_message_add_tail(&xfer[2], &msg); -	return spi_sync(st->us, &msg); +	return spi_sync_transfer(st->us, xfer, ARRAY_SIZE(xfer));  }  /** @@ -229,15 +192,13 @@ error_ret:  	return ret;  } -/* Crucial that lock is called before calling this */  /**   * sca3000_read_ctrl_reg() read from lock protected control register.   *   * Lock must be held.   **/  static int sca3000_read_ctrl_reg(struct sca3000_state *st, -				 u8 ctrl_reg, -				 u8 **rx_p) +				 u8 ctrl_reg)  {  	int ret; @@ -253,8 +214,11 @@ static int sca3000_read_ctrl_reg(struct sca3000_state *st,  	ret = sca3000_write_reg(st, SCA3000_REG_ADDR_CTRL_SEL, ctrl_reg);  	if (ret)  		goto error_ret; -	ret = sca3000_read_data(st, SCA3000_REG_ADDR_CTRL_DATA, rx_p, 1); - +	ret = sca3000_read_data_short(st, SCA3000_REG_ADDR_CTRL_DATA, 1); +	if (ret) +		goto error_ret; +	else +		return st->rx[0];  error_ret:  	return ret;  } @@ -267,20 +231,18 @@ error_ret:   **/  static int sca3000_check_status(struct device *dev)  { -	u8 *rx;  	int ret; -	struct iio_dev *indio_dev = dev_get_drvdata(dev); -	struct sca3000_state *st = indio_dev->dev_data; +	struct iio_dev *indio_dev = dev_to_iio_dev(dev); +	struct sca3000_state *st = iio_priv(indio_dev);  	mutex_lock(&st->lock); -	ret = sca3000_read_data(st, SCA3000_REG_ADDR_STATUS, &rx, 1); +	ret = sca3000_read_data_short(st, SCA3000_REG_ADDR_STATUS, 1);  	if (ret < 0)  		goto error_ret; -	if (rx[1] & SCA3000_EEPROM_CS_ERROR) +	if (st->rx[0] & SCA3000_EEPROM_CS_ERROR)  		dev_err(dev, "eeprom error\n"); -	if (rx[1] & SCA3000_SPI_FRAME_ERROR) +	if (st->rx[0] & SCA3000_SPI_FRAME_ERROR)  		dev_err(dev, "Previous SPI Frame was corrupt\n"); -	kfree(rx);  error_ret:  	mutex_unlock(&st->lock); @@ -289,75 +251,24 @@ error_ret:  #endif /* SCA3000_DEBUG */  /** - * sca3000_read_13bit_signed() sysfs interface to read 13 bit signed registers - * - * These are described as signed 12 bit on the data sheet, which appears - * to be a conventional 2's complement 13 bit. - **/ -static ssize_t sca3000_read_13bit_signed(struct device *dev, -					 struct device_attribute *attr, -					 char *buf) -{ -	int len = 0, ret; -	int val; -	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); -	u8 *rx; -	struct iio_dev *indio_dev = dev_get_drvdata(dev); -	struct sca3000_state *st = indio_dev->dev_data; - -	mutex_lock(&st->lock); -	ret = sca3000_read_data(st, this_attr->address, &rx, 2); -	if (ret < 0) -		goto error_ret; -	val = sca3000_13bit_convert(rx[1], rx[2]); -	len += sprintf(buf + len, "%d\n", val); -	kfree(rx); -error_ret: -	mutex_unlock(&st->lock); - -	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, -				 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->name); -} -/** - * sca3000_show_reg() - sysfs interface to read the chip revision number + * sca3000_show_rev() - sysfs interface to read the chip revision number   **/  static ssize_t sca3000_show_rev(struct device *dev,  				struct device_attribute *attr,  				char *buf)  {  	int len = 0, ret; -	struct iio_dev *dev_info = dev_get_drvdata(dev); -	struct sca3000_state *st = dev_info->dev_data; - -	u8 *rx; +	struct iio_dev *indio_dev = dev_to_iio_dev(dev); +	struct sca3000_state *st = iio_priv(indio_dev);  	mutex_lock(&st->lock); -	ret = sca3000_read_data(st, SCA3000_REG_ADDR_REVID, &rx, 1); +	ret = sca3000_read_data_short(st, SCA3000_REG_ADDR_REVID, 1);  	if (ret < 0)  		goto error_ret;  	len += sprintf(buf + len,  		       "major=%d, minor=%d\n", -		       rx[1] & SCA3000_REVID_MAJOR_MASK, -		       rx[1] & SCA3000_REVID_MINOR_MASK); -	kfree(rx); - +		       st->rx[0] & SCA3000_REVID_MAJOR_MASK, +		       st->rx[0] & SCA3000_REVID_MINOR_MASK);  error_ret:  	mutex_unlock(&st->lock); @@ -375,8 +286,8 @@ sca3000_show_available_measurement_modes(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; +	struct iio_dev *indio_dev = dev_to_iio_dev(dev); +	struct sca3000_state *st = iio_priv(indio_dev);  	int len = 0;  	len += sprintf(buf + len, "0 - normal mode"); @@ -400,25 +311,24 @@ sca3000_show_available_measurement_modes(struct device *dev,  }  /** - * sca3000_show_measurmenet_mode() sysfs read of current mode + * sca3000_show_measurement_mode() sysfs read of current mode   **/  static ssize_t  sca3000_show_measurement_mode(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; +	struct iio_dev *indio_dev = dev_to_iio_dev(dev); +	struct sca3000_state *st = iio_priv(indio_dev);  	int len = 0, ret; -	u8 *rx;  	mutex_lock(&st->lock); -	ret = sca3000_read_data(st, SCA3000_REG_ADDR_MODE, &rx, 1); +	ret = sca3000_read_data_short(st, SCA3000_REG_ADDR_MODE, 1);  	if (ret)  		goto error_ret;  	/* mask bottom 2 bits - only ones that are relevant */ -	rx[1] &= 0x03; -	switch (rx[1]) { +	st->rx[0] &= 0x03; +	switch (st->rx[0]) {  	case SCA3000_MEAS_MODE_NORMAL:  		len += sprintf(buf + len, "0 - normal mode\n");  		break; @@ -459,31 +369,32 @@ sca3000_store_measurement_mode(struct device *dev,  			       const char *buf,  			       size_t len)  { -	struct iio_dev *dev_info = dev_get_drvdata(dev); -	struct sca3000_state *st = dev_info->dev_data; +	struct iio_dev *indio_dev = dev_to_iio_dev(dev); +	struct sca3000_state *st = iio_priv(indio_dev);  	int ret; -	u8 *rx; -	int mask = 0x03; -	long val; +	u8 mask = 0x03; +	u8 val;  	mutex_lock(&st->lock); -	ret = strict_strtol(buf, 10, &val); +	ret = kstrtou8(buf, 10, &val);  	if (ret)  		goto error_ret; -	ret = sca3000_read_data(st, SCA3000_REG_ADDR_MODE, &rx, 1); +	if (val > 3) { +		ret = -EINVAL; +		goto error_ret; +	} +	ret = sca3000_read_data_short(st, SCA3000_REG_ADDR_MODE, 1);  	if (ret)  		goto error_ret; -	rx[1] &= ~mask; -	rx[1] |= (val & mask); -	ret = sca3000_write_reg(st, SCA3000_REG_ADDR_MODE, rx[1]); +	st->rx[0] &= ~mask; +	st->rx[0] |= (val & mask); +	ret = sca3000_write_reg(st, SCA3000_REG_ADDR_MODE, st->rx[0]);  	if (ret) -		goto error_free_rx; +		goto error_ret;  	mutex_unlock(&st->lock);  	return len; -error_free_rx: -	kfree(rx);  error_ret:  	mutex_unlock(&st->lock); @@ -491,7 +402,8 @@ error_ret:  } -/* Not even vaguely standard attributes so defined here rather than +/* + * Not even vaguely standard attributes so defined here rather than   * in the relevant IIO core headers   */  static IIO_DEVICE_ATTR(measurement_mode_available, S_IRUGO, @@ -505,18 +417,114 @@ static IIO_DEVICE_ATTR(measurement_mode, S_IRUGO | S_IWUSR,  /* More standard attributes */ -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_DEVICE_ATTR(revision, S_IRUGO, sca3000_show_rev, NULL, 0); -static IIO_DEV_ATTR_ACCEL_X(sca3000_read_13bit_signed, -			    SCA3000_REG_ADDR_X_MSB); -static IIO_DEV_ATTR_ACCEL_Y(sca3000_read_13bit_signed, -			    SCA3000_REG_ADDR_Y_MSB); -static IIO_DEV_ATTR_ACCEL_Z(sca3000_read_13bit_signed, -			    SCA3000_REG_ADDR_Z_MSB); +static const struct iio_event_spec sca3000_event = { +	.type = IIO_EV_TYPE_MAG, +	.dir = IIO_EV_DIR_RISING, +	.mask_separate = BIT(IIO_EV_INFO_VALUE) | BIT(IIO_EV_INFO_ENABLE), +}; +#define SCA3000_CHAN(index, mod)				\ +	{							\ +		.type = IIO_ACCEL,				\ +		.modified = 1,					\ +		.channel2 = mod,				\ +		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),	\ +		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),\ +		.address = index,				\ +		.scan_index = index,				\ +		.scan_type = {					\ +			.sign = 's',				\ +			.realbits = 11,				\ +			.storagebits = 16,			\ +			.shift = 5,				\ +		},						\ +		.event_spec = &sca3000_event,			\ +		.num_event_specs = 1,				\ +	 } + +static const struct iio_chan_spec sca3000_channels[] = { +	SCA3000_CHAN(0, IIO_MOD_X), +	SCA3000_CHAN(1, IIO_MOD_Y), +	SCA3000_CHAN(2, IIO_MOD_Z), +}; + +static const struct iio_chan_spec sca3000_channels_with_temp[] = { +	SCA3000_CHAN(0, IIO_MOD_X), +	SCA3000_CHAN(1, IIO_MOD_Y), +	SCA3000_CHAN(2, IIO_MOD_Z), +	{ +		.type = IIO_TEMP, +		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), +		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | +			BIT(IIO_CHAN_INFO_OFFSET), +	}, +}; + +static u8 sca3000_addresses[3][3] = { +	[0] = {SCA3000_REG_ADDR_X_MSB, SCA3000_REG_CTRL_SEL_MD_X_TH, +	       SCA3000_MD_CTRL_OR_X}, +	[1] = {SCA3000_REG_ADDR_Y_MSB, SCA3000_REG_CTRL_SEL_MD_Y_TH, +	       SCA3000_MD_CTRL_OR_Y}, +	[2] = {SCA3000_REG_ADDR_Z_MSB, SCA3000_REG_CTRL_SEL_MD_Z_TH, +	       SCA3000_MD_CTRL_OR_Z}, +}; + +static int sca3000_read_raw(struct iio_dev *indio_dev, +			    struct iio_chan_spec const *chan, +			    int *val, +			    int *val2, +			    long mask) +{ +	struct sca3000_state *st = iio_priv(indio_dev); +	int ret; +	u8 address; + +	switch (mask) { +	case IIO_CHAN_INFO_RAW: +		mutex_lock(&st->lock); +		if (chan->type == IIO_ACCEL) { +			if (st->mo_det_use_count) { +				mutex_unlock(&st->lock); +				return -EBUSY; +			} +			address = sca3000_addresses[chan->address][0]; +			ret = sca3000_read_data_short(st, address, 2); +			if (ret < 0) { +				mutex_unlock(&st->lock); +				return ret; +			} +			*val = (be16_to_cpup((__be16 *)st->rx) >> 3) & 0x1FFF; +			*val = ((*val) << (sizeof(*val)*8 - 13)) >> +				(sizeof(*val)*8 - 13); +		} else { +			/* get the temperature when available */ +			ret = sca3000_read_data_short(st, +				SCA3000_REG_ADDR_TEMP_MSB, 2); +			if (ret < 0) { +				mutex_unlock(&st->lock); +				return ret; +			} +			*val = ((st->rx[0] & 0x3F) << 3) | ((st->rx[1] & 0xE0) >> 5); +		} +		mutex_unlock(&st->lock); +		return IIO_VAL_INT; +	case IIO_CHAN_INFO_SCALE: +		*val = 0; +		if (chan->type == IIO_ACCEL) +			*val2 = st->info->scale; +		else /* temperature */ +			*val2 = 555556; +		return IIO_VAL_INT_PLUS_MICRO; +	case IIO_CHAN_INFO_OFFSET: +		*val = -214; +		*val2 = 600000; +		return IIO_VAL_INT_PLUS_MICRO; +	default: +		return -EINVAL; +	} +}  /**   * sca3000_read_av_freq() sysfs function to get available frequencies @@ -530,17 +538,18 @@ static ssize_t sca3000_read_av_freq(struct device *dev,  			     struct device_attribute *attr,  			     char *buf)  { -	struct iio_dev *indio_dev = dev_get_drvdata(dev); -	struct sca3000_state *st = indio_dev->dev_data; -	int len = 0, ret; -	u8 *rx; +	struct iio_dev *indio_dev = dev_to_iio_dev(dev); +	struct sca3000_state *st = iio_priv(indio_dev); +	int len = 0, ret, val; +  	mutex_lock(&st->lock); -	ret = sca3000_read_data(st, SCA3000_REG_ADDR_MODE, &rx, 1); +	ret = sca3000_read_data_short(st, SCA3000_REG_ADDR_MODE, 1); +	val = st->rx[0];  	mutex_unlock(&st->lock);  	if (ret)  		goto error_ret; -	rx[1] &= 0x03; -	switch (rx[1]) { + +	switch (val & 0x03) {  	case SCA3000_MEAS_MODE_NORMAL:  		len += sprintf(buf + len, "%d %d %d\n",  			       st->info->measurement_mode_freq, @@ -560,13 +569,12 @@ static ssize_t sca3000_read_av_freq(struct device *dev,  			       st->info->option_mode_2_freq/4);  		break;  	} -	kfree(rx);  	return len;  error_ret:  	return ret;  }  /** - * __sca3000_get_base_frequency() obtain mode specific base frequency + * __sca3000_get_base_freq() obtain mode specific base frequency   *   * lock must be held   **/ @@ -575,12 +583,11 @@ static inline int __sca3000_get_base_freq(struct sca3000_state *st,  					  int *base_freq)  {  	int ret; -	u8 *rx; -	ret = sca3000_read_data(st, SCA3000_REG_ADDR_MODE, &rx, 1); +	ret = sca3000_read_data_short(st, SCA3000_REG_ADDR_MODE, 1);  	if (ret)  		goto error_ret; -	switch (0x03 & rx[1]) { +	switch (0x03 & st->rx[0]) {  	case SCA3000_MEAS_MODE_NORMAL:  		*base_freq = info->measurement_mode_freq;  		break; @@ -591,7 +598,6 @@ static inline int __sca3000_get_base_freq(struct sca3000_state *st,  		*base_freq = info->option_mode_2_freq;  		break;  	} -	kfree(rx);  error_ret:  	return ret;  } @@ -603,20 +609,21 @@ static ssize_t sca3000_read_frequency(struct device *dev,  			       struct device_attribute *attr,  			       char *buf)  { -	struct iio_dev *indio_dev = dev_get_drvdata(dev); -	struct sca3000_state *st = indio_dev->dev_data; -	int ret, len = 0, base_freq = 0; -	u8 *rx; +	struct iio_dev *indio_dev = dev_to_iio_dev(dev); +	struct sca3000_state *st = iio_priv(indio_dev); +	int ret, len = 0, base_freq = 0, val; +  	mutex_lock(&st->lock);  	ret = __sca3000_get_base_freq(st, st->info, &base_freq);  	if (ret)  		goto error_ret_mut; -	ret = sca3000_read_ctrl_reg(st, SCA3000_REG_CTRL_SEL_OUT_CTRL, &rx); +	ret = sca3000_read_ctrl_reg(st, SCA3000_REG_CTRL_SEL_OUT_CTRL);  	mutex_unlock(&st->lock);  	if (ret)  		goto error_ret; +	val = ret;  	if (base_freq > 0) -		switch (rx[1]&0x03) { +		switch (val & 0x03) {  		case 0x00:  		case 0x03:  			len = sprintf(buf, "%d\n", base_freq); @@ -628,7 +635,7 @@ static ssize_t sca3000_read_frequency(struct device *dev,  			len = sprintf(buf, "%d\n", base_freq/4);  			break;  	} -	kfree(rx); +  	return len;  error_ret_mut:  	mutex_unlock(&st->lock); @@ -644,13 +651,13 @@ static ssize_t sca3000_set_frequency(struct device *dev,  			      const char *buf,  			      size_t len)  { -	struct iio_dev *indio_dev = dev_get_drvdata(dev); -	struct sca3000_state *st = indio_dev->dev_data; +	struct iio_dev *indio_dev = dev_to_iio_dev(dev); +	struct sca3000_state *st = iio_priv(indio_dev);  	int ret, base_freq = 0; -	u8 *rx; -	long val; +	int ctrlval; +	int val; -	ret = strict_strtol(buf, 10, &val); +	ret = kstrtoint(buf, 10, &val);  	if (ret)  		return ret; @@ -660,28 +667,31 @@ static ssize_t sca3000_set_frequency(struct device *dev,  	if (ret)  		goto error_free_lock; -	ret = sca3000_read_ctrl_reg(st, SCA3000_REG_CTRL_SEL_OUT_CTRL, &rx); -	if (ret) +	ret = sca3000_read_ctrl_reg(st, SCA3000_REG_CTRL_SEL_OUT_CTRL); +	if (ret < 0)  		goto error_free_lock; +	ctrlval = ret;  	/* clear the bits */ -	rx[1] &= ~0x03; +	ctrlval &= ~0x03;  	if (val == base_freq/2) { -		rx[1] |= SCA3000_OUT_CTRL_BUF_DIV_2; +		ctrlval |= SCA3000_OUT_CTRL_BUF_DIV_2;  	} else if (val == base_freq/4) { -		rx[1] |= SCA3000_OUT_CTRL_BUF_DIV_4; +		ctrlval |= SCA3000_OUT_CTRL_BUF_DIV_4;  	} else if (val != base_freq) {  		ret = -EINVAL;  		goto error_free_lock;  	} -	ret = sca3000_write_ctrl_reg(st, SCA3000_REG_CTRL_SEL_OUT_CTRL, rx[1]); +	ret = sca3000_write_ctrl_reg(st, SCA3000_REG_CTRL_SEL_OUT_CTRL, +				     ctrlval);  error_free_lock:  	mutex_unlock(&st->lock);  	return ret ? ret : len;  } -/* Should only really be registered if ring buffer support is compiled in. +/* + * 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_SAMP_FREQ_AVAIL(sca3000_read_av_freq); @@ -690,137 +700,82 @@ static IIO_DEV_ATTR_SAMP_FREQ(S_IWUSR | S_IRUGO,  			      sca3000_read_frequency,  			      sca3000_set_frequency); - -/** - * sca3000_read_temp() sysfs interface to get the temperature when available - * -* The alignment of data in here is downright odd. See data sheet. -* Converting this into a meaningful value is left to inline functions in -* userspace part of header. -**/ -static ssize_t sca3000_read_temp(struct device *dev, -				 struct device_attribute *attr, -				 char *buf) -{ -	struct iio_dev *indio_dev = dev_get_drvdata(dev); -	struct sca3000_state *st = indio_dev->dev_data; -	int len = 0, ret; -	int val; -	u8 *rx; -	ret = sca3000_read_data(st, SCA3000_REG_ADDR_TEMP_MSB, &rx, 2); -	if (ret < 0) -		goto error_ret; -	val = ((rx[1]&0x3F) << 3) | ((rx[2] & 0xE0) >> 5); -	len += sprintf(buf + len, "%d\n", val); -	kfree(rx); - -	return len; - -error_ret: -	return ret; -} -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 + * sca3000_read_thresh() - query of a threshold   **/ -static ssize_t sca3000_show_thresh(struct device *dev, -				   struct device_attribute *attr, -				   char *buf) +static int sca3000_read_thresh(struct iio_dev *indio_dev, +			       const struct iio_chan_spec *chan, +			       enum iio_event_type type, +			       enum iio_event_direction dir, +			       enum iio_event_info info, +			       int *val, int *val2)  { -	struct iio_dev *indio_dev = dev_get_drvdata(dev); -	struct sca3000_state *st = indio_dev->dev_data; -	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); -	int len = 0, ret; -	u8 *rx; - +	int ret, i; +	struct sca3000_state *st = iio_priv(indio_dev); +	int num = chan->channel2;  	mutex_lock(&st->lock); -	ret = sca3000_read_ctrl_reg(st, -				    this_attr->address, -				    &rx); +	ret = sca3000_read_ctrl_reg(st, sca3000_addresses[num][1]);  	mutex_unlock(&st->lock); -	if (ret) +	if (ret < 0)  		return ret; -	len += sprintf(buf + len, "%d\n", rx[1]); -	kfree(rx); +	*val = 0; +	if (num == 1) +		for_each_set_bit(i, (unsigned long *)&ret, +				 ARRAY_SIZE(st->info->mot_det_mult_y)) +			*val += st->info->mot_det_mult_y[i]; +	else +		for_each_set_bit(i, (unsigned long *)&ret, +				 ARRAY_SIZE(st->info->mot_det_mult_xz)) +			*val += st->info->mot_det_mult_xz[i]; -	return len; +	return IIO_VAL_INT;  }  /** - * sca3000_write_thresh() sysfs control of threshold + * sca3000_write_thresh() control of threshold   **/ -static ssize_t sca3000_write_thresh(struct device *dev, -				    struct device_attribute *attr, -				    const char *buf, -				    size_t len) +static int sca3000_write_thresh(struct iio_dev *indio_dev, +				const struct iio_chan_spec *chan, +				enum iio_event_type type, +				enum iio_event_direction dir, +				enum iio_event_info info, +				int val, int val2)  { -	struct iio_dev *indio_dev = dev_get_drvdata(dev); -	struct sca3000_state *st = indio_dev->dev_data; -	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); +	struct sca3000_state *st = iio_priv(indio_dev); +	int num = chan->channel2;  	int ret; -	long val; +	int i; +	u8 nonlinear = 0; + +	if (num == 1) { +		i = ARRAY_SIZE(st->info->mot_det_mult_y); +		while (i > 0) +			if (val >= st->info->mot_det_mult_y[--i]) { +				nonlinear |= (1 << i); +				val -= st->info->mot_det_mult_y[i]; +			} +	} else { +		i = ARRAY_SIZE(st->info->mot_det_mult_xz); +		while (i > 0) +			if (val >= st->info->mot_det_mult_xz[--i]) { +				nonlinear |= (1 << i); +				val -= st->info->mot_det_mult_xz[i]; +			} +	} -	ret = strict_strtol(buf, 10, &val); -	if (ret) -		return ret;  	mutex_lock(&st->lock); -	ret = sca3000_write_ctrl_reg(st, this_attr->address, val); +	ret = sca3000_write_ctrl_reg(st, sca3000_addresses[num][1], nonlinear);  	mutex_unlock(&st->lock); -	return ret ? ret : len; +	return ret;  } -static IIO_DEVICE_ATTR(accel_x_raw_mag_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_raw_mag_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_raw_mag_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_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_sampling_frequency_available.dev_attr.attr, -	&iio_dev_attr_sampling_frequency.dev_attr.attr, -	NULL, -}; - -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_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_sampling_frequency_available.dev_attr.attr,  	&iio_dev_attr_sampling_frequency.dev_attr.attr, -	/* Only present if temp sensor is */ -	&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,  }; @@ -828,142 +783,108 @@ static const struct attribute_group sca3000_attribute_group = {  	.attrs = sca3000_attributes,  }; -static const struct attribute_group sca3000_attribute_group_with_temp = { -	.attrs = sca3000_attributes_with_temp, -}; - -/* RING RELATED interrupt handler */ -/* depending on event, push to the ring buffer event chrdev or the event one */ -  /** - * sca3000_interrupt_handler_bh() - handling ring and non ring events + * sca3000_event_handler() - handling ring and non ring events + * + * Ring related interrupt handler. Depending on event, push to + * the ring buffer event chrdev or the event one.   *   * This function is complicated by the fact that the devices can signify ring   * and non ring events via the same interrupt line and they can only   * be distinguished via a read of the relevant status register.   **/ -static void sca3000_interrupt_handler_bh(struct work_struct *work_s) +static irqreturn_t sca3000_event_handler(int irq, void *private)  { -	struct sca3000_state *st -		= container_of(work_s, struct sca3000_state, -			       interrupt_handler_ws); -	u8 *rx; -	int ret; +	struct iio_dev *indio_dev = private; +	struct sca3000_state *st = iio_priv(indio_dev); +	int ret, val; +	s64 last_timestamp = iio_get_time_ns(); -	/* Could lead if badly timed to an extra read of status reg, +	/* +	 * Could lead if badly timed to an extra read of status reg,  	 * but ensures no interrupt is missed.  	 */ -	enable_irq(st->us->irq);  	mutex_lock(&st->lock); -	ret = sca3000_read_data(st, SCA3000_REG_ADDR_INT_STATUS, -				&rx, 1); +	ret = sca3000_read_data_short(st, SCA3000_REG_ADDR_INT_STATUS, 1); +	val = st->rx[0];  	mutex_unlock(&st->lock);  	if (ret)  		goto done; -	sca3000_ring_int_process(rx[1], st->indio_dev->ring); +	sca3000_ring_int_process(val, indio_dev->buffer); -	if (rx[1] & SCA3000_INT_STATUS_FREE_FALL) -		iio_push_event(st->indio_dev, 0, -			       IIO_MOD_EVENT_CODE(IIO_EV_CLASS_ACCEL, +	if (val & SCA3000_INT_STATUS_FREE_FALL) +		iio_push_event(indio_dev, +			       IIO_MOD_EVENT_CODE(IIO_ACCEL,  						  0, -						  IIO_EV_MOD_X_AND_Y_AND_Z, +						  IIO_MOD_X_AND_Y_AND_Z,  						  IIO_EV_TYPE_MAG,  						  IIO_EV_DIR_FALLING), -			       st->last_timestamp); +			       last_timestamp); -	if (rx[1] & SCA3000_INT_STATUS_Y_TRIGGER) -		iio_push_event(st->indio_dev, 0, -			       IIO_MOD_EVENT_CODE(IIO_EV_CLASS_ACCEL, +	if (val & SCA3000_INT_STATUS_Y_TRIGGER) +		iio_push_event(indio_dev, +			       IIO_MOD_EVENT_CODE(IIO_ACCEL,  						  0, -						  IIO_EV_MOD_Y, +						  IIO_MOD_Y,  						  IIO_EV_TYPE_MAG,  						  IIO_EV_DIR_RISING), -			       st->last_timestamp); +			       last_timestamp); -	if (rx[1] & SCA3000_INT_STATUS_X_TRIGGER) -		iio_push_event(st->indio_dev, 0, -			       IIO_MOD_EVENT_CODE(IIO_EV_CLASS_ACCEL, +	if (val & SCA3000_INT_STATUS_X_TRIGGER) +		iio_push_event(indio_dev, +			       IIO_MOD_EVENT_CODE(IIO_ACCEL,  						  0, -						  IIO_EV_MOD_X, +						  IIO_MOD_X,  						  IIO_EV_TYPE_MAG,  						  IIO_EV_DIR_RISING), -			       st->last_timestamp); +			       last_timestamp); -	if (rx[1] & SCA3000_INT_STATUS_Z_TRIGGER) -		iio_push_event(st->indio_dev, 0, -			       IIO_MOD_EVENT_CODE(IIO_EV_CLASS_ACCEL, +	if (val & SCA3000_INT_STATUS_Z_TRIGGER) +		iio_push_event(indio_dev, +			       IIO_MOD_EVENT_CODE(IIO_ACCEL,  						  0, -						  IIO_EV_MOD_Z, +						  IIO_MOD_Z,  						  IIO_EV_TYPE_MAG,  						  IIO_EV_DIR_RISING), -			       st->last_timestamp); +			       last_timestamp);  done: -	kfree(rx); -	return; -} - -/** - * sca3000_handler_th() handles all interrupt events from device - * - * These devices deploy unified interrupt status registers meaning - * all interrupts must be handled together - **/ -static int sca3000_handler_th(struct iio_dev *dev_info, -			      int index, -			      s64 timestamp, -			      int no_test) -{ -	struct sca3000_state *st = dev_info->dev_data; - -	st->last_timestamp = timestamp; -	schedule_work(&st->interrupt_handler_ws); - -	return 0; +	return IRQ_HANDLED;  }  /** - * sca3000_query_mo_det() is motion detection enabled for this axis - * - * First queries if motion detection is enabled and then if this axis is - * on. + * sca3000_read_event_config() what events are enabled   **/ -static ssize_t sca3000_query_mo_det(struct device *dev, -				    struct device_attribute *attr, -				    char *buf) +static int sca3000_read_event_config(struct iio_dev *indio_dev, +				     const struct iio_chan_spec *chan, +				     enum iio_event_type type, +				     enum iio_event_direction dir)  { -	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; -	u8 *rx; +	struct sca3000_state *st = iio_priv(indio_dev); +	int ret;  	u8 protect_mask = 0x03; +	int num = chan->channel2;  	/* read current value of mode register */  	mutex_lock(&st->lock); -	ret = sca3000_read_data(st, SCA3000_REG_ADDR_MODE, &rx, 1); +	ret = sca3000_read_data_short(st, SCA3000_REG_ADDR_MODE, 1);  	if (ret)  		goto error_ret; -	if ((rx[1]&protect_mask) != SCA3000_MEAS_MODE_MOT_DET) -		len += sprintf(buf + len, "0\n"); +	if ((st->rx[0] & protect_mask) != SCA3000_MEAS_MODE_MOT_DET) +		ret = 0;  	else { -		kfree(rx); -		ret = sca3000_read_ctrl_reg(st, -					    SCA3000_REG_CTRL_SEL_MD_CTRL, -					    &rx); -		if (ret) +		ret = sca3000_read_ctrl_reg(st, SCA3000_REG_CTRL_SEL_MD_CTRL); +		if (ret < 0)  			goto error_ret;  		/* only supporting logical or's for now */ -		len += sprintf(buf + len, "%d\n", -			       (rx[1] & this_attr->mask) ? 1 : 0); +		ret = !!(ret & sca3000_addresses[num][2]);  	} -	kfree(rx);  error_ret:  	mutex_unlock(&st->lock); -	return ret ? ret : len; +	return ret;  }  /**   * sca3000_query_free_fall_mode() is free fall mode enabled @@ -973,80 +894,20 @@ static ssize_t sca3000_query_free_fall_mode(struct device *dev,  					    char *buf)  {  	int ret, len; -	u8 *rx; -	struct iio_dev *indio_dev = dev_get_drvdata(dev); -	struct sca3000_state *st = indio_dev->dev_data; +	struct iio_dev *indio_dev = dev_to_iio_dev(dev); +	struct sca3000_state *st = iio_priv(indio_dev); +	int val;  	mutex_lock(&st->lock); -	ret = sca3000_read_data(st, SCA3000_REG_ADDR_MODE, &rx, 1); +	ret = sca3000_read_data_short(st, SCA3000_REG_ADDR_MODE, 1); +	val = st->rx[0];  	mutex_unlock(&st->lock); -	if (ret) +	if (ret < 0)  		return ret;  	len = sprintf(buf, "%d\n", -		      !!(rx[1] & SCA3000_FREE_FALL_DETECT)); -	kfree(rx); - -	return len; -} -/** - * sca3000_query_ring_int() is the hardware ring status interrupt enabled - **/ -static ssize_t sca3000_query_ring_int(struct device *dev, -				      struct device_attribute *attr, -				      char *buf) -{ -	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->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); -	mutex_unlock(&st->lock); -	if (ret) -		return ret; -	len = sprintf(buf, "%d\n", (rx[1] & this_attr->mask) ? 1 : 0); -	kfree(rx); - +		      !!(val & SCA3000_FREE_FALL_DETECT));  	return len;  } -/** - * sca3000_set_ring_int() set state of ring status interrupt - **/ -static ssize_t sca3000_set_ring_int(struct device *dev, -				      struct device_attribute *attr, -				      const char *buf, -				      size_t len) -{ -	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; -	int ret; -	u8 *rx; - -	mutex_lock(&st->lock); -	ret = strict_strtol(buf, 10, &val); -	if (ret) -		goto error_ret; -	ret = sca3000_read_data(st, SCA3000_REG_ADDR_INT_MASK, &rx, 1); -	if (ret) -		goto error_ret; -	if (val) -		ret = sca3000_write_reg(st, -					SCA3000_REG_ADDR_INT_MASK, -					rx[1] | this_attr->mask); -	else -		ret = sca3000_write_reg(st, -					SCA3000_REG_ADDR_INT_MASK, -					rx[1] & ~this_attr->mask); -	kfree(rx); -error_ret: -	mutex_unlock(&st->lock); - -	return ret ? ret : len; -}  /**   * sca3000_set_free_fall_mode() simple on off control for free fall int @@ -1055,39 +916,35 @@ error_ret:   * the device falls more than 25cm.  This has not been tested due   * to fragile wiring.   **/ -  static ssize_t sca3000_set_free_fall_mode(struct device *dev,  					  struct device_attribute *attr,  					  const char *buf,  					  size_t len)  { -	struct iio_dev *indio_dev = dev_get_drvdata(dev); -	struct sca3000_state *st = indio_dev->dev_data; -	long val; +	struct iio_dev *indio_dev = dev_to_iio_dev(dev); +	struct sca3000_state *st = iio_priv(indio_dev); +	u8 val;  	int ret; -	u8 *rx;  	u8 protect_mask = SCA3000_FREE_FALL_DETECT;  	mutex_lock(&st->lock); -	ret = strict_strtol(buf, 10, &val); +	ret = kstrtou8(buf, 10, &val);  	if (ret)  		goto error_ret;  	/* read current value of mode register */ -	ret = sca3000_read_data(st, SCA3000_REG_ADDR_MODE, &rx, 1); +	ret = sca3000_read_data_short(st, SCA3000_REG_ADDR_MODE, 1);  	if (ret)  		goto error_ret; -	/*if off and should be on*/ -	if (val && !(rx[1] & protect_mask)) +	/* if off and should be on */ +	if (val && !(st->rx[0] & protect_mask))  		ret = sca3000_write_reg(st, SCA3000_REG_ADDR_MODE, -					(rx[1] | SCA3000_FREE_FALL_DETECT)); +					(st->rx[0] | SCA3000_FREE_FALL_DETECT));  	/* if on and should be off */ -	else if (!val && (rx[1]&protect_mask)) +	else if (!val && (st->rx[0] & protect_mask))  		ret = sca3000_write_reg(st, SCA3000_REG_ADDR_MODE, -					(rx[1] & ~protect_mask)); - -	kfree(rx); +					(st->rx[0] & ~protect_mask));  error_ret:  	mutex_unlock(&st->lock); @@ -1095,7 +952,7 @@ error_ret:  }  /** - * sca3000_set_mo_det() simple on off control for motion detector + * sca3000_write_event_config() simple on off control for motion detector   *   * This is a per axis control, but enabling any will result in the   * motion detector unit being enabled. @@ -1103,132 +960,87 @@ error_ret:   * There is a complexity in knowing which mode to return to when   * this mode is disabled.  Currently normal mode is assumed.   **/ -static ssize_t sca3000_set_mo_det(struct device *dev, -				  struct device_attribute *attr, -				  const char *buf, -				  size_t len) +static int sca3000_write_event_config(struct iio_dev *indio_dev, +				      const struct iio_chan_spec *chan, +				      enum iio_event_type type, +				      enum iio_event_direction dir, +				      int state)  { -	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; -	int ret; -	u8 *rx; +	struct sca3000_state *st = iio_priv(indio_dev); +	int ret, ctrlval;  	u8 protect_mask = 0x03; -	ret = strict_strtol(buf, 10, &val); -	if (ret) -		return ret; +	int num = chan->channel2;  	mutex_lock(&st->lock); -	/* First read the motion detector config to find out if -	 * this axis is on*/ -	ret = sca3000_read_ctrl_reg(st, -				    SCA3000_REG_CTRL_SEL_MD_CTRL, -				    &rx); -	if (ret) +	/* +	 * First read the motion detector config to find out if +	 * this axis is on +	 */ +	ret = sca3000_read_ctrl_reg(st, SCA3000_REG_CTRL_SEL_MD_CTRL); +	if (ret < 0)  		goto exit_point; -	/* Off and should be on */ -	if (val && !(rx[1] & this_attr->mask)) { +	ctrlval = ret; +	/* if off and should be on */ +	if (state && !(ctrlval & sca3000_addresses[num][2])) {  		ret = sca3000_write_ctrl_reg(st,  					     SCA3000_REG_CTRL_SEL_MD_CTRL, -					     rx[1] | this_attr->mask); +					     ctrlval | +					     sca3000_addresses[num][2]);  		if (ret) -			goto exit_point_free_rx; +			goto exit_point;  		st->mo_det_use_count++; -	} else if (!val && (rx[1]&this_attr->mask)) { +	} else if (!state && (ctrlval & sca3000_addresses[num][2])) {  		ret = sca3000_write_ctrl_reg(st,  					     SCA3000_REG_CTRL_SEL_MD_CTRL, -					     rx[1] & ~(this_attr->mask)); +					     ctrlval & +					     ~(sca3000_addresses[num][2]));  		if (ret) -			goto exit_point_free_rx; +			goto exit_point;  		st->mo_det_use_count--; -	} else /* relies on clean state for device on boot */ -		goto exit_point_free_rx; -	kfree(rx); +	} +  	/* read current value of mode register */ -	ret = sca3000_read_data(st, SCA3000_REG_ADDR_MODE, &rx, 1); +	ret = sca3000_read_data_short(st, SCA3000_REG_ADDR_MODE, 1);  	if (ret)  		goto exit_point; -	/*if off and should be on*/ +	/* if off and should be on */  	if ((st->mo_det_use_count) -	    && ((rx[1]&protect_mask) != SCA3000_MEAS_MODE_MOT_DET)) +	    && ((st->rx[0] & protect_mask) != SCA3000_MEAS_MODE_MOT_DET))  		ret = sca3000_write_reg(st, SCA3000_REG_ADDR_MODE, -					(rx[1] & ~protect_mask) +					(st->rx[0] & ~protect_mask)  					| SCA3000_MEAS_MODE_MOT_DET);  	/* if on and should be off */  	else if (!(st->mo_det_use_count) -		 && ((rx[1]&protect_mask) == SCA3000_MEAS_MODE_MOT_DET)) +		 && ((st->rx[0] & protect_mask) == SCA3000_MEAS_MODE_MOT_DET))  		ret = sca3000_write_reg(st, SCA3000_REG_ADDR_MODE, -					(rx[1] & ~protect_mask)); -exit_point_free_rx: -	kfree(rx); +					(st->rx[0] & ~protect_mask));  exit_point:  	mutex_unlock(&st->lock); -	return ret ? ret : len; +	return ret;  } -/* Shared event handler for all events as single event status register */ -IIO_EVENT_SH(all, &sca3000_handler_th); -  /* Free fall detector related event attribute */ -IIO_EVENT_ATTR_NAMED_SH(accel_xayaz_mag_falling_en, -			accel_x&y&z_mag_falling_en, -			iio_event_all, -			sca3000_query_free_fall_mode, -			sca3000_set_free_fall_mode, -			0); - -IIO_CONST_ATTR_NAMED(accel_xayaz_mag_falling_period, -		     accel_x&y&z_mag_falling_period, -		     "0.226"); - -/* Motion detector related event attributes */ -IIO_EVENT_ATTR_SH(accel_x_mag_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_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_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, -			       sca3000_query_ring_int, -			       sca3000_set_ring_int, -			       SCA3000_INT_MASK_RING_HALF); - -IIO_EVENT_ATTR_RING_75_FULL_SH(iio_event_all, -			       sca3000_query_ring_int, -			       sca3000_set_ring_int, -			       SCA3000_INT_MASK_RING_THREE_QUARTER); +static IIO_DEVICE_ATTR_NAMED(accel_xayaz_mag_falling_en, +			     in_accel_x&y&z_mag_falling_en, +			     S_IRUGO | S_IWUSR, +			     sca3000_query_free_fall_mode, +			     sca3000_set_free_fall_mode, +			     0); + +static IIO_CONST_ATTR_NAMED(accel_xayaz_mag_falling_period, +			    in_accel_x&y&z_mag_falling_period, +			    "0.226");  static struct attribute *sca3000_event_attributes[] = { -	&iio_event_attr_accel_xayaz_mag_falling_en.dev_attr.attr, +	&iio_dev_attr_accel_xayaz_mag_falling_en.dev_attr.attr,  	&iio_const_attr_accel_xayaz_mag_falling_period.dev_attr.attr, -	&iio_event_attr_accel_x_mag_rising_en.dev_attr.attr, -	&iio_dev_attr_accel_x_raw_mag_rising_value.dev_attr.attr, -	&iio_event_attr_accel_y_mag_rising_en.dev_attr.attr, -	&iio_dev_attr_accel_y_raw_mag_rising_value.dev_attr.attr, -	&iio_event_attr_accel_z_mag_rising_en.dev_attr.attr, -	&iio_dev_attr_accel_z_raw_mag_rising_value.dev_attr.attr, -	&iio_event_attr_ring_50_full.dev_attr.attr, -	&iio_event_attr_ring_75_full.dev_attr.attr,  	NULL,  };  static struct attribute_group sca3000_event_attribute_group = {  	.attrs = sca3000_event_attributes, +	.name = "events",  };  /** @@ -1237,74 +1049,56 @@ static struct attribute_group sca3000_event_attribute_group = {   * Devices use flash memory to store many of the register values   * and hence can come up in somewhat unpredictable states.   * Hence reset everything on driver load. -  **/ + **/  static int sca3000_clean_setup(struct sca3000_state *st)  {  	int ret; -	u8 *rx;  	mutex_lock(&st->lock);  	/* Ensure all interrupts have been acknowledged */ -	ret = sca3000_read_data(st, SCA3000_REG_ADDR_INT_STATUS, &rx, 1); +	ret = sca3000_read_data_short(st, SCA3000_REG_ADDR_INT_STATUS, 1);  	if (ret)  		goto error_ret; -	kfree(rx);  	/* Turn off all motion detection channels */ -	ret = sca3000_read_ctrl_reg(st, -				    SCA3000_REG_CTRL_SEL_MD_CTRL, -				    &rx); -	if (ret) +	ret = sca3000_read_ctrl_reg(st, SCA3000_REG_CTRL_SEL_MD_CTRL); +	if (ret < 0)  		goto error_ret; -	ret = sca3000_write_ctrl_reg(st, -				     SCA3000_REG_CTRL_SEL_MD_CTRL, -				     rx[1] & SCA3000_MD_CTRL_PROT_MASK); -	kfree(rx); +	ret = sca3000_write_ctrl_reg(st, SCA3000_REG_CTRL_SEL_MD_CTRL, +				     ret & SCA3000_MD_CTRL_PROT_MASK);  	if (ret)  		goto error_ret;  	/* Disable ring buffer */ -	sca3000_read_ctrl_reg(st, -			      SCA3000_REG_CTRL_SEL_OUT_CTRL, -			      &rx); -	/* Frequency of ring buffer sampling deliberately restricted to make -	 * debugging easier - add control of this later */ -	ret = sca3000_write_ctrl_reg(st, -				     SCA3000_REG_CTRL_SEL_OUT_CTRL, -				     (rx[1] & SCA3000_OUT_CTRL_PROT_MASK) +	ret = sca3000_read_ctrl_reg(st, SCA3000_REG_CTRL_SEL_OUT_CTRL); +	ret = sca3000_write_ctrl_reg(st, SCA3000_REG_CTRL_SEL_OUT_CTRL, +				     (ret & SCA3000_OUT_CTRL_PROT_MASK)  				     | SCA3000_OUT_CTRL_BUF_X_EN  				     | SCA3000_OUT_CTRL_BUF_Y_EN  				     | SCA3000_OUT_CTRL_BUF_Z_EN  				     | SCA3000_OUT_CTRL_BUF_DIV_4); -	kfree(rx); -  	if (ret)  		goto error_ret;  	/* Enable interrupts, relevant to mode and set up as active low */ -	ret = sca3000_read_data(st, -			  SCA3000_REG_ADDR_INT_MASK, -			  &rx, 1); +	ret = sca3000_read_data_short(st, SCA3000_REG_ADDR_INT_MASK, 1);  	if (ret)  		goto error_ret;  	ret = sca3000_write_reg(st,  				SCA3000_REG_ADDR_INT_MASK, -				(rx[1] & SCA3000_INT_MASK_PROT_MASK) +				(ret & SCA3000_INT_MASK_PROT_MASK)  				| SCA3000_INT_MASK_ACTIVE_LOW); -	kfree(rx);  	if (ret)  		goto error_ret; -	/* Select normal measurement mode, free fall off, ring off */ -	/* Ring in 12 bit mode - it is fine to overwrite reserved bits 3,5 -	 * as that occurs in one of the example on the datasheet */ -	ret = sca3000_read_data(st, -			  SCA3000_REG_ADDR_MODE, -			  &rx, 1); +	/* +	 * Select normal measurement mode, free fall off, ring off +	 * Ring in 12 bit mode - it is fine to overwrite reserved bits 3,5 +	 * as that occurs in one of the example on the datasheet +	 */ +	ret = sca3000_read_data_short(st, SCA3000_REG_ADDR_MODE, 1);  	if (ret)  		goto error_ret; -	ret = sca3000_write_reg(st, -				SCA3000_REG_ADDR_MODE, -				(rx[1] & SCA3000_MODE_PROT_MASK)); -	kfree(rx); +	ret = sca3000_write_reg(st, SCA3000_REG_ADDR_MODE, +				(st->rx[0] & SCA3000_MODE_PROT_MASK));  	st->bpse = 11;  error_ret: @@ -1312,218 +1106,143 @@ error_ret:  	return ret;  } -static int __devinit __sca3000_probe(struct spi_device *spi, -				     enum sca3000_variant variant) +static const struct iio_info sca3000_info = { +	.attrs = &sca3000_attribute_group, +	.read_raw = &sca3000_read_raw, +	.event_attrs = &sca3000_event_attribute_group, +	.read_event_value = &sca3000_read_thresh, +	.write_event_value = &sca3000_write_thresh, +	.read_event_config = &sca3000_read_event_config, +	.write_event_config = &sca3000_write_event_config, +	.driver_module = THIS_MODULE, +}; + +static int sca3000_probe(struct spi_device *spi)  { -	int ret, regdone = 0; +	int ret;  	struct sca3000_state *st; +	struct iio_dev *indio_dev; -	st = kzalloc(sizeof(struct sca3000_state), GFP_KERNEL); -	if (st == NULL) { -		ret = -ENOMEM; -		goto error_ret; -	} -	spi_set_drvdata(spi, st); +	indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); +	if (!indio_dev) +		return -ENOMEM; -	st->tx = kmalloc(sizeof(*st->tx)*6, GFP_KERNEL); -	if (st->tx == NULL) { -		ret = -ENOMEM; -		goto error_clear_st; -	} -	st->rx = kmalloc(sizeof(*st->rx)*3, GFP_KERNEL); -	if (st->rx == NULL) { -		ret = -ENOMEM; -		goto error_free_tx; -	} +	st = iio_priv(indio_dev); +	spi_set_drvdata(spi, indio_dev);  	st->us = spi;  	mutex_init(&st->lock); -	st->info = &sca3000_spi_chip_info_tbl[variant]; - -	st->indio_dev = iio_allocate_device(); -	if (st->indio_dev == NULL) { -		ret = -ENOMEM; -		goto error_free_rx; +	st->info = &sca3000_spi_chip_info_tbl[spi_get_device_id(spi) +					      ->driver_data]; + +	indio_dev->dev.parent = &spi->dev; +	indio_dev->name = spi_get_device_id(spi)->name; +	indio_dev->info = &sca3000_info; +	if (st->info->temp_output) { +		indio_dev->channels = sca3000_channels_with_temp; +		indio_dev->num_channels = +			ARRAY_SIZE(sca3000_channels_with_temp); +	} else { +		indio_dev->channels = sca3000_channels; +		indio_dev->num_channels = ARRAY_SIZE(sca3000_channels);  	} +	indio_dev->modes = INDIO_DIRECT_MODE; -	st->indio_dev->dev.parent = &spi->dev; -	st->indio_dev->num_interrupt_lines = 1; -	st->indio_dev->event_attrs = &sca3000_event_attribute_group; -	if (st->info->temp_output) -		st->indio_dev->attrs = &sca3000_attribute_group_with_temp; -	else -		st->indio_dev->attrs = &sca3000_attribute_group; -	st->indio_dev->dev_data = (void *)(st); -	st->indio_dev->modes = INDIO_DIRECT_MODE; - -	sca3000_configure_ring(st->indio_dev); - -	ret = iio_device_register(st->indio_dev); +	sca3000_configure_ring(indio_dev); +	ret = iio_device_register(indio_dev);  	if (ret < 0) -		goto error_free_dev; -	regdone = 1; -	ret = iio_ring_buffer_register(st->indio_dev->ring, 0); +		return ret; + +	ret = iio_buffer_register(indio_dev, +				  sca3000_channels, +				  ARRAY_SIZE(sca3000_channels));  	if (ret < 0)  		goto error_unregister_dev; -	if (spi->irq && gpio_is_valid(irq_to_gpio(spi->irq)) > 0) { -		INIT_WORK(&st->interrupt_handler_ws, -			  sca3000_interrupt_handler_bh); -		ret = iio_register_interrupt_line(spi->irq, -						  st->indio_dev, -						  0, -						  IRQF_TRIGGER_FALLING, -						  "sca3000"); +	if (indio_dev->buffer) { +		iio_scan_mask_set(indio_dev, indio_dev->buffer, 0); +		iio_scan_mask_set(indio_dev, indio_dev->buffer, 1); +		iio_scan_mask_set(indio_dev, indio_dev->buffer, 2); +	} + +	if (spi->irq) { +		ret = request_threaded_irq(spi->irq, +					   NULL, +					   &sca3000_event_handler, +					   IRQF_TRIGGER_FALLING | IRQF_ONESHOT, +					   "sca3000", +					   indio_dev);  		if (ret)  			goto error_unregister_ring; -		/* RFC -		 * Probably a common situation.  All interrupts need an ack -		 * and there is only one handler so the complicated list system -		 * is overkill.  At very least a simpler registration method -		 * might be worthwhile. -		 */ -		iio_add_event_to_list( -			iio_event_attr_accel_z_mag_rising_en.listel, -			&st->indio_dev -			->interrupts[0]->ev_list);  	} -	sca3000_register_ring_funcs(st->indio_dev); +	sca3000_register_ring_funcs(indio_dev);  	ret = sca3000_clean_setup(st);  	if (ret) -		goto error_unregister_interrupt_line; +		goto error_free_irq;  	return 0; -error_unregister_interrupt_line: -	if (spi->irq && gpio_is_valid(irq_to_gpio(spi->irq)) > 0) -		iio_unregister_interrupt_line(st->indio_dev, 0); +error_free_irq: +	if (spi->irq) +		free_irq(spi->irq, indio_dev);  error_unregister_ring: -	iio_ring_buffer_unregister(st->indio_dev->ring); +	iio_buffer_unregister(indio_dev);  error_unregister_dev: -error_free_dev: -	if (regdone) -		iio_device_unregister(st->indio_dev); -	else -		iio_free_device(st->indio_dev); -error_free_rx: -	kfree(st->rx); -error_free_tx: -	kfree(st->tx); -error_clear_st: -	kfree(st); -error_ret: +	iio_device_unregister(indio_dev);  	return ret;  }  static int sca3000_stop_all_interrupts(struct sca3000_state *st)  {  	int ret; -	u8 *rx;  	mutex_lock(&st->lock); -	ret = sca3000_read_data(st, SCA3000_REG_ADDR_INT_MASK, &rx, 1); +	ret = sca3000_read_data_short(st, SCA3000_REG_ADDR_INT_MASK, 1);  	if (ret)  		goto error_ret;  	ret = sca3000_write_reg(st, SCA3000_REG_ADDR_INT_MASK, -				(rx[1] & ~(SCA3000_INT_MASK_RING_THREE_QUARTER -					   | SCA3000_INT_MASK_RING_HALF -					   | SCA3000_INT_MASK_ALL_INTS))); +				(st->rx[0] & +				 ~(SCA3000_INT_MASK_RING_THREE_QUARTER | +				   SCA3000_INT_MASK_RING_HALF | +				   SCA3000_INT_MASK_ALL_INTS)));  error_ret: -	kfree(rx); +	mutex_unlock(&st->lock);  	return ret; -  }  static int sca3000_remove(struct spi_device *spi)  { -	struct sca3000_state *st =  spi_get_drvdata(spi); -	struct iio_dev *indio_dev = st->indio_dev; -	int ret; -	/* Must ensure no interrupts can be generated after this!*/ -	ret = sca3000_stop_all_interrupts(st); -	if (ret) -		return ret; -	if (spi->irq && gpio_is_valid(irq_to_gpio(spi->irq)) > 0) -		iio_unregister_interrupt_line(indio_dev, 0); -	iio_ring_buffer_unregister(indio_dev->ring); -	sca3000_unconfigure_ring(indio_dev); -	iio_device_unregister(indio_dev); - -	kfree(st->tx); -	kfree(st->rx); -	kfree(st); - -	return 0; -} - -/* These macros save on an awful lot of repeated code */ -#define SCA3000_VARIANT_PROBE(_name)				\ -	static int __devinit					\ -	sca3000_##_name##_probe(struct spi_device *spi)		\ -	{							\ -		return __sca3000_probe(spi, _name);		\ -	} - -#define SCA3000_VARIANT_SPI_DRIVER(_name)			\ -	struct spi_driver sca3000_##_name##_driver = {		\ -		.driver = {					\ -			.name = "sca3000_" #_name,		\ -			.owner = THIS_MODULE,			\ -		},						\ -		.probe = sca3000_##_name##_probe,		\ -		.remove = __devexit_p(sca3000_remove),		\ -	} - -SCA3000_VARIANT_PROBE(d01); -static SCA3000_VARIANT_SPI_DRIVER(d01); - -SCA3000_VARIANT_PROBE(e02); -static SCA3000_VARIANT_SPI_DRIVER(e02); - -SCA3000_VARIANT_PROBE(e04); -static SCA3000_VARIANT_SPI_DRIVER(e04); - -SCA3000_VARIANT_PROBE(e05); -static SCA3000_VARIANT_SPI_DRIVER(e05); - -static __init int sca3000_init(void) -{ -	int ret; +	struct iio_dev *indio_dev = spi_get_drvdata(spi); +	struct sca3000_state *st = iio_priv(indio_dev); -	ret = spi_register_driver(&sca3000_d01_driver); -	if (ret) -		goto error_ret; -	ret = spi_register_driver(&sca3000_e02_driver); -	if (ret) -		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; +	/* Must ensure no interrupts can be generated after this! */ +	sca3000_stop_all_interrupts(st); +	if (spi->irq) +		free_irq(spi->irq, indio_dev); +	iio_device_unregister(indio_dev); +	iio_buffer_unregister(indio_dev); +	sca3000_unconfigure_ring(indio_dev);  	return 0; - -error_unreg_e04: -	spi_unregister_driver(&sca3000_e04_driver); -error_unreg_e02: -	spi_unregister_driver(&sca3000_e02_driver); -error_unreg_d01: -	spi_unregister_driver(&sca3000_d01_driver); -error_ret: - -	return ret;  } -static __exit void sca3000_exit(void) -{ -	spi_unregister_driver(&sca3000_e05_driver); -	spi_unregister_driver(&sca3000_e04_driver); -	spi_unregister_driver(&sca3000_e02_driver); -	spi_unregister_driver(&sca3000_d01_driver); -} +static const struct spi_device_id sca3000_id[] = { +	{"sca3000_d01", d01}, +	{"sca3000_e02", e02}, +	{"sca3000_e04", e04}, +	{"sca3000_e05", e05}, +	{} +}; +MODULE_DEVICE_TABLE(spi, sca3000_id); -module_init(sca3000_init); -module_exit(sca3000_exit); +static struct spi_driver sca3000_driver = { +	.driver = { +		.name = "sca3000", +		.owner = THIS_MODULE, +	}, +	.probe = sca3000_probe, +	.remove = sca3000_remove, +	.id_table = sca3000_id, +}; +module_spi_driver(sca3000_driver); -MODULE_AUTHOR("Jonathan Cameron <jic23@cam.ac.uk>"); +MODULE_AUTHOR("Jonathan Cameron <jic23@kernel.org>");  MODULE_DESCRIPTION("VTI SCA3000 Series Accelerometers SPI driver");  MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/iio/accel/sca3000_ring.c b/drivers/staging/iio/accel/sca3000_ring.c index c872fddfb2b..198710651e0 100644 --- a/drivers/staging/iio/accel/sca3000_ring.c +++ b/drivers/staging/iio/accel/sca3000_ring.c @@ -5,24 +5,23 @@   * under the terms of the GNU General Public License version 2 as published by   * the Free Software Foundation.   * - * Copyright (c) 2009 Jonathan Cameron <jic23@cam.ac.uk> + * Copyright (c) 2009 Jonathan Cameron <jic23@kernel.org>   *   */  #include <linux/interrupt.h> -#include <linux/gpio.h>  #include <linux/fs.h> -#include <linux/device.h>  #include <linux/slab.h>  #include <linux/kernel.h>  #include <linux/spi/spi.h>  #include <linux/sysfs.h> +#include <linux/sched.h> +#include <linux/poll.h> -#include "../iio.h" -#include "../sysfs.h" -#include "../ring_generic.h" +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> +#include <linux/iio/buffer.h>  #include "../ring_hw.h" -#include "accel.h"  #include "sca3000.h"  /* RFC / future work @@ -34,26 +33,57 @@   * Currently scan elements aren't configured so it doesn't matter.   */ +static int sca3000_read_data(struct sca3000_state *st, +			    uint8_t reg_address_high, +			    u8 **rx_p, +			    int len) +{ +	int ret; +	struct spi_transfer xfer[2] = { +		{ +			.len = 1, +			.tx_buf = st->tx, +		}, { +			.len = len, +		} +	}; +	*rx_p = kmalloc(len, GFP_KERNEL); +	if (*rx_p == NULL) { +		ret = -ENOMEM; +		goto error_ret; +	} +	xfer[1].rx_buf = *rx_p; +	st->tx[0] = SCA3000_READ_REG(reg_address_high); +	ret = spi_sync_transfer(st->us, xfer, ARRAY_SIZE(xfer)); +	if (ret) { +		dev_err(get_device(&st->us->dev), "problem reading register"); +		goto error_free_rx; +	} + +	return 0; +error_free_rx: +	kfree(*rx_p); +error_ret: +	return ret; +} +  /** - * sca3000_rip_hw_rb() - main ring access function, pulls data from ring + * sca3000_read_first_n_hw_rb() - main ring access, pulls data from ring   * @r:			the ring   * @count:		number of samples to try and pull   * @data:		output the actual samples pulled from the hw ring - * @dead_offset:	cheating a bit here: Set to 1 so as to allow for the - *			leading byte used in bus comms.   *   * Currently does not provide timestamps.  As the hardware doesn't add them they - * can only be inferred aproximately from ring buffer events such as 50% full + * can only be inferred approximately from ring buffer events such as 50% full   * and knowledge of when buffer was last emptied.  This is left to userspace.   **/ -static int sca3000_rip_hw_rb(struct iio_ring_buffer *r, -			     size_t count, u8 **data, int *dead_offset) +static int sca3000_read_first_n_hw_rb(struct iio_buffer *r, +				      size_t count, char __user *buf)  { -	struct iio_hw_ring_buffer *hw_ring = iio_to_hw_ring_buf(r); +	struct iio_hw_buffer *hw_ring = iio_to_hw_buf(r);  	struct iio_dev *indio_dev = hw_ring->private; -	struct sca3000_state *st = indio_dev->dev_data; +	struct sca3000_state *st = iio_priv(indio_dev);  	u8 *rx; -	s16 *samples;  	int ret, i, num_available, num_read = 0;  	int bytes_per_sample = 1; @@ -61,44 +91,38 @@ static int sca3000_rip_hw_rb(struct iio_ring_buffer *r,  		bytes_per_sample = 2;  	mutex_lock(&st->lock); -	/* Check how much data is available: -	 * RFC: Implement an ioctl to not bother checking whether there -	 * is enough data in the ring?  Afterall, if we are responding -	 * to an interrupt we have a minimum content guaranteed so it -	 * seems slight silly to waste time checking it is there. -	 */ -	ret = sca3000_read_data(st, -				SCA3000_REG_ADDR_BUF_COUNT, -				&rx, 1); +	if (count % bytes_per_sample) { +		ret = -EINVAL; +		goto error_ret; +	} + +	ret = sca3000_read_data_short(st, SCA3000_REG_ADDR_BUF_COUNT, 1);  	if (ret)  		goto error_ret;  	else -		num_available = rx[1]; -	/* num_available is the total number of samples available +		num_available = st->rx[0]; +	/* +	 * num_available is the total number of samples available  	 * i.e. number of time points * number of channels.  	 */ -	kfree(rx);  	if (count > num_available * bytes_per_sample)  		num_read = num_available*bytes_per_sample;  	else -		num_read = count - (count % (bytes_per_sample)); +		num_read = count; -	/* Avoid the read request byte */ -	*dead_offset = 1;  	ret = sca3000_read_data(st,  				SCA3000_REG_ADDR_RING_OUT, -				data, num_read); - -	/* Convert byte order and shift to default resolution */ -	if (st->bpse == 11) { -		samples = (s16*)(*data+1); -		for (i = 0; i < (num_read/2); i++) { -			samples[i] = be16_to_cpup( -					(__be16 *)&(samples[i])); -			samples[i] >>= 3; -		} -	} +				&rx, num_read); +	if (ret) +		goto error_ret; + +	for (i = 0; i < num_read; i++) +		*(((u16 *)rx) + i) = be16_to_cpup((u16 *)rx + i); +	if (copy_to_user(buf, rx, num_read)) +		ret = -EFAULT; +	kfree(rx); +	r->stufftoread = 0;  error_ret:  	mutex_unlock(&st->lock); @@ -106,122 +130,102 @@ error_ret:  }  /* This is only valid with all 3 elements enabled */ -static int sca3000_ring_get_length(struct iio_ring_buffer *r) +static int sca3000_ring_get_length(struct iio_buffer *r)  {  	return 64;  }  /* only valid if resolution is kept at 11bits */ -static int sca3000_ring_get_bytes_per_datum(struct iio_ring_buffer *r) +static int sca3000_ring_get_bytes_per_datum(struct iio_buffer *r)  {  	return 6;  } -static void sca3000_ring_release(struct device *dev) -{ -	struct iio_ring_buffer *r = to_iio_ring_buffer(dev); -	kfree(iio_to_hw_ring_buf(r)); -} -static IIO_RING_ENABLE_ATTR; -static IIO_RING_BYTES_PER_DATUM_ATTR; -static IIO_RING_LENGTH_ATTR; +static IIO_BUFFER_ENABLE_ATTR; +static IIO_BUFFER_LENGTH_ATTR;  /** - * sca3000_show_ring_bpse() -sysfs function to query bits per sample from ring - * @dev: ring buffer device - * @attr: this device attribute - * @buf: buffer to write to + * sca3000_query_ring_int() is the hardware ring status interrupt enabled   **/ -static ssize_t sca3000_show_ring_bpse(struct device *dev, +static ssize_t sca3000_query_ring_int(struct device *dev,  				      struct device_attribute *attr,  				      char *buf)  { -	int len = 0, ret; -	u8 *rx; -	struct iio_ring_buffer *ring = dev_get_drvdata(dev); -	struct iio_dev *indio_dev = ring->indio_dev; -	struct sca3000_state *st = indio_dev->dev_data; +	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); +	int ret, val; +	struct iio_dev *indio_dev = dev_to_iio_dev(dev); +	struct sca3000_state *st = iio_priv(indio_dev);  	mutex_lock(&st->lock); -	ret = sca3000_read_data(st, SCA3000_REG_ADDR_MODE, &rx, 1); -	if (ret) -		goto error_ret; -	if (rx[1] & SCA3000_RING_BUF_8BIT) -		len = sprintf(buf, "s8/8\n"); -	else -		len = sprintf(buf, "s11/16\n"); -	kfree(rx); -error_ret: +	ret = sca3000_read_data_short(st, SCA3000_REG_ADDR_INT_MASK, 1); +	val = st->rx[0];  	mutex_unlock(&st->lock); +	if (ret) +		return ret; -	return ret ? ret : len; +	return sprintf(buf, "%d\n", !!(val & this_attr->address));  }  /** - * sca3000_store_ring_bpse() - bits per scan element - * @dev: ring buffer device - * @attr: attribute called from - * @buf: input from userspace - * @len: length of input + * sca3000_set_ring_int() set state of ring status interrupt   **/ -static ssize_t sca3000_store_ring_bpse(struct device *dev, +static ssize_t sca3000_set_ring_int(struct device *dev,  				      struct device_attribute *attr,  				      const char *buf,  				      size_t len)  { -	struct iio_ring_buffer *ring = dev_get_drvdata(dev); -	struct iio_dev *indio_dev = ring->indio_dev; -	struct sca3000_state *st = indio_dev->dev_data; +	struct iio_dev *indio_dev = dev_to_iio_dev(dev); +	struct sca3000_state *st = iio_priv(indio_dev); +	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); +	u8 val;  	int ret; -	u8 *rx;  	mutex_lock(&st->lock); - -	ret = sca3000_read_data(st, SCA3000_REG_ADDR_MODE, &rx, 1); +	ret = kstrtou8(buf, 10, &val);  	if (ret)  		goto error_ret; -	if (strncmp(buf, "s8/8", 4) == 0) { -		ret = sca3000_write_reg(st, SCA3000_REG_ADDR_MODE, -					rx[1] | SCA3000_RING_BUF_8BIT); -		st->bpse = 8; -	} else if (strncmp(buf, "s11/16", 5) == 0) { -		ret = sca3000_write_reg(st, SCA3000_REG_ADDR_MODE, -					rx[1] & ~SCA3000_RING_BUF_8BIT); -		st->bpse = 11; -	} else -		ret = -EINVAL; +	ret = sca3000_read_data_short(st, SCA3000_REG_ADDR_INT_MASK, 1); +	if (ret) +		goto error_ret; +	if (val) +		ret = sca3000_write_reg(st, +					SCA3000_REG_ADDR_INT_MASK, +					st->rx[0] | this_attr->address); +	else +		ret = sca3000_write_reg(st, +					SCA3000_REG_ADDR_INT_MASK, +					st->rx[0] & ~this_attr->address);  error_ret:  	mutex_unlock(&st->lock);  	return ret ? ret : len;  } -static IIO_SCAN_EL_C(accel_x, 0, 0, NULL); -static IIO_SCAN_EL_C(accel_y, 1, 0, NULL); -static IIO_SCAN_EL_C(accel_z, 2, 0, NULL); -static IIO_CONST_ATTR(accel_type_available, "s8/8 s11/16"); -static IIO_DEVICE_ATTR(accel_type, -		       S_IRUGO | S_IWUSR, -		       sca3000_show_ring_bpse, -		       sca3000_store_ring_bpse, -		       0); +static IIO_DEVICE_ATTR(50_percent, S_IRUGO | S_IWUSR, +		       sca3000_query_ring_int, +		       sca3000_set_ring_int, +		       SCA3000_INT_MASK_RING_HALF); -static struct attribute *sca3000_scan_el_attrs[] = { -	&iio_scan_el_accel_x.dev_attr.attr, -	&iio_const_attr_accel_x_index.dev_attr.attr, -	&iio_scan_el_accel_y.dev_attr.attr, -	&iio_const_attr_accel_y_index.dev_attr.attr, -	&iio_scan_el_accel_z.dev_attr.attr, -	&iio_const_attr_accel_z_index.dev_attr.attr, -	&iio_const_attr_accel_type_available.dev_attr.attr, -	&iio_dev_attr_accel_type.dev_attr.attr, -	NULL -}; +static IIO_DEVICE_ATTR(75_percent, S_IRUGO | S_IWUSR, +		       sca3000_query_ring_int, +		       sca3000_set_ring_int, +		       SCA3000_INT_MASK_RING_THREE_QUARTER); -static struct attribute_group sca3000_scan_el_group = { -	.attrs = sca3000_scan_el_attrs, -	.name = "scan_elements", -}; +static ssize_t sca3000_show_buffer_scale(struct device *dev, +					 struct device_attribute *attr, +					 char *buf) +{ +	struct iio_dev *indio_dev = dev_to_iio_dev(dev); +	struct sca3000_state *st = iio_priv(indio_dev); + +	return sprintf(buf, "0.%06d\n", 4*st->info->scale); +} + +static IIO_DEVICE_ATTR(in_accel_scale, +		       S_IRUGO, +		       sca3000_show_buffer_scale, +		       NULL, +		       0);  /*   * Ring buffer attributes @@ -231,91 +235,88 @@ static struct attribute_group sca3000_scan_el_group = {   */  static struct attribute *sca3000_ring_attributes[] = {  	&dev_attr_length.attr, -	&dev_attr_bytes_per_datum.attr,  	&dev_attr_enable.attr, +	&iio_dev_attr_50_percent.dev_attr.attr, +	&iio_dev_attr_75_percent.dev_attr.attr, +	&iio_dev_attr_in_accel_scale.dev_attr.attr,  	NULL,  };  static struct attribute_group sca3000_ring_attr = {  	.attrs = sca3000_ring_attributes, +	.name = "buffer",  }; -static const struct attribute_group *sca3000_ring_attr_groups[] = { -	&sca3000_ring_attr, -	NULL -}; - -static struct device_type sca3000_ring_type = { -	.release = sca3000_ring_release, -	.groups = sca3000_ring_attr_groups, -}; - -static struct iio_ring_buffer *sca3000_rb_allocate(struct iio_dev *indio_dev) +static struct iio_buffer *sca3000_rb_allocate(struct iio_dev *indio_dev)  { -	struct iio_ring_buffer *buf; -	struct iio_hw_ring_buffer *ring; +	struct iio_buffer *buf; +	struct iio_hw_buffer *ring; -	ring = kzalloc(sizeof *ring, GFP_KERNEL); +	ring = kzalloc(sizeof(*ring), GFP_KERNEL);  	if (!ring)  		return NULL; +  	ring->private = indio_dev;  	buf = &ring->buf; -	iio_ring_buffer_init(buf, indio_dev); -	buf->dev.type = &sca3000_ring_type; -	device_initialize(&buf->dev); -	buf->dev.parent = &indio_dev->dev; -	dev_set_drvdata(&buf->dev, (void *)buf); +	buf->stufftoread = 0; +	buf->attrs = &sca3000_ring_attr; +	iio_buffer_init(buf);  	return buf;  } -static inline void sca3000_rb_free(struct iio_ring_buffer *r) +static void sca3000_ring_release(struct iio_buffer *r)  { -	if (r) -		iio_put_ring_buffer(r); +	kfree(iio_to_hw_buf(r));  } +static const struct iio_buffer_access_funcs sca3000_ring_access_funcs = { +	.read_first_n = &sca3000_read_first_n_hw_rb, +	.get_length = &sca3000_ring_get_length, +	.get_bytes_per_datum = &sca3000_ring_get_bytes_per_datum, +	.release = sca3000_ring_release, +}; +  int sca3000_configure_ring(struct iio_dev *indio_dev)  { -	indio_dev->ring = sca3000_rb_allocate(indio_dev); -	if (indio_dev->ring == NULL) +	struct iio_buffer *buffer; + +	buffer = sca3000_rb_allocate(indio_dev); +	if (buffer == NULL)  		return -ENOMEM; -	indio_dev->modes |= INDIO_RING_HARDWARE_BUFFER; +	indio_dev->modes |= INDIO_BUFFER_HARDWARE; -	indio_dev->ring->scan_el_attrs = &sca3000_scan_el_group; -	indio_dev->ring->access.rip_lots = &sca3000_rip_hw_rb; -	indio_dev->ring->access.get_length = &sca3000_ring_get_length; -	indio_dev->ring->access.get_bytes_per_datum = &sca3000_ring_get_bytes_per_datum; +	indio_dev->buffer->access = &sca3000_ring_access_funcs; + +	iio_device_attach_buffer(indio_dev, buffer);  	return 0;  }  void sca3000_unconfigure_ring(struct iio_dev *indio_dev)  { -	sca3000_rb_free(indio_dev->ring); +	iio_buffer_put(indio_dev->buffer);  }  static inline  int __sca3000_hw_ring_state_set(struct iio_dev *indio_dev, bool state)  { -	struct sca3000_state *st = indio_dev->dev_data; +	struct sca3000_state *st = iio_priv(indio_dev);  	int ret; -	u8 *rx;  	mutex_lock(&st->lock); -	ret = sca3000_read_data(st, SCA3000_REG_ADDR_MODE, &rx, 1); +	ret = sca3000_read_data_short(st, SCA3000_REG_ADDR_MODE, 1);  	if (ret)  		goto error_ret;  	if (state) { -		printk(KERN_INFO "supposedly enabling ring buffer\n"); +		dev_info(&indio_dev->dev, "supposedly enabling ring buffer\n");  		ret = sca3000_write_reg(st,  					SCA3000_REG_ADDR_MODE, -					(rx[1] | SCA3000_RING_BUF_ENABLE)); +					(st->rx[0] | SCA3000_RING_BUF_ENABLE));  	} else  		ret = sca3000_write_reg(st,  					SCA3000_REG_ADDR_MODE, -					(rx[1] & ~SCA3000_RING_BUF_ENABLE)); -	kfree(rx); +					(st->rx[0] & ~SCA3000_RING_BUF_ENABLE));  error_ret:  	mutex_unlock(&st->lock); @@ -338,10 +339,14 @@ static int sca3000_hw_ring_postdisable(struct iio_dev *indio_dev)  	return __sca3000_hw_ring_state_set(indio_dev, 0);  } +static const struct iio_buffer_setup_ops sca3000_ring_setup_ops = { +	.preenable = &sca3000_hw_ring_preenable, +	.postdisable = &sca3000_hw_ring_postdisable, +}; +  void sca3000_register_ring_funcs(struct iio_dev *indio_dev)  { -	indio_dev->ring->preenable = &sca3000_hw_ring_preenable; -	indio_dev->ring->postdisable = &sca3000_hw_ring_postdisable; +	indio_dev->setup_ops = &sca3000_ring_setup_ops;  }  /** @@ -350,13 +355,11 @@ void sca3000_register_ring_funcs(struct iio_dev *indio_dev)   * This is only split from the main interrupt handler so as to   * reduce the amount of code if the ring buffer is not enabled.   **/ -void sca3000_ring_int_process(u8 val, struct iio_ring_buffer *ring) +void sca3000_ring_int_process(u8 val, struct iio_buffer *ring)  { -	if (val & SCA3000_INT_STATUS_THREE_QUARTERS) -		iio_push_or_escallate_ring_event(ring, -						 IIO_EVENT_CODE_RING_75_FULL, -						 0); -	else if (val & SCA3000_INT_STATUS_HALF) -		iio_push_ring_event(ring, -				    IIO_EVENT_CODE_RING_50_FULL, 0); +	if (val & (SCA3000_INT_STATUS_THREE_QUARTERS | +		   SCA3000_INT_STATUS_HALF)) { +		ring->stufftoread = true; +		wake_up_interruptible(&ring->pollq); +	}  } diff --git a/drivers/staging/iio/adc/Kconfig b/drivers/staging/iio/adc/Kconfig index acb67677e56..b87e382ad76 100644 --- a/drivers/staging/iio/adc/Kconfig +++ b/drivers/staging/iio/adc/Kconfig @@ -1,63 +1,117 @@  #  # ADC drivers  # -comment "Analog to digital convertors" +menu "Analog to digital converters" -config MAX1363 -	tristate "MAXIM max1363 ADC driver" +config AD7291 +	tristate "Analog Devices AD7291 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, 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" -	depends on MAX1363 -	select IIO_RING_BUFFER -	select IIO_SW_RING  	help -	  Say yes here to include ring buffer support in the MAX1363 -	  ADC driver. +	  Say yes here to build support for Analog Devices AD7291 +	  8 Channel ADC with temperature sensor. -config AD799X -	tristate "Analog Devices AD799x ADC driver" -	depends on I2C -	select IIO_TRIGGER if IIO_RING_BUFFER -	select AD799X_RING_BUFFER +config AD7606 +	tristate "Analog Devices AD7606 ADC driver" +	depends on GPIOLIB +	select IIO_BUFFER +	select IIO_TRIGGERED_BUFFER  	help  	  Say yes here to build support for Analog Devices: -	  ad7991, ad7995, ad7999, ad7992, ad7993, ad7994, ad7997, ad7998 -	  i2c analog to digital convertors (ADC). Provides direct access -	  via sysfs. +	  ad7606, ad7606-6, ad7606-4 analog to digital converters (ADC). + +	  To compile this driver as a module, choose M here: the +	  module will be called ad7606. + +config AD7606_IFACE_PARALLEL +	tristate "parallel interface support" +	depends on AD7606 +	help +	  Say yes here to include parallel interface support on the AD7606 +	  ADC driver. -config AD799X_RING_BUFFER -	bool "Analog Devices AD799x: use ring buffer" -	depends on AD799X -	select IIO_RING_BUFFER -	select IIO_SW_RING +config AD7606_IFACE_SPI +	tristate "spi interface support" +	depends on AD7606 +	depends on SPI  	help -	  Say yes here to include ring buffer support in the AD799X +	  Say yes here to include parallel interface support on the AD7606  	  ADC driver. -config AD7476 -	tristate "Analog Devices AD7475/6/7/8 AD7466/7/8 and AD7495 ADC driver" +config AD7780 +	tristate "Analog Devices AD7780 and similar ADCs driver"  	depends on SPI -	select IIO_RING_BUFFER	 -	select IIO_SW_RING -	select IIO_TRIGGER 	 +	depends on GPIOLIB +	select AD_SIGMA_DELTA  	help -	  Say yes here to build support for Analog Devices -	  AD7475, AD7476, AD7477, AD7478, AD7466, AD7467, AD7468, AD7495 -	  SPI analog to digital convertors (ADC). +	  Say yes here to build support for Analog Devices AD7170, AD7171, +	  AD7780 and AD7781 SPI analog to digital converters (ADC).  	  If unsure, say N (but it's safe to say "Y").  	  To compile this driver as a module, choose M here: the -	  module will be called ad7476. +	  module will be called ad7780. + +config AD7816 +	tristate "Analog Devices AD7816/7/8 temperature sensor and ADC driver" +	depends on SPI +	depends on GPIOLIB +	help +	  Say yes here to build support for Analog Devices AD7816/7/8 +	  temperature sensors and ADC. + +config AD7192 +	tristate "Analog Devices AD7190 AD7192 AD7195 ADC driver" +	depends on SPI +	select AD_SIGMA_DELTA +	help +	  Say yes here to build support for Analog Devices AD7190, +	  AD7192 or AD7195 SPI analog to digital converters (ADC). +	  If unsure, say N (but it's safe to say "Y"). + +	  To compile this driver as a module, choose M here: the +	  module will be called ad7192. + +config AD7280 +	tristate "Analog Devices AD7280A Lithium Ion Battery Monitoring System" +	depends on SPI +	help +	  Say yes here to build support for Analog Devices AD7280A +	  Lithium Ion Battery Monitoring System. + +	  To compile this driver as a module, choose M here: the +	  module will be called ad7280a + +config LPC32XX_ADC +	tristate "NXP LPC32XX ADC" +	depends on ARCH_LPC32XX || COMPILE_TEST +	depends on HAS_IOMEM +	help +	  Say yes here to build support for the integrated ADC inside the +	  LPC32XX SoC. Note that this feature uses the same hardware as the +	  touchscreen driver, so you should either select only one of the two +	  drivers (lpc32xx_adc or lpc32xx_ts) or, in the OpenFirmware case, +	  activate only one via device tree selection.  Provides direct access +	  via sysfs. + +config MXS_LRADC +	tristate "Freescale i.MX23/i.MX28 LRADC" +	depends on ARCH_MXS || COMPILE_TEST +	depends on INPUT +	select STMP_DEVICE +	select IIO_BUFFER +	select IIO_TRIGGERED_BUFFER +	help +	  Say yes here to build support for i.MX23/i.MX28 LRADC convertor +	  built into these chips. + +	  To compile this driver as a module, choose M here: the +	  module will be called mxs-lradc. + +config SPEAR_ADC +	tristate "ST SPEAr ADC" +	depends on PLAT_SPEAR || COMPILE_TEST +	depends on HAS_IOMEM +	help +	  Say yes here to build support for the integrated ADC inside the +	  ST SPEAr SoC. Provides direct access via sysfs. + +endmenu diff --git a/drivers/staging/iio/adc/Makefile b/drivers/staging/iio/adc/Makefile index b62c319bced..afdcd1ff08f 100644 --- a/drivers/staging/iio/adc/Makefile +++ b/drivers/staging/iio/adc/Makefile @@ -2,15 +2,17 @@  # Makefile for industrial I/O ADC drivers  # -max1363-y := max1363_core.o -max1363-y += max1363_ring.o +ad7606-y := ad7606_core.o +ad7606-$(CONFIG_IIO_BUFFER) += ad7606_ring.o +ad7606-$(CONFIG_AD7606_IFACE_PARALLEL) += ad7606_par.o +ad7606-$(CONFIG_AD7606_IFACE_SPI) += ad7606_spi.o +obj-$(CONFIG_AD7606) += ad7606.o -obj-$(CONFIG_MAX1363) += max1363.o - -ad799x-y := ad799x_core.o -ad799x-$(CONFIG_AD799X_RING_BUFFER) += ad799x_ring.o -obj-$(CONFIG_AD799X) += ad799x.o - -ad7476-y := ad7476_core.o -ad7476-$(CONFIG_IIO_RING_BUFFER) += ad7476_ring.o -obj-$(CONFIG_AD7476) += ad7476.o +obj-$(CONFIG_AD7291) += ad7291.o +obj-$(CONFIG_AD7780) += ad7780.o +obj-$(CONFIG_AD7816) += ad7816.o +obj-$(CONFIG_AD7192) += ad7192.o +obj-$(CONFIG_AD7280) += ad7280a.o +obj-$(CONFIG_LPC32XX_ADC) += lpc32xx_adc.o +obj-$(CONFIG_MXS_LRADC) += mxs-lradc.o +obj-$(CONFIG_SPEAR_ADC) += spear_adc.o diff --git a/drivers/staging/iio/adc/ad7192.c b/drivers/staging/iio/adc/ad7192.c new file mode 100644 index 00000000000..83bb44b3815 --- /dev/null +++ b/drivers/staging/iio/adc/ad7192.c @@ -0,0 +1,719 @@ +/* + * AD7190 AD7192 AD7195 SPI ADC driver + * + * Copyright 2011-2012 Analog Devices Inc. + * + * Licensed under the GPL-2. + */ + +#include <linux/interrupt.h> +#include <linux/device.h> +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/sysfs.h> +#include <linux/spi/spi.h> +#include <linux/regulator/consumer.h> +#include <linux/err.h> +#include <linux/sched.h> +#include <linux/delay.h> + +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> +#include <linux/iio/buffer.h> +#include <linux/iio/trigger.h> +#include <linux/iio/trigger_consumer.h> +#include <linux/iio/triggered_buffer.h> +#include <linux/iio/adc/ad_sigma_delta.h> + +#include "ad7192.h" + +/* Registers */ +#define AD7192_REG_COMM		0 /* Communications Register (WO, 8-bit) */ +#define AD7192_REG_STAT		0 /* Status Register	     (RO, 8-bit) */ +#define AD7192_REG_MODE		1 /* Mode Register	     (RW, 24-bit */ +#define AD7192_REG_CONF		2 /* Configuration Register  (RW, 24-bit) */ +#define AD7192_REG_DATA		3 /* Data Register	     (RO, 24/32-bit) */ +#define AD7192_REG_ID		4 /* ID Register	     (RO, 8-bit) */ +#define AD7192_REG_GPOCON	5 /* GPOCON Register	     (RO, 8-bit) */ +#define AD7192_REG_OFFSET	6 /* Offset Register	     (RW, 16-bit +				   * (AD7792)/24-bit (AD7192)) */ +#define AD7192_REG_FULLSALE	7 /* Full-Scale Register +				   * (RW, 16-bit (AD7792)/24-bit (AD7192)) */ + +/* Communications Register Bit Designations (AD7192_REG_COMM) */ +#define AD7192_COMM_WEN		(1 << 7) /* Write Enable */ +#define AD7192_COMM_WRITE	(0 << 6) /* Write Operation */ +#define AD7192_COMM_READ	(1 << 6) /* Read Operation */ +#define AD7192_COMM_ADDR(x)	(((x) & 0x7) << 3) /* Register Address */ +#define AD7192_COMM_CREAD	(1 << 2) /* Continuous Read of Data Register */ + +/* Status Register Bit Designations (AD7192_REG_STAT) */ +#define AD7192_STAT_RDY		(1 << 7) /* Ready */ +#define AD7192_STAT_ERR		(1 << 6) /* Error (Overrange, Underrange) */ +#define AD7192_STAT_NOREF	(1 << 5) /* Error no external reference */ +#define AD7192_STAT_PARITY	(1 << 4) /* Parity */ +#define AD7192_STAT_CH3		(1 << 2) /* Channel 3 */ +#define AD7192_STAT_CH2		(1 << 1) /* Channel 2 */ +#define AD7192_STAT_CH1		(1 << 0) /* Channel 1 */ + +/* Mode Register Bit Designations (AD7192_REG_MODE) */ +#define AD7192_MODE_SEL(x)	(((x) & 0x7) << 21) /* Operation Mode Select */ +#define AD7192_MODE_SEL_MASK	(0x7 << 21) /* Operation Mode Select Mask */ +#define AD7192_MODE_DAT_STA	(1 << 20) /* Status Register transmission */ +#define AD7192_MODE_CLKSRC(x)	(((x) & 0x3) << 18) /* Clock Source Select */ +#define AD7192_MODE_SINC3	(1 << 15) /* SINC3 Filter Select */ +#define AD7192_MODE_ACX		(1 << 14) /* AC excitation enable(AD7195 only)*/ +#define AD7192_MODE_ENPAR	(1 << 13) /* Parity Enable */ +#define AD7192_MODE_CLKDIV	(1 << 12) /* Clock divide by 2 (AD7190/2 only)*/ +#define AD7192_MODE_SCYCLE	(1 << 11) /* Single cycle conversion */ +#define AD7192_MODE_REJ60	(1 << 10) /* 50/60Hz notch filter */ +#define AD7192_MODE_RATE(x)	((x) & 0x3FF) /* Filter Update Rate Select */ + +/* Mode Register: AD7192_MODE_SEL options */ +#define AD7192_MODE_CONT		0 /* Continuous Conversion Mode */ +#define AD7192_MODE_SINGLE		1 /* Single Conversion Mode */ +#define AD7192_MODE_IDLE		2 /* Idle Mode */ +#define AD7192_MODE_PWRDN		3 /* Power-Down Mode */ +#define AD7192_MODE_CAL_INT_ZERO	4 /* Internal Zero-Scale Calibration */ +#define AD7192_MODE_CAL_INT_FULL	5 /* Internal Full-Scale Calibration */ +#define AD7192_MODE_CAL_SYS_ZERO	6 /* System Zero-Scale Calibration */ +#define AD7192_MODE_CAL_SYS_FULL	7 /* System Full-Scale Calibration */ + +/* Mode Register: AD7192_MODE_CLKSRC options */ +#define AD7192_CLK_EXT_MCLK1_2		0 /* External 4.92 MHz Clock connected +					   * from MCLK1 to MCLK2 */ +#define AD7192_CLK_EXT_MCLK2		1 /* External Clock applied to MCLK2 */ +#define AD7192_CLK_INT			2 /* Internal 4.92 MHz Clock not +					   * available at the MCLK2 pin */ +#define AD7192_CLK_INT_CO		3 /* Internal 4.92 MHz Clock available +					   * at the MCLK2 pin */ + + +/* Configuration Register Bit Designations (AD7192_REG_CONF) */ + +#define AD7192_CONF_CHOP	(1 << 23) /* CHOP enable */ +#define AD7192_CONF_REFSEL	(1 << 20) /* REFIN1/REFIN2 Reference Select */ +#define AD7192_CONF_CHAN(x)	(((1 << (x)) & 0xFF) << 8) /* Channel select */ +#define AD7192_CONF_CHAN_MASK	(0xFF << 8) /* Channel select mask */ +#define AD7192_CONF_BURN	(1 << 7) /* Burnout current enable */ +#define AD7192_CONF_REFDET	(1 << 6) /* Reference detect enable */ +#define AD7192_CONF_BUF		(1 << 4) /* Buffered Mode Enable */ +#define AD7192_CONF_UNIPOLAR	(1 << 3) /* Unipolar/Bipolar Enable */ +#define AD7192_CONF_GAIN(x)	((x) & 0x7) /* Gain Select */ + +#define AD7192_CH_AIN1P_AIN2M	0 /* AIN1(+) - AIN2(-) */ +#define AD7192_CH_AIN3P_AIN4M	1 /* AIN3(+) - AIN4(-) */ +#define AD7192_CH_TEMP		2 /* Temp Sensor */ +#define AD7192_CH_AIN2P_AIN2M	3 /* AIN2(+) - AIN2(-) */ +#define AD7192_CH_AIN1		4 /* AIN1 - AINCOM */ +#define AD7192_CH_AIN2		5 /* AIN2 - AINCOM */ +#define AD7192_CH_AIN3		6 /* AIN3 - AINCOM */ +#define AD7192_CH_AIN4		7 /* AIN4 - AINCOM */ + +/* ID Register Bit Designations (AD7192_REG_ID) */ +#define ID_AD7190		0x4 +#define ID_AD7192		0x0 +#define ID_AD7195		0x6 +#define AD7192_ID_MASK		0x0F + +/* GPOCON Register Bit Designations (AD7192_REG_GPOCON) */ +#define AD7192_GPOCON_BPDSW	(1 << 6) /* Bridge power-down switch enable */ +#define AD7192_GPOCON_GP32EN	(1 << 5) /* Digital Output P3 and P2 enable */ +#define AD7192_GPOCON_GP10EN	(1 << 4) /* Digital Output P1 and P0 enable */ +#define AD7192_GPOCON_P3DAT	(1 << 3) /* P3 state */ +#define AD7192_GPOCON_P2DAT	(1 << 2) /* P2 state */ +#define AD7192_GPOCON_P1DAT	(1 << 1) /* P1 state */ +#define AD7192_GPOCON_P0DAT	(1 << 0) /* P0 state */ + +#define AD7192_INT_FREQ_MHz	4915200 + +/* NOTE: + * The AD7190/2/5 features a dual use data out ready DOUT/RDY output. + * In order to avoid contentions on the SPI bus, it's therefore necessary + * to use spi bus locking. + * + * The DOUT/RDY output must also be wired to an interrupt capable GPIO. + */ + +struct ad7192_state { +	struct regulator		*reg; +	u16				int_vref_mv; +	u32				mclk; +	u32				f_order; +	u32				mode; +	u32				conf; +	u32				scale_avail[8][2]; +	u8				gpocon; +	u8				devid; + +	struct ad_sigma_delta		sd; +}; + +static struct ad7192_state *ad_sigma_delta_to_ad7192(struct ad_sigma_delta *sd) +{ +	return container_of(sd, struct ad7192_state, sd); +} + +static int ad7192_set_channel(struct ad_sigma_delta *sd, unsigned int channel) +{ +	struct ad7192_state *st = ad_sigma_delta_to_ad7192(sd); + +	st->conf &= ~AD7192_CONF_CHAN_MASK; +	st->conf |= AD7192_CONF_CHAN(channel); + +	return ad_sd_write_reg(&st->sd, AD7192_REG_CONF, 3, st->conf); +} + +static int ad7192_set_mode(struct ad_sigma_delta *sd, +			   enum ad_sigma_delta_mode mode) +{ +	struct ad7192_state *st = ad_sigma_delta_to_ad7192(sd); + +	st->mode &= ~AD7192_MODE_SEL_MASK; +	st->mode |= AD7192_MODE_SEL(mode); + +	return ad_sd_write_reg(&st->sd, AD7192_REG_MODE, 3, st->mode); +} + +static const struct ad_sigma_delta_info ad7192_sigma_delta_info = { +	.set_channel = ad7192_set_channel, +	.set_mode = ad7192_set_mode, +	.has_registers = true, +	.addr_shift = 3, +	.read_mask = BIT(6), +}; + +static const struct ad_sd_calib_data ad7192_calib_arr[8] = { +	{AD7192_MODE_CAL_INT_ZERO, AD7192_CH_AIN1}, +	{AD7192_MODE_CAL_INT_FULL, AD7192_CH_AIN1}, +	{AD7192_MODE_CAL_INT_ZERO, AD7192_CH_AIN2}, +	{AD7192_MODE_CAL_INT_FULL, AD7192_CH_AIN2}, +	{AD7192_MODE_CAL_INT_ZERO, AD7192_CH_AIN3}, +	{AD7192_MODE_CAL_INT_FULL, AD7192_CH_AIN3}, +	{AD7192_MODE_CAL_INT_ZERO, AD7192_CH_AIN4}, +	{AD7192_MODE_CAL_INT_FULL, AD7192_CH_AIN4} +}; + +static int ad7192_calibrate_all(struct ad7192_state *st) +{ +		return ad_sd_calibrate_all(&st->sd, ad7192_calib_arr, +				ARRAY_SIZE(ad7192_calib_arr)); +} + +static int ad7192_setup(struct ad7192_state *st, +	const struct ad7192_platform_data *pdata) +{ +	struct iio_dev *indio_dev = spi_get_drvdata(st->sd.spi); +	unsigned long long scale_uv; +	int i, ret, id; +	u8 ones[6]; + +	/* reset the serial interface */ +	memset(&ones, 0xFF, 6); +	ret = spi_write(st->sd.spi, &ones, 6); +	if (ret < 0) +		goto out; +	msleep(1); /* Wait for at least 500us */ + +	/* write/read test for device presence */ +	ret = ad_sd_read_reg(&st->sd, AD7192_REG_ID, 1, &id); +	if (ret) +		goto out; + +	id &= AD7192_ID_MASK; + +	if (id != st->devid) +		dev_warn(&st->sd.spi->dev, "device ID query failed (0x%X)\n", id); + +	switch (pdata->clock_source_sel) { +	case AD7192_CLK_EXT_MCLK1_2: +	case AD7192_CLK_EXT_MCLK2: +		st->mclk = AD7192_INT_FREQ_MHz; +		break; +	case AD7192_CLK_INT: +	case AD7192_CLK_INT_CO: +		if (pdata->ext_clk_Hz) +			st->mclk = pdata->ext_clk_Hz; +		else +			st->mclk = AD7192_INT_FREQ_MHz; +			break; +	default: +		ret = -EINVAL; +		goto out; +	} + +	st->mode = AD7192_MODE_SEL(AD7192_MODE_IDLE) | +		AD7192_MODE_CLKSRC(pdata->clock_source_sel) | +		AD7192_MODE_RATE(480); + +	st->conf = AD7192_CONF_GAIN(0); + +	if (pdata->rej60_en) +		st->mode |= AD7192_MODE_REJ60; + +	if (pdata->sinc3_en) +		st->mode |= AD7192_MODE_SINC3; + +	if (pdata->refin2_en && (st->devid != ID_AD7195)) +		st->conf |= AD7192_CONF_REFSEL; + +	if (pdata->chop_en) { +		st->conf |= AD7192_CONF_CHOP; +		if (pdata->sinc3_en) +			st->f_order = 3; /* SINC 3rd order */ +		else +			st->f_order = 4; /* SINC 4th order */ +	} else { +		st->f_order = 1; +	} + +	if (pdata->buf_en) +		st->conf |= AD7192_CONF_BUF; + +	if (pdata->unipolar_en) +		st->conf |= AD7192_CONF_UNIPOLAR; + +	if (pdata->burnout_curr_en) +		st->conf |= AD7192_CONF_BURN; + +	ret = ad_sd_write_reg(&st->sd, AD7192_REG_MODE, 3, st->mode); +	if (ret) +		goto out; + +	ret = ad_sd_write_reg(&st->sd, AD7192_REG_CONF, 3, st->conf); +	if (ret) +		goto out; + +	ret = ad7192_calibrate_all(st); +	if (ret) +		goto out; + +	/* Populate available ADC input ranges */ +	for (i = 0; i < ARRAY_SIZE(st->scale_avail); i++) { +		scale_uv = ((u64)st->int_vref_mv * 100000000) +			>> (indio_dev->channels[0].scan_type.realbits - +			((st->conf & AD7192_CONF_UNIPOLAR) ? 0 : 1)); +		scale_uv >>= i; + +		st->scale_avail[i][1] = do_div(scale_uv, 100000000) * 10; +		st->scale_avail[i][0] = scale_uv; +	} + +	return 0; +out: +	dev_err(&st->sd.spi->dev, "setup failed\n"); +	return ret; +} + +static ssize_t ad7192_read_frequency(struct device *dev, +		struct device_attribute *attr, +		char *buf) +{ +	struct iio_dev *indio_dev = dev_to_iio_dev(dev); +	struct ad7192_state *st = iio_priv(indio_dev); + +	return sprintf(buf, "%d\n", st->mclk / +			(st->f_order * 1024 * AD7192_MODE_RATE(st->mode))); +} + +static ssize_t ad7192_write_frequency(struct device *dev, +		struct device_attribute *attr, +		const char *buf, +		size_t len) +{ +	struct iio_dev *indio_dev = dev_to_iio_dev(dev); +	struct ad7192_state *st = iio_priv(indio_dev); +	unsigned long lval; +	int div, ret; + +	ret = kstrtoul(buf, 10, &lval); +	if (ret) +		return ret; +	if (lval == 0) +		return -EINVAL; + +	mutex_lock(&indio_dev->mlock); +	if (iio_buffer_enabled(indio_dev)) { +		mutex_unlock(&indio_dev->mlock); +		return -EBUSY; +	} + +	div = st->mclk / (lval * st->f_order * 1024); +	if (div < 1 || div > 1023) { +		ret = -EINVAL; +		goto out; +	} + +	st->mode &= ~AD7192_MODE_RATE(-1); +	st->mode |= AD7192_MODE_RATE(div); +	ad_sd_write_reg(&st->sd, AD7192_REG_MODE, 3, st->mode); + +out: +	mutex_unlock(&indio_dev->mlock); + +	return ret ? ret : len; +} + +static IIO_DEV_ATTR_SAMP_FREQ(S_IWUSR | S_IRUGO, +		ad7192_read_frequency, +		ad7192_write_frequency); + +static ssize_t ad7192_show_scale_available(struct device *dev, +			struct device_attribute *attr, char *buf) +{ +	struct iio_dev *indio_dev = dev_to_iio_dev(dev); +	struct ad7192_state *st = iio_priv(indio_dev); +	int i, len = 0; + +	for (i = 0; i < ARRAY_SIZE(st->scale_avail); i++) +		len += sprintf(buf + len, "%d.%09u ", st->scale_avail[i][0], +			       st->scale_avail[i][1]); + +	len += sprintf(buf + len, "\n"); + +	return len; +} + +static IIO_DEVICE_ATTR_NAMED(in_v_m_v_scale_available, +			     in_voltage-voltage_scale_available, +			     S_IRUGO, ad7192_show_scale_available, NULL, 0); + +static IIO_DEVICE_ATTR(in_voltage_scale_available, S_IRUGO, +		       ad7192_show_scale_available, NULL, 0); + +static ssize_t ad7192_show_ac_excitation(struct device *dev, +		struct device_attribute *attr, +		char *buf) +{ +	struct iio_dev *indio_dev = dev_to_iio_dev(dev); +	struct ad7192_state *st = iio_priv(indio_dev); + +	return sprintf(buf, "%d\n", !!(st->mode & AD7192_MODE_ACX)); +} + +static ssize_t ad7192_show_bridge_switch(struct device *dev, +		struct device_attribute *attr, +		char *buf) +{ +	struct iio_dev *indio_dev = dev_to_iio_dev(dev); +	struct ad7192_state *st = iio_priv(indio_dev); + +	return sprintf(buf, "%d\n", !!(st->gpocon & AD7192_GPOCON_BPDSW)); +} + +static ssize_t ad7192_set(struct device *dev, +		struct device_attribute *attr, +		const char *buf, +		size_t len) +{ +	struct iio_dev *indio_dev = dev_to_iio_dev(dev); +	struct ad7192_state *st = iio_priv(indio_dev); +	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); +	int ret; +	bool val; + +	ret = strtobool(buf, &val); +	if (ret < 0) +		return ret; + +	mutex_lock(&indio_dev->mlock); +	if (iio_buffer_enabled(indio_dev)) { +		mutex_unlock(&indio_dev->mlock); +		return -EBUSY; +	} + +	switch ((u32) this_attr->address) { +	case AD7192_REG_GPOCON: +		if (val) +			st->gpocon |= AD7192_GPOCON_BPDSW; +		else +			st->gpocon &= ~AD7192_GPOCON_BPDSW; + +		ad_sd_write_reg(&st->sd, AD7192_REG_GPOCON, 1, st->gpocon); +		break; +	case AD7192_REG_MODE: +		if (val) +			st->mode |= AD7192_MODE_ACX; +		else +			st->mode &= ~AD7192_MODE_ACX; + +		ad_sd_write_reg(&st->sd, AD7192_REG_MODE, 3, st->mode); +		break; +	default: +		ret = -EINVAL; +	} + +	mutex_unlock(&indio_dev->mlock); + +	return ret ? ret : len; +} + +static IIO_DEVICE_ATTR(bridge_switch_en, S_IRUGO | S_IWUSR, +		       ad7192_show_bridge_switch, ad7192_set, +		       AD7192_REG_GPOCON); + +static IIO_DEVICE_ATTR(ac_excitation_en, S_IRUGO | S_IWUSR, +		       ad7192_show_ac_excitation, ad7192_set, +		       AD7192_REG_MODE); + +static struct attribute *ad7192_attributes[] = { +	&iio_dev_attr_sampling_frequency.dev_attr.attr, +	&iio_dev_attr_in_v_m_v_scale_available.dev_attr.attr, +	&iio_dev_attr_in_voltage_scale_available.dev_attr.attr, +	&iio_dev_attr_bridge_switch_en.dev_attr.attr, +	&iio_dev_attr_ac_excitation_en.dev_attr.attr, +	NULL +}; + +static const struct attribute_group ad7192_attribute_group = { +	.attrs = ad7192_attributes, +}; + +static struct attribute *ad7195_attributes[] = { +	&iio_dev_attr_sampling_frequency.dev_attr.attr, +	&iio_dev_attr_in_v_m_v_scale_available.dev_attr.attr, +	&iio_dev_attr_in_voltage_scale_available.dev_attr.attr, +	&iio_dev_attr_bridge_switch_en.dev_attr.attr, +	NULL +}; + +static const struct attribute_group ad7195_attribute_group = { +	.attrs = ad7195_attributes, +}; + +static unsigned int ad7192_get_temp_scale(bool unipolar) +{ +	return unipolar ? 2815 * 2 : 2815; +} + +static int ad7192_read_raw(struct iio_dev *indio_dev, +			   struct iio_chan_spec const *chan, +			   int *val, +			   int *val2, +			   long m) +{ +	struct ad7192_state *st = iio_priv(indio_dev); +	bool unipolar = !!(st->conf & AD7192_CONF_UNIPOLAR); + +	switch (m) { +	case IIO_CHAN_INFO_RAW: +		return ad_sigma_delta_single_conversion(indio_dev, chan, val); +	case IIO_CHAN_INFO_SCALE: +		switch (chan->type) { +		case IIO_VOLTAGE: +			mutex_lock(&indio_dev->mlock); +			*val = st->scale_avail[AD7192_CONF_GAIN(st->conf)][0]; +			*val2 = st->scale_avail[AD7192_CONF_GAIN(st->conf)][1]; +			mutex_unlock(&indio_dev->mlock); +			return IIO_VAL_INT_PLUS_NANO; +		case IIO_TEMP: +			*val = 0; +			*val2 = 1000000000 / ad7192_get_temp_scale(unipolar); +			return IIO_VAL_INT_PLUS_NANO; +		default: +			return -EINVAL; +		} +	case IIO_CHAN_INFO_OFFSET: +		if (!unipolar) +			*val = -(1 << (chan->scan_type.realbits - 1)); +		else +			*val = 0; +		/* Kelvin to Celsius */ +		if (chan->type == IIO_TEMP) +			*val -= 273 * ad7192_get_temp_scale(unipolar); +		return IIO_VAL_INT; +	} + +	return -EINVAL; +} + +static int ad7192_write_raw(struct iio_dev *indio_dev, +			       struct iio_chan_spec const *chan, +			       int val, +			       int val2, +			       long mask) +{ +	struct ad7192_state *st = iio_priv(indio_dev); +	int ret, i; +	unsigned int tmp; + +	mutex_lock(&indio_dev->mlock); +	if (iio_buffer_enabled(indio_dev)) { +		mutex_unlock(&indio_dev->mlock); +		return -EBUSY; +	} + +	switch (mask) { +	case IIO_CHAN_INFO_SCALE: +		ret = -EINVAL; +		for (i = 0; i < ARRAY_SIZE(st->scale_avail); i++) +			if (val2 == st->scale_avail[i][1]) { +				ret = 0; +				tmp = st->conf; +				st->conf &= ~AD7192_CONF_GAIN(-1); +				st->conf |= AD7192_CONF_GAIN(i); +				if (tmp == st->conf) +					break; +				ad_sd_write_reg(&st->sd, AD7192_REG_CONF, +						 3, st->conf); +				ad7192_calibrate_all(st); +				break; +			} +		break; +	default: +		ret = -EINVAL; +	} + +	mutex_unlock(&indio_dev->mlock); + +	return ret; +} + +static int ad7192_write_raw_get_fmt(struct iio_dev *indio_dev, +			       struct iio_chan_spec const *chan, +			       long mask) +{ +	return IIO_VAL_INT_PLUS_NANO; +} + +static const struct iio_info ad7192_info = { +	.read_raw = &ad7192_read_raw, +	.write_raw = &ad7192_write_raw, +	.write_raw_get_fmt = &ad7192_write_raw_get_fmt, +	.attrs = &ad7192_attribute_group, +	.validate_trigger = ad_sd_validate_trigger, +	.driver_module = THIS_MODULE, +}; + +static const struct iio_info ad7195_info = { +	.read_raw = &ad7192_read_raw, +	.write_raw = &ad7192_write_raw, +	.write_raw_get_fmt = &ad7192_write_raw_get_fmt, +	.attrs = &ad7195_attribute_group, +	.validate_trigger = ad_sd_validate_trigger, +	.driver_module = THIS_MODULE, +}; + +static const struct iio_chan_spec ad7192_channels[] = { +	AD_SD_DIFF_CHANNEL(0, 1, 2, AD7192_CH_AIN1P_AIN2M, 24, 32, 0), +	AD_SD_DIFF_CHANNEL(1, 3, 4, AD7192_CH_AIN3P_AIN4M, 24, 32, 0), +	AD_SD_TEMP_CHANNEL(2, AD7192_CH_TEMP, 24, 32, 0), +	AD_SD_SHORTED_CHANNEL(3, 2, AD7192_CH_AIN2P_AIN2M, 24, 32, 0), +	AD_SD_CHANNEL(4, 1, AD7192_CH_AIN1, 24, 32, 0), +	AD_SD_CHANNEL(5, 2, AD7192_CH_AIN2, 24, 32, 0), +	AD_SD_CHANNEL(6, 3, AD7192_CH_AIN3, 24, 32, 0), +	AD_SD_CHANNEL(7, 4, AD7192_CH_AIN4, 24, 32, 0), +	IIO_CHAN_SOFT_TIMESTAMP(8), +}; + +static int ad7192_probe(struct spi_device *spi) +{ +	const struct ad7192_platform_data *pdata = spi->dev.platform_data; +	struct ad7192_state *st; +	struct iio_dev *indio_dev; +	int ret , voltage_uv = 0; + +	if (!pdata) { +		dev_err(&spi->dev, "no platform data?\n"); +		return -ENODEV; +	} + +	if (!spi->irq) { +		dev_err(&spi->dev, "no IRQ?\n"); +		return -ENODEV; +	} + +	indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); +	if (indio_dev == NULL) +		return -ENOMEM; + +	st = iio_priv(indio_dev); + +	st->reg = devm_regulator_get(&spi->dev, "vcc"); +	if (!IS_ERR(st->reg)) { +		ret = regulator_enable(st->reg); +		if (ret) +			return ret; + +		voltage_uv = regulator_get_voltage(st->reg); +	} + +	if (pdata && pdata->vref_mv) +		st->int_vref_mv = pdata->vref_mv; +	else if (voltage_uv) +		st->int_vref_mv = voltage_uv / 1000; +	else +		dev_warn(&spi->dev, "reference voltage undefined\n"); + +	spi_set_drvdata(spi, indio_dev); +	st->devid = spi_get_device_id(spi)->driver_data; +	indio_dev->dev.parent = &spi->dev; +	indio_dev->name = spi_get_device_id(spi)->name; +	indio_dev->modes = INDIO_DIRECT_MODE; +	indio_dev->channels = ad7192_channels; +	indio_dev->num_channels = ARRAY_SIZE(ad7192_channels); +	if (st->devid == ID_AD7195) +		indio_dev->info = &ad7195_info; +	else +		indio_dev->info = &ad7192_info; + +	ad_sd_init(&st->sd, indio_dev, spi, &ad7192_sigma_delta_info); + +	ret = ad_sd_setup_buffer_and_trigger(indio_dev); +	if (ret) +		goto error_disable_reg; + +	ret = ad7192_setup(st, pdata); +	if (ret) +		goto error_remove_trigger; + +	ret = iio_device_register(indio_dev); +	if (ret < 0) +		goto error_remove_trigger; +	return 0; + +error_remove_trigger: +	ad_sd_cleanup_buffer_and_trigger(indio_dev); +error_disable_reg: +	if (!IS_ERR(st->reg)) +		regulator_disable(st->reg); + +	return ret; +} + +static int ad7192_remove(struct spi_device *spi) +{ +	struct iio_dev *indio_dev = spi_get_drvdata(spi); +	struct ad7192_state *st = iio_priv(indio_dev); + +	iio_device_unregister(indio_dev); +	ad_sd_cleanup_buffer_and_trigger(indio_dev); + +	if (!IS_ERR(st->reg)) +		regulator_disable(st->reg); + +	return 0; +} + +static const struct spi_device_id ad7192_id[] = { +	{"ad7190", ID_AD7190}, +	{"ad7192", ID_AD7192}, +	{"ad7195", ID_AD7195}, +	{} +}; +MODULE_DEVICE_TABLE(spi, ad7192_id); + +static struct spi_driver ad7192_driver = { +	.driver = { +		.name	= "ad7192", +		.owner	= THIS_MODULE, +	}, +	.probe		= ad7192_probe, +	.remove		= ad7192_remove, +	.id_table	= ad7192_id, +}; +module_spi_driver(ad7192_driver); + +MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>"); +MODULE_DESCRIPTION("Analog Devices AD7190, AD7192, AD7195 ADC"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/iio/adc/ad7192.h b/drivers/staging/iio/adc/ad7192.h new file mode 100644 index 00000000000..a0a5b61a41f --- /dev/null +++ b/drivers/staging/iio/adc/ad7192.h @@ -0,0 +1,47 @@ +/* + * AD7190 AD7192 AD7195 SPI ADC driver + * + * Copyright 2011 Analog Devices Inc. + * + * Licensed under the GPL-2. + */ +#ifndef IIO_ADC_AD7192_H_ +#define IIO_ADC_AD7192_H_ + +/* + * TODO: struct ad7192_platform_data needs to go into include/linux/iio + */ + +/** + * struct ad7192_platform_data - platform/board specific information + * @vref_mv:		the external reference voltage in millivolt + * @clock_source_sel:	[0..3] + *			0 External 4.92 MHz clock connected from MCLK1 to MCLK2 + *			1 External Clock applied to MCLK2 + *			2 Internal 4.92 MHz Clock not available at the MCLK2 pin + *			3 Internal 4.92 MHz Clock available at the MCLK2 pin + * @ext_clk_Hz:		the external clock frequency in Hz, if not set + *			the driver uses the internal clock (16.776 MHz) + * @refin2_en:		REFIN1/REFIN2 Reference Select (AD7190/2 only) + * @rej60_en:		50/60Hz notch filter enable + * @sinc3_en:		SINC3 filter enable (default SINC4) + * @chop_en:		CHOP mode enable + * @buf_en:		buffered input mode enable + * @unipolar_en:	unipolar mode enable + * @burnout_curr_en:	constant current generators on AIN(+|-) enable + */ + +struct ad7192_platform_data { +	u16		vref_mv; +	u8		clock_source_sel; +	u32		ext_clk_Hz; +	bool		refin2_en; +	bool		rej60_en; +	bool		sinc3_en; +	bool		chop_en; +	bool		buf_en; +	bool		unipolar_en; +	bool		burnout_curr_en; +}; + +#endif /* IIO_ADC_AD7192_H_ */ diff --git a/drivers/staging/iio/adc/ad7280a.c b/drivers/staging/iio/adc/ad7280a.c new file mode 100644 index 00000000000..d215edf66af --- /dev/null +++ b/drivers/staging/iio/adc/ad7280a.c @@ -0,0 +1,984 @@ +/* + * AD7280A Lithium Ion Battery Monitoring System + * + * Copyright 2011 Analog Devices Inc. + * + * Licensed under the GPL-2. + */ + +#include <linux/device.h> +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/sysfs.h> +#include <linux/spi/spi.h> +#include <linux/err.h> +#include <linux/delay.h> +#include <linux/interrupt.h> +#include <linux/module.h> + +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> +#include <linux/iio/events.h> + +#include "ad7280a.h" + +/* Registers */ +#define AD7280A_CELL_VOLTAGE_1		0x0  /* D11 to D0, Read only */ +#define AD7280A_CELL_VOLTAGE_2		0x1  /* D11 to D0, Read only */ +#define AD7280A_CELL_VOLTAGE_3		0x2  /* D11 to D0, Read only */ +#define AD7280A_CELL_VOLTAGE_4		0x3  /* D11 to D0, Read only */ +#define AD7280A_CELL_VOLTAGE_5		0x4  /* D11 to D0, Read only */ +#define AD7280A_CELL_VOLTAGE_6		0x5  /* D11 to D0, Read only */ +#define AD7280A_AUX_ADC_1		0x6  /* D11 to D0, Read only */ +#define AD7280A_AUX_ADC_2		0x7  /* D11 to D0, Read only */ +#define AD7280A_AUX_ADC_3		0x8  /* D11 to D0, Read only */ +#define AD7280A_AUX_ADC_4		0x9  /* D11 to D0, Read only */ +#define AD7280A_AUX_ADC_5		0xA  /* D11 to D0, Read only */ +#define AD7280A_AUX_ADC_6		0xB  /* D11 to D0, Read only */ +#define AD7280A_SELF_TEST		0xC  /* D11 to D0, Read only */ +#define AD7280A_CONTROL_HB		0xD  /* D15 to D8, Read/write */ +#define AD7280A_CONTROL_LB		0xE  /* D7 to D0, Read/write */ +#define AD7280A_CELL_OVERVOLTAGE	0xF  /* D7 to D0, Read/write */ +#define AD7280A_CELL_UNDERVOLTAGE	0x10 /* D7 to D0, Read/write */ +#define AD7280A_AUX_ADC_OVERVOLTAGE	0x11 /* D7 to D0, Read/write */ +#define AD7280A_AUX_ADC_UNDERVOLTAGE	0x12 /* D7 to D0, Read/write */ +#define AD7280A_ALERT			0x13 /* D7 to D0, Read/write */ +#define AD7280A_CELL_BALANCE		0x14 /* D7 to D0, Read/write */ +#define AD7280A_CB1_TIMER		0x15 /* D7 to D0, Read/write */ +#define AD7280A_CB2_TIMER		0x16 /* D7 to D0, Read/write */ +#define AD7280A_CB3_TIMER		0x17 /* D7 to D0, Read/write */ +#define AD7280A_CB4_TIMER		0x18 /* D7 to D0, Read/write */ +#define AD7280A_CB5_TIMER		0x19 /* D7 to D0, Read/write */ +#define AD7280A_CB6_TIMER		0x1A /* D7 to D0, Read/write */ +#define AD7280A_PD_TIMER		0x1B /* D7 to D0, Read/write */ +#define AD7280A_READ			0x1C /* D7 to D0, Read/write */ +#define AD7280A_CNVST_CONTROL		0x1D /* D7 to D0, Read/write */ + +/* Bits and Masks */ +#define AD7280A_CTRL_HB_CONV_INPUT_ALL			(0 << 6) +#define AD7280A_CTRL_HB_CONV_INPUT_6CELL_AUX1_3_4	(1 << 6) +#define AD7280A_CTRL_HB_CONV_INPUT_6CELL		(2 << 6) +#define AD7280A_CTRL_HB_CONV_INPUT_SELF_TEST		(3 << 6) +#define AD7280A_CTRL_HB_CONV_RES_READ_ALL		(0 << 4) +#define AD7280A_CTRL_HB_CONV_RES_READ_6CELL_AUX1_3_4	(1 << 4) +#define AD7280A_CTRL_HB_CONV_RES_READ_6CELL		(2 << 4) +#define AD7280A_CTRL_HB_CONV_RES_READ_NO		(3 << 4) +#define AD7280A_CTRL_HB_CONV_START_CNVST		(0 << 3) +#define AD7280A_CTRL_HB_CONV_START_CS			(1 << 3) +#define AD7280A_CTRL_HB_CONV_AVG_DIS			(0 << 1) +#define AD7280A_CTRL_HB_CONV_AVG_2			(1 << 1) +#define AD7280A_CTRL_HB_CONV_AVG_4			(2 << 1) +#define AD7280A_CTRL_HB_CONV_AVG_8			(3 << 1) +#define AD7280A_CTRL_HB_CONV_AVG(x)			((x) << 1) +#define AD7280A_CTRL_HB_PWRDN_SW			(1 << 0) + +#define AD7280A_CTRL_LB_SWRST				(1 << 7) +#define AD7280A_CTRL_LB_ACQ_TIME_400ns			(0 << 5) +#define AD7280A_CTRL_LB_ACQ_TIME_800ns			(1 << 5) +#define AD7280A_CTRL_LB_ACQ_TIME_1200ns			(2 << 5) +#define AD7280A_CTRL_LB_ACQ_TIME_1600ns			(3 << 5) +#define AD7280A_CTRL_LB_ACQ_TIME(x)			((x) << 5) +#define AD7280A_CTRL_LB_MUST_SET			(1 << 4) +#define AD7280A_CTRL_LB_THERMISTOR_EN			(1 << 3) +#define AD7280A_CTRL_LB_LOCK_DEV_ADDR			(1 << 2) +#define AD7280A_CTRL_LB_INC_DEV_ADDR			(1 << 1) +#define AD7280A_CTRL_LB_DAISY_CHAIN_RB_EN		(1 << 0) + +#define AD7280A_ALERT_GEN_STATIC_HIGH			(1 << 6) +#define AD7280A_ALERT_RELAY_SIG_CHAIN_DOWN		(3 << 6) + +#define AD7280A_ALL_CELLS				(0xAD << 16) + +#define AD7280A_MAX_SPI_CLK_Hz		700000 /* < 1MHz */ +#define AD7280A_MAX_CHAIN		8 +#define AD7280A_CELLS_PER_DEV		6 +#define AD7280A_BITS			12 +#define AD7280A_NUM_CH			(AD7280A_AUX_ADC_6 - \ +					AD7280A_CELL_VOLTAGE_1 + 1) + +#define AD7280A_DEVADDR_MASTER		0 +#define AD7280A_DEVADDR_ALL		0x1F +/* 5-bit device address is sent LSB first */ +#define AD7280A_DEVADDR(addr)	(((addr & 0x1) << 4) | ((addr & 0x2) << 3) | \ +				(addr & 0x4) | ((addr & 0x8) >> 3) | \ +				((addr & 0x10) >> 4)) + +/* During a read a valid write is mandatory. + * So writing to the highest available address (Address 0x1F) + * and setting the address all parts bit to 0 is recommended + * So the TXVAL is AD7280A_DEVADDR_ALL + CRC + */ +#define AD7280A_READ_TXVAL	0xF800030A + +/* + * AD7280 CRC + * + * P(x) = x^8 + x^5 + x^3 + x^2 + x^1 + x^0 = 0b100101111 => 0x2F + */ +#define POLYNOM		0x2F +#define POLYNOM_ORDER	8 +#define HIGHBIT		(1 << (POLYNOM_ORDER - 1)) + +struct ad7280_state { +	struct spi_device		*spi; +	struct iio_chan_spec		*channels; +	struct iio_dev_attr		*iio_attr; +	int				slave_num; +	int				scan_cnt; +	int				readback_delay_us; +	unsigned char			crc_tab[256]; +	unsigned char			ctrl_hb; +	unsigned char			ctrl_lb; +	unsigned char			cell_threshhigh; +	unsigned char			cell_threshlow; +	unsigned char			aux_threshhigh; +	unsigned char			aux_threshlow; +	unsigned char			cb_mask[AD7280A_MAX_CHAIN]; + +	__be32				buf[2] ____cacheline_aligned; +}; + +static void ad7280_crc8_build_table(unsigned char *crc_tab) +{ +	unsigned char bit, crc; +	int cnt, i; + +	for (cnt = 0; cnt < 256; cnt++) { +		crc = cnt; +		for (i = 0; i < 8; i++) { +			bit = crc & HIGHBIT; +			crc <<= 1; +			if (bit) +				crc ^= POLYNOM; +		} +		crc_tab[cnt] = crc; +	} +} + +static unsigned char ad7280_calc_crc8(unsigned char *crc_tab, unsigned val) +{ +	unsigned char crc; + +	crc = crc_tab[val >> 16 & 0xFF]; +	crc = crc_tab[crc ^ (val >> 8 & 0xFF)]; + +	return  crc ^ (val & 0xFF); +} + +static int ad7280_check_crc(struct ad7280_state *st, unsigned val) +{ +	unsigned char crc = ad7280_calc_crc8(st->crc_tab, val >> 10); + +	if (crc != ((val >> 2) & 0xFF)) +		return -EIO; + +	return 0; +} + +/* After initiating a conversion sequence we need to wait until the + * conversion is done. The delay is typically in the range of 15..30 us + * however depending an the number of devices in the daisy chain and the + * number of averages taken, conversion delays and acquisition time options + * it may take up to 250us, in this case we better sleep instead of busy + * wait. + */ + +static void ad7280_delay(struct ad7280_state *st) +{ +	if (st->readback_delay_us < 50) +		udelay(st->readback_delay_us); +	else +		msleep(1); +} + +static int __ad7280_read32(struct ad7280_state *st, unsigned *val) +{ +	int ret; +	struct spi_transfer t = { +		.tx_buf	= &st->buf[0], +		.rx_buf = &st->buf[1], +		.len = 4, +	}; + +	st->buf[0] = cpu_to_be32(AD7280A_READ_TXVAL); + +	ret = spi_sync_transfer(st->spi, &t, 1); +	if (ret) +		return ret; + +	*val = be32_to_cpu(st->buf[1]); + +	return 0; +} + +static int ad7280_write(struct ad7280_state *st, unsigned devaddr, +			unsigned addr, bool all, unsigned val) +{ +	unsigned reg = (devaddr << 27 | addr << 21 | +			(val & 0xFF) << 13 | all << 12); + +	reg |= ad7280_calc_crc8(st->crc_tab, reg >> 11) << 3 | 0x2; +	st->buf[0] = cpu_to_be32(reg); + +	return spi_write(st->spi, &st->buf[0], 4); +} + +static int ad7280_read(struct ad7280_state *st, unsigned devaddr, +			unsigned addr) +{ +	int ret; +	unsigned tmp; + +	/* turns off the read operation on all parts */ +	ret = ad7280_write(st, AD7280A_DEVADDR_MASTER, AD7280A_CONTROL_HB, 1, +			AD7280A_CTRL_HB_CONV_INPUT_ALL | +			AD7280A_CTRL_HB_CONV_RES_READ_NO | +			st->ctrl_hb); +	if (ret) +		return ret; + +	/* turns on the read operation on the addressed part */ +	ret = ad7280_write(st, devaddr, AD7280A_CONTROL_HB, 0, +			AD7280A_CTRL_HB_CONV_INPUT_ALL | +			AD7280A_CTRL_HB_CONV_RES_READ_ALL | +			st->ctrl_hb); +	if (ret) +		return ret; + +	/* Set register address on the part to be read from */ +	ret = ad7280_write(st, devaddr, AD7280A_READ, 0, addr << 2); +	if (ret) +		return ret; + +	__ad7280_read32(st, &tmp); + +	if (ad7280_check_crc(st, tmp)) +		return -EIO; + +	if (((tmp >> 27) != devaddr) || (((tmp >> 21) & 0x3F) != addr)) +		return -EFAULT; + +	return (tmp >> 13) & 0xFF; +} + +static int ad7280_read_channel(struct ad7280_state *st, unsigned devaddr, +			       unsigned addr) +{ +	int ret; +	unsigned tmp; + +	ret = ad7280_write(st, devaddr, AD7280A_READ, 0, addr << 2); +	if (ret) +		return ret; + +	ret = ad7280_write(st, AD7280A_DEVADDR_MASTER, AD7280A_CONTROL_HB, 1, +			AD7280A_CTRL_HB_CONV_INPUT_ALL | +			AD7280A_CTRL_HB_CONV_RES_READ_NO | +			st->ctrl_hb); +	if (ret) +		return ret; + +	ret = ad7280_write(st, devaddr, AD7280A_CONTROL_HB, 0, +			AD7280A_CTRL_HB_CONV_INPUT_ALL | +			AD7280A_CTRL_HB_CONV_RES_READ_ALL | +			AD7280A_CTRL_HB_CONV_START_CS | +			st->ctrl_hb); +	if (ret) +		return ret; + +	ad7280_delay(st); + +	__ad7280_read32(st, &tmp); + +	if (ad7280_check_crc(st, tmp)) +		return -EIO; + +	if (((tmp >> 27) != devaddr) || (((tmp >> 23) & 0xF) != addr)) +		return -EFAULT; + +	return (tmp >> 11) & 0xFFF; +} + +static int ad7280_read_all_channels(struct ad7280_state *st, unsigned cnt, +			     unsigned *array) +{ +	int i, ret; +	unsigned tmp, sum = 0; + +	ret = ad7280_write(st, AD7280A_DEVADDR_MASTER, AD7280A_READ, 1, +			   AD7280A_CELL_VOLTAGE_1 << 2); +	if (ret) +		return ret; + +	ret = ad7280_write(st, AD7280A_DEVADDR_MASTER, AD7280A_CONTROL_HB, 1, +			AD7280A_CTRL_HB_CONV_INPUT_ALL | +			AD7280A_CTRL_HB_CONV_RES_READ_ALL | +			AD7280A_CTRL_HB_CONV_START_CS | +			st->ctrl_hb); +	if (ret) +		return ret; + +	ad7280_delay(st); + +	for (i = 0; i < cnt; i++) { +		__ad7280_read32(st, &tmp); + +		if (ad7280_check_crc(st, tmp)) +			return -EIO; + +		if (array) +			array[i] = tmp; +		/* only sum cell voltages */ +		if (((tmp >> 23) & 0xF) <= AD7280A_CELL_VOLTAGE_6) +			sum += ((tmp >> 11) & 0xFFF); +	} + +	return sum; +} + +static int ad7280_chain_setup(struct ad7280_state *st) +{ +	unsigned val, n; +	int ret; + +	ret = ad7280_write(st, AD7280A_DEVADDR_MASTER, AD7280A_CONTROL_LB, 1, +			AD7280A_CTRL_LB_DAISY_CHAIN_RB_EN | +			AD7280A_CTRL_LB_LOCK_DEV_ADDR | +			AD7280A_CTRL_LB_MUST_SET | +			AD7280A_CTRL_LB_SWRST | +			st->ctrl_lb); +	if (ret) +		return ret; + +	ret = ad7280_write(st, AD7280A_DEVADDR_MASTER, AD7280A_CONTROL_LB, 1, +			AD7280A_CTRL_LB_DAISY_CHAIN_RB_EN | +			AD7280A_CTRL_LB_LOCK_DEV_ADDR | +			AD7280A_CTRL_LB_MUST_SET | +			st->ctrl_lb); +	if (ret) +		return ret; + +	ret = ad7280_write(st, AD7280A_DEVADDR_MASTER, AD7280A_READ, 1, +			AD7280A_CONTROL_LB << 2); +	if (ret) +		return ret; + +	for (n = 0; n <= AD7280A_MAX_CHAIN; n++) { +		__ad7280_read32(st, &val); +		if (val == 0) +			return n - 1; + +		if (ad7280_check_crc(st, val)) +			return -EIO; + +		if (n != AD7280A_DEVADDR(val >> 27)) +			return -EIO; +	} + +	return -EFAULT; +} + +static ssize_t ad7280_show_balance_sw(struct device *dev, +					struct device_attribute *attr, +					char *buf) +{ +	struct iio_dev *indio_dev = dev_to_iio_dev(dev); +	struct ad7280_state *st = iio_priv(indio_dev); +	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); + +	return sprintf(buf, "%d\n", +		       !!(st->cb_mask[this_attr->address >> 8] & +		       (1 << ((this_attr->address & 0xFF) + 2)))); +} + +static ssize_t ad7280_store_balance_sw(struct device *dev, +					 struct device_attribute *attr, +					 const char *buf, +					 size_t len) +{ +	struct iio_dev *indio_dev = dev_to_iio_dev(dev); +	struct ad7280_state *st = iio_priv(indio_dev); +	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); +	bool readin; +	int ret; +	unsigned devaddr, ch; + +	ret = strtobool(buf, &readin); +	if (ret) +		return ret; + +	devaddr = this_attr->address >> 8; +	ch = this_attr->address & 0xFF; + +	mutex_lock(&indio_dev->mlock); +	if (readin) +		st->cb_mask[devaddr] |= 1 << (ch + 2); +	else +		st->cb_mask[devaddr] &= ~(1 << (ch + 2)); + +	ret = ad7280_write(st, devaddr, AD7280A_CELL_BALANCE, +			   0, st->cb_mask[devaddr]); +	mutex_unlock(&indio_dev->mlock); + +	return ret ? ret : len; +} + +static ssize_t ad7280_show_balance_timer(struct device *dev, +					struct device_attribute *attr, +					char *buf) +{ +	struct iio_dev *indio_dev = dev_to_iio_dev(dev); +	struct ad7280_state *st = iio_priv(indio_dev); +	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); +	int ret; +	unsigned msecs; + +	mutex_lock(&indio_dev->mlock); +	ret = ad7280_read(st, this_attr->address >> 8, +			this_attr->address & 0xFF); +	mutex_unlock(&indio_dev->mlock); + +	if (ret < 0) +		return ret; + +	msecs = (ret >> 3) * 71500; + +	return sprintf(buf, "%u\n", msecs); +} + +static ssize_t ad7280_store_balance_timer(struct device *dev, +					 struct device_attribute *attr, +					 const char *buf, +					 size_t len) +{ +	struct iio_dev *indio_dev = dev_to_iio_dev(dev); +	struct ad7280_state *st = iio_priv(indio_dev); +	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); +	unsigned long val; +	int ret; + +	ret = kstrtoul(buf, 10, &val); +	if (ret) +		return ret; + +	val /= 71500; + +	if (val > 31) +		return -EINVAL; + +	mutex_lock(&indio_dev->mlock); +	ret = ad7280_write(st, this_attr->address >> 8, +			   this_attr->address & 0xFF, +			   0, (val & 0x1F) << 3); +	mutex_unlock(&indio_dev->mlock); + +	return ret ? ret : len; +} + +static struct attribute *ad7280_attributes[AD7280A_MAX_CHAIN * +					   AD7280A_CELLS_PER_DEV * 2 + 1]; + +static struct attribute_group ad7280_attrs_group = { +	.attrs = ad7280_attributes, +}; + +static int ad7280_channel_init(struct ad7280_state *st) +{ +	int dev, ch, cnt; + +	st->channels = kcalloc((st->slave_num + 1) * 12 + 2, +			       sizeof(*st->channels), GFP_KERNEL); +	if (st->channels == NULL) +		return -ENOMEM; + +	for (dev = 0, cnt = 0; dev <= st->slave_num; dev++) +		for (ch = AD7280A_CELL_VOLTAGE_1; ch <= AD7280A_AUX_ADC_6; ch++, +			cnt++) { +			if (ch < AD7280A_AUX_ADC_1) { +				st->channels[cnt].type = IIO_VOLTAGE; +				st->channels[cnt].differential = 1; +				st->channels[cnt].channel = (dev * 6) + ch; +				st->channels[cnt].channel2 = +					st->channels[cnt].channel + 1; +			} else { +				st->channels[cnt].type = IIO_TEMP; +				st->channels[cnt].channel = (dev * 6) + ch - 6; +			} +			st->channels[cnt].indexed = 1; +			st->channels[cnt].info_mask_separate = +				BIT(IIO_CHAN_INFO_RAW); +			st->channels[cnt].info_mask_shared_by_type = +				BIT(IIO_CHAN_INFO_SCALE); +			st->channels[cnt].address = +				AD7280A_DEVADDR(dev) << 8 | ch; +			st->channels[cnt].scan_index = cnt; +			st->channels[cnt].scan_type.sign = 'u'; +			st->channels[cnt].scan_type.realbits = 12; +			st->channels[cnt].scan_type.storagebits = 32; +			st->channels[cnt].scan_type.shift = 0; +		} + +	st->channels[cnt].type = IIO_VOLTAGE; +	st->channels[cnt].differential = 1; +	st->channels[cnt].channel = 0; +	st->channels[cnt].channel2 = dev * 6; +	st->channels[cnt].address = AD7280A_ALL_CELLS; +	st->channels[cnt].indexed = 1; +	st->channels[cnt].info_mask_separate = BIT(IIO_CHAN_INFO_RAW); +	st->channels[cnt].info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE); +	st->channels[cnt].scan_index = cnt; +	st->channels[cnt].scan_type.sign = 'u'; +	st->channels[cnt].scan_type.realbits = 32; +	st->channels[cnt].scan_type.storagebits = 32; +	st->channels[cnt].scan_type.shift = 0; +	cnt++; +	st->channels[cnt].type = IIO_TIMESTAMP; +	st->channels[cnt].channel = -1; +	st->channels[cnt].scan_index = cnt; +	st->channels[cnt].scan_type.sign = 's'; +	st->channels[cnt].scan_type.realbits = 64; +	st->channels[cnt].scan_type.storagebits = 64; +	st->channels[cnt].scan_type.shift = 0; + +	return cnt + 1; +} + +static int ad7280_attr_init(struct ad7280_state *st) +{ +	int dev, ch, cnt; + +	st->iio_attr = kzalloc(sizeof(*st->iio_attr) * (st->slave_num + 1) * +				AD7280A_CELLS_PER_DEV * 2, GFP_KERNEL); +	if (st->iio_attr == NULL) +		return -ENOMEM; + +	for (dev = 0, cnt = 0; dev <= st->slave_num; dev++) +		for (ch = AD7280A_CELL_VOLTAGE_1; ch <= AD7280A_CELL_VOLTAGE_6; +			ch++, cnt++) { +			st->iio_attr[cnt].address = +				AD7280A_DEVADDR(dev) << 8 | ch; +			st->iio_attr[cnt].dev_attr.attr.mode = +				S_IWUSR | S_IRUGO; +			st->iio_attr[cnt].dev_attr.show = +				ad7280_show_balance_sw; +			st->iio_attr[cnt].dev_attr.store = +				ad7280_store_balance_sw; +			st->iio_attr[cnt].dev_attr.attr.name = +				kasprintf(GFP_KERNEL, +					"in%d-in%d_balance_switch_en", +					(dev * AD7280A_CELLS_PER_DEV) + ch, +					(dev * AD7280A_CELLS_PER_DEV) + ch + 1); +			ad7280_attributes[cnt] = +				&st->iio_attr[cnt].dev_attr.attr; +			cnt++; +			st->iio_attr[cnt].address = +				AD7280A_DEVADDR(dev) << 8 | +				(AD7280A_CB1_TIMER + ch); +			st->iio_attr[cnt].dev_attr.attr.mode = +				S_IWUSR | S_IRUGO; +			st->iio_attr[cnt].dev_attr.show = +				ad7280_show_balance_timer; +			st->iio_attr[cnt].dev_attr.store = +				ad7280_store_balance_timer; +			st->iio_attr[cnt].dev_attr.attr.name = +				kasprintf(GFP_KERNEL, "in%d-in%d_balance_timer", +					(dev * AD7280A_CELLS_PER_DEV) + ch, +					(dev * AD7280A_CELLS_PER_DEV) + ch + 1); +			ad7280_attributes[cnt] = +				&st->iio_attr[cnt].dev_attr.attr; +		} + +	ad7280_attributes[cnt] = NULL; + +	return 0; +} + +static ssize_t ad7280_read_channel_config(struct device *dev, +					struct device_attribute *attr, +					char *buf) +{ +	struct iio_dev *indio_dev = dev_to_iio_dev(dev); +	struct ad7280_state *st = iio_priv(indio_dev); +	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); +	unsigned val; + +	switch ((u32) this_attr->address) { +	case AD7280A_CELL_OVERVOLTAGE: +		val = 1000 + (st->cell_threshhigh * 1568) / 100; +		break; +	case AD7280A_CELL_UNDERVOLTAGE: +		val = 1000 + (st->cell_threshlow * 1568) / 100; +		break; +	case AD7280A_AUX_ADC_OVERVOLTAGE: +		val = (st->aux_threshhigh * 196) / 10; +		break; +	case AD7280A_AUX_ADC_UNDERVOLTAGE: +		val = (st->aux_threshlow * 196) / 10; +		break; +	default: +		return -EINVAL; +	} + +	return sprintf(buf, "%u\n", val); +} + +static ssize_t ad7280_write_channel_config(struct device *dev, +					 struct device_attribute *attr, +					 const char *buf, +					 size_t len) +{ +	struct iio_dev *indio_dev = dev_to_iio_dev(dev); +	struct ad7280_state *st = iio_priv(indio_dev); +	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); + +	long val; +	int ret; + +	ret = kstrtol(buf, 10, &val); +	if (ret) +		return ret; + +	switch ((u32) this_attr->address) { +	case AD7280A_CELL_OVERVOLTAGE: +	case AD7280A_CELL_UNDERVOLTAGE: +		val = ((val - 1000) * 100) / 1568; /* LSB 15.68mV */ +		break; +	case AD7280A_AUX_ADC_OVERVOLTAGE: +	case AD7280A_AUX_ADC_UNDERVOLTAGE: +		val = (val * 10) / 196; /* LSB 19.6mV */ +		break; +	default: +		return -EFAULT; +	} + +	val = clamp(val, 0L, 0xFFL); + +	mutex_lock(&indio_dev->mlock); +	switch ((u32) this_attr->address) { +	case AD7280A_CELL_OVERVOLTAGE: +		st->cell_threshhigh = val; +		break; +	case AD7280A_CELL_UNDERVOLTAGE: +		st->cell_threshlow = val; +		break; +	case AD7280A_AUX_ADC_OVERVOLTAGE: +		st->aux_threshhigh = val; +		break; +	case AD7280A_AUX_ADC_UNDERVOLTAGE: +		st->aux_threshlow = val; +		break; +	} + +	ret = ad7280_write(st, AD7280A_DEVADDR_MASTER, +			   this_attr->address, 1, val); + +	mutex_unlock(&indio_dev->mlock); + +	return ret ? ret : len; +} + +static irqreturn_t ad7280_event_handler(int irq, void *private) +{ +	struct iio_dev *indio_dev = private; +	struct ad7280_state *st = iio_priv(indio_dev); +	unsigned *channels; +	int i, ret; + +	channels = kcalloc(st->scan_cnt, sizeof(*channels), GFP_KERNEL); +	if (channels == NULL) +		return IRQ_HANDLED; + +	ret = ad7280_read_all_channels(st, st->scan_cnt, channels); +	if (ret < 0) +		goto out; + +	for (i = 0; i < st->scan_cnt; i++) { +		if (((channels[i] >> 23) & 0xF) <= AD7280A_CELL_VOLTAGE_6) { +			if (((channels[i] >> 11) & 0xFFF) >= +				st->cell_threshhigh) +				iio_push_event(indio_dev, +					IIO_EVENT_CODE(IIO_VOLTAGE, +						       1, +						       0, +						       IIO_EV_DIR_RISING, +						       IIO_EV_TYPE_THRESH, +						       0, 0, 0), +					iio_get_time_ns()); +			else if (((channels[i] >> 11) & 0xFFF) <= +				st->cell_threshlow) +				iio_push_event(indio_dev, +					IIO_EVENT_CODE(IIO_VOLTAGE, +						       1, +						       0, +						       IIO_EV_DIR_FALLING, +						       IIO_EV_TYPE_THRESH, +						       0, 0, 0), +					iio_get_time_ns()); +		} else { +			if (((channels[i] >> 11) & 0xFFF) >= st->aux_threshhigh) +				iio_push_event(indio_dev, +					IIO_UNMOD_EVENT_CODE(IIO_TEMP, +					0, +					IIO_EV_TYPE_THRESH, +					IIO_EV_DIR_RISING), +					iio_get_time_ns()); +			else if (((channels[i] >> 11) & 0xFFF) <= +				st->aux_threshlow) +				iio_push_event(indio_dev, +					IIO_UNMOD_EVENT_CODE(IIO_TEMP, +					0, +					IIO_EV_TYPE_THRESH, +					IIO_EV_DIR_FALLING), +					iio_get_time_ns()); +		} +	} + +out: +	kfree(channels); + +	return IRQ_HANDLED; +} + +static IIO_DEVICE_ATTR_NAMED(in_thresh_low_value, +		in_voltage-voltage_thresh_low_value, +		S_IRUGO | S_IWUSR, +		ad7280_read_channel_config, +		ad7280_write_channel_config, +		AD7280A_CELL_UNDERVOLTAGE); + +static IIO_DEVICE_ATTR_NAMED(in_thresh_high_value, +		in_voltage-voltage_thresh_high_value, +		S_IRUGO | S_IWUSR, +		ad7280_read_channel_config, +		ad7280_write_channel_config, +		AD7280A_CELL_OVERVOLTAGE); + +static IIO_DEVICE_ATTR(in_temp_thresh_low_value, +		S_IRUGO | S_IWUSR, +		ad7280_read_channel_config, +		ad7280_write_channel_config, +		AD7280A_AUX_ADC_UNDERVOLTAGE); + +static IIO_DEVICE_ATTR(in_temp_thresh_high_value, +		S_IRUGO | S_IWUSR, +		ad7280_read_channel_config, +		ad7280_write_channel_config, +		AD7280A_AUX_ADC_OVERVOLTAGE); + + +static struct attribute *ad7280_event_attributes[] = { +	&iio_dev_attr_in_thresh_low_value.dev_attr.attr, +	&iio_dev_attr_in_thresh_high_value.dev_attr.attr, +	&iio_dev_attr_in_temp_thresh_low_value.dev_attr.attr, +	&iio_dev_attr_in_temp_thresh_high_value.dev_attr.attr, +	NULL, +}; + +static struct attribute_group ad7280_event_attrs_group = { +	.attrs = ad7280_event_attributes, +}; + +static int ad7280_read_raw(struct iio_dev *indio_dev, +			   struct iio_chan_spec const *chan, +			   int *val, +			   int *val2, +			   long m) +{ +	struct ad7280_state *st = iio_priv(indio_dev); +	int ret; + +	switch (m) { +	case IIO_CHAN_INFO_RAW: +		mutex_lock(&indio_dev->mlock); +		if (chan->address == AD7280A_ALL_CELLS) +			ret = ad7280_read_all_channels(st, st->scan_cnt, NULL); +		else +			ret = ad7280_read_channel(st, chan->address >> 8, +						  chan->address & 0xFF); +		mutex_unlock(&indio_dev->mlock); + +		if (ret < 0) +			return ret; + +		*val = ret; + +		return IIO_VAL_INT; +	case IIO_CHAN_INFO_SCALE: +		if ((chan->address & 0xFF) <= AD7280A_CELL_VOLTAGE_6) +			*val = 4000; +		else +			*val = 5000; + +		*val2 = AD7280A_BITS; +		return IIO_VAL_FRACTIONAL_LOG2; +	} +	return -EINVAL; +} + +static const struct iio_info ad7280_info = { +	.read_raw = &ad7280_read_raw, +	.event_attrs = &ad7280_event_attrs_group, +	.attrs = &ad7280_attrs_group, +	.driver_module = THIS_MODULE, +}; + +static const struct ad7280_platform_data ad7793_default_pdata = { +	.acquisition_time = AD7280A_ACQ_TIME_400ns, +	.conversion_averaging = AD7280A_CONV_AVG_DIS, +	.thermistor_term_en = true, +}; + +static int ad7280_probe(struct spi_device *spi) +{ +	const struct ad7280_platform_data *pdata = spi->dev.platform_data; +	struct ad7280_state *st; +	int ret; +	const unsigned short tACQ_ns[4] = {465, 1010, 1460, 1890}; +	const unsigned short nAVG[4] = {1, 2, 4, 8}; +	struct iio_dev *indio_dev; + +	indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); +	if (indio_dev == NULL) +		return -ENOMEM; + +	st = iio_priv(indio_dev); +	spi_set_drvdata(spi, indio_dev); +	st->spi = spi; + +	if (!pdata) +		pdata = &ad7793_default_pdata; + +	ad7280_crc8_build_table(st->crc_tab); + +	st->spi->max_speed_hz = AD7280A_MAX_SPI_CLK_Hz; +	st->spi->mode = SPI_MODE_1; +	spi_setup(st->spi); + +	st->ctrl_lb = AD7280A_CTRL_LB_ACQ_TIME(pdata->acquisition_time & 0x3); +	st->ctrl_hb = AD7280A_CTRL_HB_CONV_AVG(pdata->conversion_averaging +			& 0x3) | (pdata->thermistor_term_en ? +			AD7280A_CTRL_LB_THERMISTOR_EN : 0); + +	ret = ad7280_chain_setup(st); +	if (ret < 0) +		return ret; + +	st->slave_num = ret; +	st->scan_cnt = (st->slave_num + 1) * AD7280A_NUM_CH; +	st->cell_threshhigh = 0xFF; +	st->aux_threshhigh = 0xFF; + +	/* +	 * Total Conversion Time = ((tACQ + tCONV) * +	 *			   (Number of Conversions per Part)) − +	 *			   tACQ + ((N - 1) * tDELAY) +	 * +	 * Readback Delay = Total Conversion Time + tWAIT +	 */ + +	st->readback_delay_us = +		((tACQ_ns[pdata->acquisition_time & 0x3] + 695) * +		(AD7280A_NUM_CH * nAVG[pdata->conversion_averaging & 0x3])) +		- tACQ_ns[pdata->acquisition_time & 0x3] + +		st->slave_num * 250; + +	/* Convert to usecs */ +	st->readback_delay_us = DIV_ROUND_UP(st->readback_delay_us, 1000); +	st->readback_delay_us += 5; /* Add tWAIT */ + +	indio_dev->name = spi_get_device_id(spi)->name; +	indio_dev->dev.parent = &spi->dev; +	indio_dev->modes = INDIO_DIRECT_MODE; + +	ret = ad7280_channel_init(st); +	if (ret < 0) +		return ret; + +	indio_dev->num_channels = ret; +	indio_dev->channels = st->channels; +	indio_dev->info = &ad7280_info; + +	ret = ad7280_attr_init(st); +	if (ret < 0) +		goto error_free_channels; + +	ret = iio_device_register(indio_dev); +	if (ret) +		goto error_free_attr; + +	if (spi->irq > 0) { +		ret = ad7280_write(st, AD7280A_DEVADDR_MASTER, +				   AD7280A_ALERT, 1, +				   AD7280A_ALERT_RELAY_SIG_CHAIN_DOWN); +		if (ret) +			goto error_unregister; + +		ret = ad7280_write(st, AD7280A_DEVADDR(st->slave_num), +				   AD7280A_ALERT, 0, +				   AD7280A_ALERT_GEN_STATIC_HIGH | +				   (pdata->chain_last_alert_ignore & 0xF)); +		if (ret) +			goto error_unregister; + +		ret = request_threaded_irq(spi->irq, +					   NULL, +					   ad7280_event_handler, +					   IRQF_TRIGGER_FALLING | +					   IRQF_ONESHOT, +					   indio_dev->name, +					   indio_dev); +		if (ret) +			goto error_unregister; +	} + +	return 0; +error_unregister: +	iio_device_unregister(indio_dev); + +error_free_attr: +	kfree(st->iio_attr); + +error_free_channels: +	kfree(st->channels); + +	return ret; +} + +static int ad7280_remove(struct spi_device *spi) +{ +	struct iio_dev *indio_dev = spi_get_drvdata(spi); +	struct ad7280_state *st = iio_priv(indio_dev); + +	if (spi->irq > 0) +		free_irq(spi->irq, indio_dev); +	iio_device_unregister(indio_dev); + +	ad7280_write(st, AD7280A_DEVADDR_MASTER, AD7280A_CONTROL_HB, 1, +			AD7280A_CTRL_HB_PWRDN_SW | st->ctrl_hb); + +	kfree(st->channels); +	kfree(st->iio_attr); + +	return 0; +} + +static const struct spi_device_id ad7280_id[] = { +	{"ad7280a", 0}, +	{} +}; +MODULE_DEVICE_TABLE(spi, ad7280_id); + +static struct spi_driver ad7280_driver = { +	.driver = { +		.name	= "ad7280", +		.owner	= THIS_MODULE, +	}, +	.probe		= ad7280_probe, +	.remove		= ad7280_remove, +	.id_table	= ad7280_id, +}; +module_spi_driver(ad7280_driver); + +MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>"); +MODULE_DESCRIPTION("Analog Devices AD7280A"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/iio/adc/ad7280a.h b/drivers/staging/iio/adc/ad7280a.h new file mode 100644 index 00000000000..20400b0045e --- /dev/null +++ b/drivers/staging/iio/adc/ad7280a.h @@ -0,0 +1,38 @@ +/* + * AD7280A Lithium Ion Battery Monitoring System + * + * Copyright 2011 Analog Devices Inc. + * + * Licensed under the GPL-2. + */ + +#ifndef IIO_ADC_AD7280_H_ +#define IIO_ADC_AD7280_H_ + +/* + * TODO: struct ad7280_platform_data needs to go into include/linux/iio + */ + +#define AD7280A_ACQ_TIME_400ns			0 +#define AD7280A_ACQ_TIME_800ns			1 +#define AD7280A_ACQ_TIME_1200ns			2 +#define AD7280A_ACQ_TIME_1600ns			3 + +#define AD7280A_CONV_AVG_DIS			0 +#define AD7280A_CONV_AVG_2			1 +#define AD7280A_CONV_AVG_4			2 +#define AD7280A_CONV_AVG_8			3 + +#define AD7280A_ALERT_REMOVE_VIN5		(1 << 2) +#define AD7280A_ALERT_REMOVE_VIN4_VIN5		(2 << 2) +#define AD7280A_ALERT_REMOVE_AUX5		(1 << 0) +#define AD7280A_ALERT_REMOVE_AUX4_AUX5		(2 << 0) + +struct ad7280_platform_data { +	unsigned acquisition_time; +	unsigned conversion_averaging; +	unsigned chain_last_alert_ignore; +	bool thermistor_term_en; +}; + +#endif /* IIO_ADC_AD7280_H_ */ diff --git a/drivers/staging/iio/adc/ad7291.c b/drivers/staging/iio/adc/ad7291.c new file mode 100644 index 00000000000..7194bd13876 --- /dev/null +++ b/drivers/staging/iio/adc/ad7291.c @@ -0,0 +1,580 @@ +/* + * AD7291 8-Channel, I2C, 12-Bit SAR ADC with Temperature Sensor + * + * Copyright 2010-2011 Analog Devices Inc. + * + * Licensed under the GPL-2 or later. + */ + +#include <linux/interrupt.h> +#include <linux/device.h> +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/sysfs.h> +#include <linux/i2c.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/regulator/consumer.h> +#include <linux/err.h> + +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> +#include <linux/iio/events.h> + +#include "ad7291.h" + +/* + * Simplified handling + * + * If no events enabled - single polled channel read + * If event enabled direct reads disable unless channel + * is in the read mask. + * + * The noise-delayed bit as per datasheet suggestion is always enabled. + * + */ + +/* + * AD7291 registers definition + */ +#define AD7291_COMMAND			0x00 +#define AD7291_VOLTAGE			0x01 +#define AD7291_T_SENSE			0x02 +#define AD7291_T_AVERAGE		0x03 +#define AD7291_DATA_HIGH(x)		((x) * 3 + 0x4) +#define AD7291_DATA_LOW(x)		((x) * 3 + 0x5) +#define AD7291_HYST(x)			((x) * 3 + 0x6) +#define AD7291_VOLTAGE_ALERT_STATUS	0x1F +#define AD7291_T_ALERT_STATUS		0x20 + +#define AD7291_VOLTAGE_LIMIT_COUNT	8 + + +/* + * AD7291 command + */ +#define AD7291_AUTOCYCLE		(1 << 0) +#define AD7291_RESET			(1 << 1) +#define AD7291_ALERT_CLEAR		(1 << 2) +#define AD7291_ALERT_POLARITY		(1 << 3) +#define AD7291_EXT_REF			(1 << 4) +#define AD7291_NOISE_DELAY		(1 << 5) +#define AD7291_T_SENSE_MASK		(1 << 7) +#define AD7291_VOLTAGE_MASK		0xFF00 +#define AD7291_VOLTAGE_OFFSET		0x8 + +/* + * AD7291 value masks + */ +#define AD7291_CHANNEL_MASK		0xF000 +#define AD7291_BITS			12 +#define AD7291_VALUE_MASK		0xFFF +#define AD7291_T_VALUE_SIGN		0x400 +#define AD7291_T_VALUE_FLOAT_OFFSET	2 +#define AD7291_T_VALUE_FLOAT_MASK	0x2 + +#define AD7291_BITS			12 + +struct ad7291_chip_info { +	struct i2c_client	*client; +	struct regulator	*reg; +	u16			command; +	u16			c_mask;	/* Active voltage channels for events */ +	struct mutex		state_lock; +}; + +static int ad7291_i2c_read(struct ad7291_chip_info *chip, u8 reg, u16 *data) +{ +	struct i2c_client *client = chip->client; +	int ret = 0; + +	ret = i2c_smbus_read_word_swapped(client, reg); +	if (ret < 0) { +		dev_err(&client->dev, "I2C read error\n"); +		return ret; +	} + +	*data = ret; + +	return 0; +} + +static int ad7291_i2c_write(struct ad7291_chip_info *chip, u8 reg, u16 data) +{ +	return i2c_smbus_write_word_swapped(chip->client, reg, data); +} + +static irqreturn_t ad7291_event_handler(int irq, void *private) +{ +	struct iio_dev *indio_dev = private; +	struct ad7291_chip_info *chip = iio_priv(private); +	u16 t_status, v_status; +	u16 command; +	int i; +	s64 timestamp = iio_get_time_ns(); + +	if (ad7291_i2c_read(chip, AD7291_T_ALERT_STATUS, &t_status)) +		return IRQ_HANDLED; + +	if (ad7291_i2c_read(chip, AD7291_VOLTAGE_ALERT_STATUS, &v_status)) +		return IRQ_HANDLED; + +	if (!(t_status || v_status)) +		return IRQ_HANDLED; + +	command = chip->command | AD7291_ALERT_CLEAR; +	ad7291_i2c_write(chip, AD7291_COMMAND, command); + +	command = chip->command & ~AD7291_ALERT_CLEAR; +	ad7291_i2c_write(chip, AD7291_COMMAND, command); + +	/* For now treat t_sense and t_sense_average the same */ +	if ((t_status & (1 << 0)) || (t_status & (1 << 2))) +		iio_push_event(indio_dev, +			       IIO_UNMOD_EVENT_CODE(IIO_TEMP, +						    0, +						    IIO_EV_TYPE_THRESH, +						    IIO_EV_DIR_FALLING), +			       timestamp); +	if ((t_status & (1 << 1)) || (t_status & (1 << 3))) +		iio_push_event(indio_dev, +			       IIO_UNMOD_EVENT_CODE(IIO_TEMP, +						    0, +						    IIO_EV_TYPE_THRESH, +						    IIO_EV_DIR_RISING), +			       timestamp); + +	for (i = 0; i < AD7291_VOLTAGE_LIMIT_COUNT*2; i += 2) { +		if (v_status & (1 << i)) +			iio_push_event(indio_dev, +				       IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, +							    i/2, +							    IIO_EV_TYPE_THRESH, +							    IIO_EV_DIR_FALLING), +				       timestamp); +		if (v_status & (1 << (i + 1))) +			iio_push_event(indio_dev, +				       IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, +							    i/2, +							    IIO_EV_TYPE_THRESH, +							    IIO_EV_DIR_RISING), +				       timestamp); +	} + +	return IRQ_HANDLED; +} + +static unsigned int ad7291_threshold_reg(const struct iio_chan_spec *chan, +	enum iio_event_direction dir, enum iio_event_info info) +{ +	unsigned int offset; + +	switch (chan->type) { +	case IIO_VOLTAGE: +		offset = chan->channel; +		break; +	case IIO_TEMP: +		offset = 8; +		break; +	default: +	    return 0; +	} + +	switch (info) { +	case IIO_EV_INFO_VALUE: +			if (dir == IIO_EV_DIR_FALLING) +					return AD7291_DATA_HIGH(offset); +			else +					return AD7291_DATA_LOW(offset); +	case IIO_EV_INFO_HYSTERESIS: +			return AD7291_HYST(offset); +	default: +			break; +	} +	return 0; +} + +static int ad7291_read_event_value(struct iio_dev *indio_dev, +				   const struct iio_chan_spec *chan, +				   enum iio_event_type type, +				   enum iio_event_direction dir, +				   enum iio_event_info info, +				   int *val, int *val2) +{ +	struct ad7291_chip_info *chip = iio_priv(indio_dev); +	int ret; +	u16 uval; + +	ret = ad7291_i2c_read(chip, ad7291_threshold_reg(chan, dir, info), +		&uval); +	if (ret < 0) +		return ret; + +	if (info == IIO_EV_INFO_HYSTERESIS || chan->type == IIO_VOLTAGE) +		*val = uval & AD7291_VALUE_MASK; + +	else +		*val = sign_extend32(uval, 11); + +	return IIO_VAL_INT; +} + +static int ad7291_write_event_value(struct iio_dev *indio_dev, +				    const struct iio_chan_spec *chan, +				    enum iio_event_type type, +				    enum iio_event_direction dir, +				    enum iio_event_info info, +				    int val, int val2) +{ +	struct ad7291_chip_info *chip = iio_priv(indio_dev); + +	if (info == IIO_EV_INFO_HYSTERESIS || chan->type == IIO_VOLTAGE) { +		if (val > AD7291_VALUE_MASK || val < 0) +			return -EINVAL; +	} else { +		if (val > 2047 || val < -2048) +			return -EINVAL; +	} + +	return ad7291_i2c_write(chip, ad7291_threshold_reg(chan, dir, info), +		val); +} + +static int ad7291_read_event_config(struct iio_dev *indio_dev, +				    const struct iio_chan_spec *chan, +				    enum iio_event_type type, +				    enum iio_event_direction dir) +{ +	struct ad7291_chip_info *chip = iio_priv(indio_dev); +	/* To be enabled the channel must simply be on. If any are enabled +	   we are in continuous sampling mode */ + +	switch (chan->type) { +	case IIO_VOLTAGE: +		if (chip->c_mask & (1 << (15 - chan->channel))) +			return 1; +		else +			return 0; +	case IIO_TEMP: +		/* always on */ +		return 1; +	default: +		return -EINVAL; +	} + +} + +static int ad7291_write_event_config(struct iio_dev *indio_dev, +				     const struct iio_chan_spec *chan, +				     enum iio_event_type type, +				     enum iio_event_direction dir, +				     int state) +{ +	int ret = 0; +	struct ad7291_chip_info *chip = iio_priv(indio_dev); +	unsigned int mask; +	u16 regval; + +	mutex_lock(&chip->state_lock); +	regval = chip->command; +	/* +	 * To be enabled the channel must simply be on. If any are enabled +	 * use continuous sampling mode. +	 * Possible to disable temp as well but that makes single read tricky. +	 */ + +	mask = BIT(15 - chan->channel); + +	switch (chan->type) { +	case IIO_VOLTAGE: +		if ((!state) && (chip->c_mask & mask)) +			chip->c_mask &= ~mask; +		else if (state && (!(chip->c_mask & mask))) +			chip->c_mask |= mask; +		else +			break; + +		regval &= ~AD7291_AUTOCYCLE; +		regval |= chip->c_mask; +		if (chip->c_mask) /* Enable autocycle? */ +			regval |= AD7291_AUTOCYCLE; + +		ret = ad7291_i2c_write(chip, AD7291_COMMAND, regval); +		if (ret < 0) +			goto error_ret; + +		chip->command = regval; +		break; +	default: +		ret = -EINVAL; +	} + +error_ret: +	mutex_unlock(&chip->state_lock); +	return ret; +} + +static int ad7291_read_raw(struct iio_dev *indio_dev, +			   struct iio_chan_spec const *chan, +			   int *val, +			   int *val2, +			   long mask) +{ +	int ret; +	struct ad7291_chip_info *chip = iio_priv(indio_dev); +	u16 regval; + +	switch (mask) { +	case IIO_CHAN_INFO_RAW: +		switch (chan->type) { +		case IIO_VOLTAGE: +			mutex_lock(&chip->state_lock); +			/* If in autocycle mode drop through */ +			if (chip->command & AD7291_AUTOCYCLE) { +				mutex_unlock(&chip->state_lock); +				return -EBUSY; +			} +			/* Enable this channel alone */ +			regval = chip->command & (~AD7291_VOLTAGE_MASK); +			regval |= 1 << (15 - chan->channel); +			ret = ad7291_i2c_write(chip, AD7291_COMMAND, regval); +			if (ret < 0) { +				mutex_unlock(&chip->state_lock); +				return ret; +			} +			/* Read voltage */ +			ret = i2c_smbus_read_word_swapped(chip->client, +						       AD7291_VOLTAGE); +			if (ret < 0) { +				mutex_unlock(&chip->state_lock); +				return ret; +			} +			*val = ret & AD7291_VALUE_MASK; +			mutex_unlock(&chip->state_lock); +			return IIO_VAL_INT; +		case IIO_TEMP: +			/* Assumes tsense bit of command register always set */ +			ret = i2c_smbus_read_word_swapped(chip->client, +						       AD7291_T_SENSE); +			if (ret < 0) +				return ret; +			*val = sign_extend32(ret, 11); +			return IIO_VAL_INT; +		default: +			return -EINVAL; +		} +	case IIO_CHAN_INFO_AVERAGE_RAW: +		ret = i2c_smbus_read_word_swapped(chip->client, +					       AD7291_T_AVERAGE); +			if (ret < 0) +				return ret; +			*val = sign_extend32(ret, 11); +			return IIO_VAL_INT; +	case IIO_CHAN_INFO_SCALE: +		switch (chan->type) { +		case IIO_VOLTAGE: +			if (chip->reg) { +				int vref; +				vref = regulator_get_voltage(chip->reg); +				if (vref < 0) +					return vref; +				*val = vref / 1000; +			} else { +				*val = 2500; +			} +			*val2 = AD7291_BITS; +			return IIO_VAL_FRACTIONAL_LOG2; +		case IIO_TEMP: +			/* +			 * One LSB of the ADC corresponds to 0.25 deg C. +			 * The temperature reading is in 12-bit twos +			 * complement format +			 */ +			*val = 250; +			return IIO_VAL_INT; +		default: +			return -EINVAL; +		} +	default: +		return -EINVAL; +	} +} + +static const struct iio_event_spec ad7291_events[] = { +	{ +		.type = IIO_EV_TYPE_THRESH, +		.dir = IIO_EV_DIR_RISING, +		.mask_separate = BIT(IIO_EV_INFO_VALUE) | +			BIT(IIO_EV_INFO_ENABLE), +	}, { +		.type = IIO_EV_TYPE_THRESH, +		.dir = IIO_EV_DIR_FALLING, +		.mask_separate = BIT(IIO_EV_INFO_VALUE) | +			BIT(IIO_EV_INFO_ENABLE), +	}, { +		.type = IIO_EV_TYPE_THRESH, +		.dir = IIO_EV_DIR_EITHER, +		.mask_separate = BIT(IIO_EV_INFO_HYSTERESIS), +	}, +}; + +#define AD7291_VOLTAGE_CHAN(_chan)					\ +{									\ +	.type = IIO_VOLTAGE,						\ +	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),			\ +	.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),		\ +	.indexed = 1,							\ +	.channel = _chan,						\ +	.event_spec = ad7291_events,					\ +	.num_event_specs = ARRAY_SIZE(ad7291_events),			\ +} + +static const struct iio_chan_spec ad7291_channels[] = { +	AD7291_VOLTAGE_CHAN(0), +	AD7291_VOLTAGE_CHAN(1), +	AD7291_VOLTAGE_CHAN(2), +	AD7291_VOLTAGE_CHAN(3), +	AD7291_VOLTAGE_CHAN(4), +	AD7291_VOLTAGE_CHAN(5), +	AD7291_VOLTAGE_CHAN(6), +	AD7291_VOLTAGE_CHAN(7), +	{ +		.type = IIO_TEMP, +		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | +				BIT(IIO_CHAN_INFO_AVERAGE_RAW) | +				BIT(IIO_CHAN_INFO_SCALE), +		.indexed = 1, +		.channel = 0, +		.event_spec = ad7291_events, +		.num_event_specs = ARRAY_SIZE(ad7291_events), +	} +}; + +static const struct iio_info ad7291_info = { +	.read_raw = &ad7291_read_raw, +	.read_event_config = &ad7291_read_event_config, +	.write_event_config = &ad7291_write_event_config, +	.read_event_value = &ad7291_read_event_value, +	.write_event_value = &ad7291_write_event_value, +	.driver_module = THIS_MODULE, +}; + +static int ad7291_probe(struct i2c_client *client, +		const struct i2c_device_id *id) +{ +	struct ad7291_platform_data *pdata = client->dev.platform_data; +	struct ad7291_chip_info *chip; +	struct iio_dev *indio_dev; +	int ret; + +	indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*chip)); +	if (!indio_dev) +		return -ENOMEM; +	chip = iio_priv(indio_dev); + +	if (pdata && pdata->use_external_ref) { +		chip->reg = devm_regulator_get(&client->dev, "vref"); +		if (IS_ERR(chip->reg)) +			return PTR_ERR(chip->reg); + +		ret = regulator_enable(chip->reg); +		if (ret) +			return ret; +	} + +	mutex_init(&chip->state_lock); +	/* this is only used for device removal purposes */ +	i2c_set_clientdata(client, indio_dev); + +	chip->client = client; + +	chip->command = AD7291_NOISE_DELAY | +			AD7291_T_SENSE_MASK | /* Tsense always enabled */ +			AD7291_ALERT_POLARITY; /* set irq polarity low level */ + +	if (pdata && pdata->use_external_ref) +		chip->command |= AD7291_EXT_REF; + +	indio_dev->name = id->name; +	indio_dev->channels = ad7291_channels; +	indio_dev->num_channels = ARRAY_SIZE(ad7291_channels); + +	indio_dev->dev.parent = &client->dev; +	indio_dev->info = &ad7291_info; +	indio_dev->modes = INDIO_DIRECT_MODE; + +	ret = ad7291_i2c_write(chip, AD7291_COMMAND, AD7291_RESET); +	if (ret) { +		ret = -EIO; +		goto error_disable_reg; +	} + +	ret = ad7291_i2c_write(chip, AD7291_COMMAND, chip->command); +	if (ret) { +		ret = -EIO; +		goto error_disable_reg; +	} + +	if (client->irq > 0) { +		ret = request_threaded_irq(client->irq, +					   NULL, +					   &ad7291_event_handler, +					   IRQF_TRIGGER_LOW | IRQF_ONESHOT, +					   id->name, +					   indio_dev); +		if (ret) +			goto error_disable_reg; +	} + +	ret = iio_device_register(indio_dev); +	if (ret) +		goto error_unreg_irq; + +	return 0; + +error_unreg_irq: +	if (client->irq) +		free_irq(client->irq, indio_dev); +error_disable_reg: +	if (chip->reg) +		regulator_disable(chip->reg); + +	return ret; +} + +static int ad7291_remove(struct i2c_client *client) +{ +	struct iio_dev *indio_dev = i2c_get_clientdata(client); +	struct ad7291_chip_info *chip = iio_priv(indio_dev); + +	iio_device_unregister(indio_dev); + +	if (client->irq) +		free_irq(client->irq, indio_dev); + +	if (chip->reg) +		regulator_disable(chip->reg); + +	return 0; +} + +static const struct i2c_device_id ad7291_id[] = { +	{ "ad7291", 0 }, +	{} +}; + +MODULE_DEVICE_TABLE(i2c, ad7291_id); + +static struct i2c_driver ad7291_driver = { +	.driver = { +		.name = KBUILD_MODNAME, +	}, +	.probe = ad7291_probe, +	.remove = ad7291_remove, +	.id_table = ad7291_id, +}; +module_i2c_driver(ad7291_driver); + +MODULE_AUTHOR("Sonic Zhang <sonic.zhang@analog.com>"); +MODULE_DESCRIPTION("Analog Devices AD7291 ADC driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/iio/adc/ad7291.h b/drivers/staging/iio/adc/ad7291.h new file mode 100644 index 00000000000..bbd89fa5118 --- /dev/null +++ b/drivers/staging/iio/adc/ad7291.h @@ -0,0 +1,12 @@ +#ifndef __IIO_AD7291_H__ +#define __IIO_AD7291_H__ + +/** + * struct ad7291_platform_data - AD7291 platform data + * @use_external_ref: Whether to use an external or internal reference voltage + */ +struct ad7291_platform_data { +	bool use_external_ref; +}; + +#endif diff --git a/drivers/staging/iio/adc/ad7476.h b/drivers/staging/iio/adc/ad7476.h deleted file mode 100644 index b51b49e4abd..00000000000 --- a/drivers/staging/iio/adc/ad7476.h +++ /dev/null @@ -1,77 +0,0 @@ -/* - * AD7476/5/7/8 (A) SPI ADC driver - * - * Copyright 2010 Analog Devices Inc. - * - * Licensed under the GPL-2 or later. - */ -#ifndef IIO_ADC_AD7476_H_ -#define IIO_ADC_AD7476_H_ - -#define RES_MASK(bits)	((1 << (bits)) - 1) - -/* - * TODO: struct ad7476_platform_data needs to go into include/linux/iio - */ - -struct ad7476_platform_data { -	u16				vref_mv; -}; - -struct ad7476_chip_info { -	u8				bits; -	u8				storagebits; -	u8				res_shift; -	char				sign; -	u16				int_vref_mv; -}; - -struct ad7476_state { -	struct iio_dev			*indio_dev; -	struct spi_device		*spi; -	const struct ad7476_chip_info	*chip_info; -	struct regulator		*reg; -	struct work_struct		poll_work; -	atomic_t			protect_ring; -	u16				int_vref_mv; -	struct spi_transfer		xfer; -	struct spi_message		msg; -	/* -	 * DMA (thus cache coherency maintenance) requires the -	 * transfer buffers to live in their own cache lines. -	 */ -	unsigned char			data[2] ____cacheline_aligned; -}; - -enum ad7476_supported_device_ids { -	ID_AD7466, -	ID_AD7467, -	ID_AD7468, -	ID_AD7475, -	ID_AD7476, -	ID_AD7477, -	ID_AD7478, -	ID_AD7495 -}; - -#ifdef CONFIG_IIO_RING_BUFFER -int ad7476_scan_from_ring(struct ad7476_state *st); -int ad7476_register_ring_funcs_and_init(struct iio_dev *indio_dev); -void ad7476_ring_cleanup(struct iio_dev *indio_dev); -#else /* CONFIG_IIO_RING_BUFFER */ -static inline int ad7476_scan_from_ring(struct ad7476_state *st) -{ -	return 0; -} - -static inline int -ad7476_register_ring_funcs_and_init(struct iio_dev *indio_dev) -{ -	return 0; -} - -static inline void ad7476_ring_cleanup(struct iio_dev *indio_dev) -{ -} -#endif /* CONFIG_IIO_RING_BUFFER */ -#endif /* IIO_ADC_AD7476_H_ */ diff --git a/drivers/staging/iio/adc/ad7476_core.c b/drivers/staging/iio/adc/ad7476_core.c deleted file mode 100644 index deb68c8a6e1..00000000000 --- a/drivers/staging/iio/adc/ad7476_core.c +++ /dev/null @@ -1,293 +0,0 @@ -/* - * AD7466/7/8 AD7476/5/7/8 (A) SPI ADC driver - * - * Copyright 2010 Analog Devices Inc. - * - * Licensed under the GPL-2 or later. - */ - -#include <linux/interrupt.h> -#include <linux/workqueue.h> -#include <linux/device.h> -#include <linux/kernel.h> -#include <linux/slab.h> -#include <linux/sysfs.h> -#include <linux/list.h> -#include <linux/spi/spi.h> -#include <linux/regulator/consumer.h> -#include <linux/err.h> - -#include "../iio.h" -#include "../sysfs.h" -#include "../ring_generic.h" -#include "adc.h" - -#include "ad7476.h" - -static int ad7476_scan_direct(struct ad7476_state *st) -{ -	int ret; - -	ret = spi_sync(st->spi, &st->msg); -	if (ret) -		return ret; - -	return (st->data[0] << 8) | st->data[1]; -} - -static ssize_t ad7476_scan(struct device *dev, -			    struct device_attribute *attr, -			    char *buf) -{ -	struct iio_dev *dev_info = dev_get_drvdata(dev); -	struct ad7476_state *st = dev_info->dev_data; -	int ret; - -	mutex_lock(&dev_info->mlock); -	if (iio_ring_enabled(dev_info)) -		ret = ad7476_scan_from_ring(st); -	else -		ret = ad7476_scan_direct(st); -	mutex_unlock(&dev_info->mlock); - -	if (ret < 0) -		return ret; - -	return sprintf(buf, "%d\n", (ret >> st->chip_info->res_shift) & -		       RES_MASK(st->chip_info->bits)); -} -static IIO_DEV_ATTR_IN_RAW(0, ad7476_scan, 0); - -static ssize_t ad7476_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 ad7476_state *st = iio_dev_get_devdata(dev_info); -	/* Corresponds to Vref / 2^(bits) */ -	unsigned int scale_uv = (st->int_vref_mv * 1000) >> st->chip_info->bits; - -	return sprintf(buf, "%d.%d\n", scale_uv / 1000, scale_uv % 1000); -} -static IIO_DEVICE_ATTR(in_scale, S_IRUGO, ad7476_show_scale, NULL, 0); - -static ssize_t ad7476_show_name(struct device *dev, -				 struct device_attribute *attr, -				 char *buf) -{ -	struct iio_dev *dev_info = dev_get_drvdata(dev); -	struct ad7476_state *st = iio_dev_get_devdata(dev_info); - -	return sprintf(buf, "%s\n", spi_get_device_id(st->spi)->name); -} -static IIO_DEVICE_ATTR(name, S_IRUGO, ad7476_show_name, NULL, 0); - -static struct attribute *ad7476_attributes[] = { -	&iio_dev_attr_in0_raw.dev_attr.attr, -	&iio_dev_attr_in_scale.dev_attr.attr, -	&iio_dev_attr_name.dev_attr.attr, -	NULL, -}; - -static const struct attribute_group ad7476_attribute_group = { -	.attrs = ad7476_attributes, -}; - -static const struct ad7476_chip_info ad7476_chip_info_tbl[] = { -	[ID_AD7466] = { -		.bits = 12, -		.storagebits = 16, -		.res_shift = 0, -		.sign = IIO_SCAN_EL_TYPE_UNSIGNED, -	}, -	[ID_AD7467] = { -		.bits = 10, -		.storagebits = 16, -		.res_shift = 2, -		.sign = IIO_SCAN_EL_TYPE_UNSIGNED, -	}, -	[ID_AD7468] = { -		.bits = 8, -		.storagebits = 16, -		.res_shift = 4, -		.sign = IIO_SCAN_EL_TYPE_UNSIGNED, -	}, -	[ID_AD7475] = { -		.bits = 12, -		.storagebits = 16, -		.res_shift = 0, -		.sign = IIO_SCAN_EL_TYPE_UNSIGNED, -	}, -	[ID_AD7476] = { -		.bits = 12, -		.storagebits = 16, -		.res_shift = 0, -		.sign = IIO_SCAN_EL_TYPE_UNSIGNED, -	}, -	[ID_AD7477] = { -		.bits = 10, -		.storagebits = 16, -		.res_shift = 2, -		.sign = IIO_SCAN_EL_TYPE_UNSIGNED, -	}, -	[ID_AD7478] = { -		.bits = 8, -		.storagebits = 16, -		.res_shift = 4, -		.sign = IIO_SCAN_EL_TYPE_UNSIGNED, -	}, -	[ID_AD7495] = { -		.bits = 12, -		.storagebits = 16, -		.res_shift = 0, -		.int_vref_mv = 2500, -		.sign = IIO_SCAN_EL_TYPE_UNSIGNED, -	}, -}; - -static int __devinit ad7476_probe(struct spi_device *spi) -{ -	struct ad7476_platform_data *pdata = spi->dev.platform_data; -	struct ad7476_state *st; -	int ret, voltage_uv = 0; - -	st = kzalloc(sizeof(*st), GFP_KERNEL); -	if (st == NULL) { -		ret = -ENOMEM; -		goto error_ret; -	} - -	st->reg = regulator_get(&spi->dev, "vcc"); -	if (!IS_ERR(st->reg)) { -		ret = regulator_enable(st->reg); -		if (ret) -			goto error_put_reg; - -		voltage_uv = regulator_get_voltage(st->reg); -	} - -	st->chip_info = -		&ad7476_chip_info_tbl[spi_get_device_id(spi)->driver_data]; - -	if (st->chip_info->int_vref_mv) -		st->int_vref_mv = st->chip_info->int_vref_mv; -	else if (pdata && pdata->vref_mv) -		st->int_vref_mv = pdata->vref_mv; -	else if (voltage_uv) -		st->int_vref_mv = voltage_uv / 1000; -	else -		dev_warn(&spi->dev, "reference voltage unspecified\n"); - -	spi_set_drvdata(spi, st); - -	atomic_set(&st->protect_ring, 0); -	st->spi = spi; - -	st->indio_dev = iio_allocate_device(); -	if (st->indio_dev == NULL) { -		ret = -ENOMEM; -		goto error_disable_reg; -	} - -	/* Estabilish that the iio_dev is a child of the i2c device */ -	st->indio_dev->dev.parent = &spi->dev; -	st->indio_dev->attrs = &ad7476_attribute_group; -	st->indio_dev->dev_data = (void *)(st); -	st->indio_dev->driver_module = THIS_MODULE; -	st->indio_dev->modes = INDIO_DIRECT_MODE; - -	/* Setup default message */ - -	st->xfer.rx_buf = &st->data; -	st->xfer.len = st->chip_info->storagebits / 8; - -	spi_message_init(&st->msg); -	spi_message_add_tail(&st->xfer, &st->msg); - -	ret = ad7476_register_ring_funcs_and_init(st->indio_dev); -	if (ret) -		goto error_free_device; - -	ret = iio_device_register(st->indio_dev); -	if (ret) -		goto error_free_device; - -	ret = iio_ring_buffer_register(st->indio_dev->ring, 0); -	if (ret) -		goto error_cleanup_ring; -	return 0; - -error_cleanup_ring: -	ad7476_ring_cleanup(st->indio_dev); -	iio_device_unregister(st->indio_dev); -error_free_device: -	iio_free_device(st->indio_dev); -error_disable_reg: -	if (!IS_ERR(st->reg)) -		regulator_disable(st->reg); -error_put_reg: -	if (!IS_ERR(st->reg)) -		regulator_put(st->reg); -	kfree(st); -error_ret: -	return ret; -} - -static int ad7476_remove(struct spi_device *spi) -{ -	struct ad7476_state *st = spi_get_drvdata(spi); -	struct iio_dev *indio_dev = st->indio_dev; -	iio_ring_buffer_unregister(indio_dev->ring); -	ad7476_ring_cleanup(indio_dev); -	iio_device_unregister(indio_dev); -	if (!IS_ERR(st->reg)) { -		regulator_disable(st->reg); -		regulator_put(st->reg); -	} -	kfree(st); -	return 0; -} - -static const struct spi_device_id ad7476_id[] = { -	{"ad7466", ID_AD7466}, -	{"ad7467", ID_AD7467}, -	{"ad7468", ID_AD7468}, -	{"ad7475", ID_AD7475}, -	{"ad7476", ID_AD7476}, -	{"ad7476a", ID_AD7476}, -	{"ad7477", ID_AD7477}, -	{"ad7477a", ID_AD7477}, -	{"ad7478", ID_AD7478}, -	{"ad7478a", ID_AD7478}, -	{"ad7495", ID_AD7495}, -	{} -}; - -static struct spi_driver ad7476_driver = { -	.driver = { -		.name	= "ad7476", -		.bus	= &spi_bus_type, -		.owner	= THIS_MODULE, -	}, -	.probe		= ad7476_probe, -	.remove		= __devexit_p(ad7476_remove), -	.id_table	= ad7476_id, -}; - -static int __init ad7476_init(void) -{ -	return spi_register_driver(&ad7476_driver); -} -module_init(ad7476_init); - -static void __exit ad7476_exit(void) -{ -	spi_unregister_driver(&ad7476_driver); -} -module_exit(ad7476_exit); - -MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>"); -MODULE_DESCRIPTION("Analog Devices AD7475/6/7/8(A) AD7466/7/8 ADC"); -MODULE_LICENSE("GPL v2"); -MODULE_ALIAS("spi:ad7476"); diff --git a/drivers/staging/iio/adc/ad7476_ring.c b/drivers/staging/iio/adc/ad7476_ring.c deleted file mode 100644 index 85de14274ad..00000000000 --- a/drivers/staging/iio/adc/ad7476_ring.c +++ /dev/null @@ -1,207 +0,0 @@ -/* - * Copyright 2010 Analog Devices Inc. - * Copyright (C) 2008 Jonathan Cameron - * - * Licensed under the GPL-2 or later. - * - * ad7476_ring.c - */ - -#include <linux/interrupt.h> -#include <linux/gpio.h> -#include <linux/workqueue.h> -#include <linux/device.h> -#include <linux/kernel.h> -#include <linux/slab.h> -#include <linux/sysfs.h> -#include <linux/list.h> -#include <linux/spi/spi.h> - -#include "../iio.h" -#include "../ring_generic.h" -#include "../ring_sw.h" -#include "../trigger.h" -#include "../sysfs.h" - -#include "ad7476.h" - -static IIO_SCAN_EL_C(in0, 0, 0, NULL); - -static ssize_t ad7476_show_type(struct device *dev, -				struct device_attribute *attr, -				char *buf) -{ -	struct iio_ring_buffer *ring = dev_get_drvdata(dev); -	struct iio_dev *indio_dev = ring->indio_dev; -	struct ad7476_state *st = indio_dev->dev_data; - -	return sprintf(buf, "%c%d/%d>>%d\n", st->chip_info->sign, -		       st->chip_info->bits, st->chip_info->storagebits, -		       st->chip_info->res_shift); -} -static IIO_DEVICE_ATTR(in_type, S_IRUGO, ad7476_show_type, NULL, 0); - -static struct attribute *ad7476_scan_el_attrs[] = { -	&iio_scan_el_in0.dev_attr.attr, -	&iio_const_attr_in0_index.dev_attr.attr, -	&iio_dev_attr_in_type.dev_attr.attr, -	NULL, -}; - -static struct attribute_group ad7476_scan_el_group = { -	.name = "scan_elements", -	.attrs = ad7476_scan_el_attrs, -}; - -int ad7476_scan_from_ring(struct ad7476_state *st) -{ -	struct iio_ring_buffer *ring = st->indio_dev->ring; -	int ret; -	u8 *ring_data; - -	ring_data = kmalloc(ring->access.get_bytes_per_datum(ring), GFP_KERNEL); -	if (ring_data == NULL) { -		ret = -ENOMEM; -		goto error_ret; -	} -	ret = ring->access.read_last(ring, ring_data); -	if (ret) -		goto error_free_ring_data; - -	ret = (ring_data[0] << 8) | ring_data[1]; - -error_free_ring_data: -	kfree(ring_data); -error_ret: -	return ret; -} - -/** - * ad7476_ring_preenable() setup the parameters of the ring before enabling - * - * The complex nature of the setting of the nuber of bytes per datum is due - * to this driver currently ensuring that the timestamp is stored at an 8 - * byte boundary. - **/ -static int ad7476_ring_preenable(struct iio_dev *indio_dev) -{ -	struct ad7476_state *st = indio_dev->dev_data; -	size_t d_size; - -	if (indio_dev->ring->access.set_bytes_per_datum) { -		d_size = st->chip_info->storagebits / 8 + sizeof(s64); -		if (d_size % 8) -			d_size += 8 - (d_size % 8); -		indio_dev->ring->access.set_bytes_per_datum(indio_dev->ring, -							    d_size); -	} - -	return 0; -} - -/** - * ad7476_poll_func_th() th of trigger launched polling to ring buffer - * - * As sampling only occurs on i2c comms occuring, leave timestamping until - * then.  Some triggers will generate their own time stamp.  Currently - * there is no way of notifying them when no one cares. - **/ -static void ad7476_poll_func_th(struct iio_dev *indio_dev, s64 time) -{ -	struct ad7476_state *st = indio_dev->dev_data; - -	schedule_work(&st->poll_work); -	return; -} -/** - * ad7476_poll_bh_to_ring() bh of trigger launched polling to ring buffer - * @work_s:	the work struct through which this was scheduled - * - * Currently there is no option in this driver to disable the saving of - * timestamps within the ring. - * I think the one copy of this at a time was to avoid problems if the - * trigger was set far too high and the reads then locked up the computer. - **/ -static void ad7476_poll_bh_to_ring(struct work_struct *work_s) -{ -	struct ad7476_state *st = container_of(work_s, struct ad7476_state, -						  poll_work); -	struct iio_dev *indio_dev = st->indio_dev; -	struct iio_sw_ring_buffer *sw_ring = iio_to_sw_ring(indio_dev->ring); -	s64 time_ns; -	__u8 *rxbuf; -	int b_sent; -	size_t d_size; - -	/* Ensure the timestamp is 8 byte aligned */ -	d_size = st->chip_info->storagebits / 8 + sizeof(s64); -	if (d_size % sizeof(s64)) -		d_size += sizeof(s64) - (d_size % sizeof(s64)); - -	/* Ensure only one copy of this function running at a time */ -	if (atomic_inc_return(&st->protect_ring) > 1) -		return; - -	rxbuf = kzalloc(d_size,	GFP_KERNEL); -	if (rxbuf == NULL) -		return; - -	b_sent = spi_read(st->spi, rxbuf, st->chip_info->storagebits / 8); -	if (b_sent < 0) -		goto done; - -	time_ns = iio_get_time_ns(); - -	memcpy(rxbuf + d_size - sizeof(s64), &time_ns, sizeof(time_ns)); - -	indio_dev->ring->access.store_to(&sw_ring->buf, rxbuf, time_ns); -done: -	kfree(rxbuf); -	atomic_dec(&st->protect_ring); -} - -int ad7476_register_ring_funcs_and_init(struct iio_dev *indio_dev) -{ -	struct ad7476_state *st = indio_dev->dev_data; -	int ret = 0; - -	indio_dev->ring = iio_sw_rb_allocate(indio_dev); -	if (!indio_dev->ring) { -		ret = -ENOMEM; -		goto error_ret; -	} -	/* Effectively select the ring buffer implementation */ -	iio_ring_sw_register_funcs(&indio_dev->ring->access); -	ret = iio_alloc_pollfunc(indio_dev, NULL, &ad7476_poll_func_th); -	if (ret) -		goto error_deallocate_sw_rb; - -	/* Ring buffer functions - here trigger setup related */ - -	indio_dev->ring->preenable = &ad7476_ring_preenable; -	indio_dev->ring->postenable = &iio_triggered_ring_postenable; -	indio_dev->ring->predisable = &iio_triggered_ring_predisable; -	indio_dev->ring->scan_el_attrs = &ad7476_scan_el_group; - -	INIT_WORK(&st->poll_work, &ad7476_poll_bh_to_ring); - -	/* Flag that polled ring buffering is possible */ -	indio_dev->modes |= INDIO_RING_TRIGGERED; -	return 0; -error_deallocate_sw_rb: -	iio_sw_rb_free(indio_dev->ring); -error_ret: -	return ret; -} - -void ad7476_ring_cleanup(struct iio_dev *indio_dev) -{ -	/* ensure that the trigger has been detached */ -	if (indio_dev->trig) { -		iio_put_trigger(indio_dev->trig); -		iio_trigger_dettach_poll_func(indio_dev->trig, -					      indio_dev->pollfunc); -	} -	kfree(indio_dev->pollfunc); -	iio_sw_rb_free(indio_dev->ring); -} diff --git a/drivers/staging/iio/adc/ad7606.h b/drivers/staging/iio/adc/ad7606.h new file mode 100644 index 00000000000..ec89d055cf5 --- /dev/null +++ b/drivers/staging/iio/adc/ad7606.h @@ -0,0 +1,104 @@ +/* + * AD7606 ADC driver + * + * Copyright 2011 Analog Devices Inc. + * + * Licensed under the GPL-2. + */ + +#ifndef IIO_ADC_AD7606_H_ +#define IIO_ADC_AD7606_H_ + +/* + * TODO: struct ad7606_platform_data needs to go into include/linux/iio + */ + +/** + * struct ad7606_platform_data - platform/board specific information + * @default_os:		default oversampling value {0, 2, 4, 8, 16, 32, 64} + * @default_range:	default range +/-{5000, 10000} mVolt + * @gpio_convst:	number of gpio connected to the CONVST pin + * @gpio_reset:		gpio connected to the RESET pin, if not used set to -1 + * @gpio_range:		gpio connected to the RANGE pin, if not used set to -1 + * @gpio_os0:		gpio connected to the OS0 pin, if not used set to -1 + * @gpio_os1:		gpio connected to the OS1 pin, if not used set to -1 + * @gpio_os2:		gpio connected to the OS2 pin, if not used set to -1 + * @gpio_frstdata:	gpio connected to the FRSTDAT pin, if not used set to -1 + * @gpio_stby:		gpio connected to the STBY pin, if not used set to -1 + */ + +struct ad7606_platform_data { +	unsigned			default_os; +	unsigned			default_range; +	unsigned			gpio_convst; +	unsigned			gpio_reset; +	unsigned			gpio_range; +	unsigned			gpio_os0; +	unsigned			gpio_os1; +	unsigned			gpio_os2; +	unsigned			gpio_frstdata; +	unsigned			gpio_stby; +}; + +/** + * struct ad7606_chip_info - chip specific information + * @name:		identification string for chip + * @int_vref_mv:	the internal reference voltage + * @channels:		channel specification + * @num_channels:	number of channels + */ + +struct ad7606_chip_info { +	const char			*name; +	u16				int_vref_mv; +	const struct iio_chan_spec	*channels; +	unsigned			num_channels; +}; + +/** + * struct ad7606_state - driver instance specific data + */ + +struct ad7606_state { +	struct device			*dev; +	const struct ad7606_chip_info	*chip_info; +	struct ad7606_platform_data	*pdata; +	struct regulator		*reg; +	struct work_struct		poll_work; +	wait_queue_head_t		wq_data_avail; +	const struct ad7606_bus_ops	*bops; +	unsigned			range; +	unsigned			oversampling; +	bool				done; +	void __iomem			*base_address; + +	/* +	 * DMA (thus cache coherency maintenance) requires the +	 * transfer buffers to live in their own cache lines. +	 */ + +	unsigned short			data[8] ____cacheline_aligned; +}; + +struct ad7606_bus_ops { +	/* more methods added in future? */ +	int (*read_block)(struct device *, int, void *); +}; + +void ad7606_suspend(struct iio_dev *indio_dev); +void ad7606_resume(struct iio_dev *indio_dev); +struct iio_dev *ad7606_probe(struct device *dev, int irq, +			      void __iomem *base_address, unsigned id, +			      const struct ad7606_bus_ops *bops); +int ad7606_remove(struct iio_dev *indio_dev, int irq); +int ad7606_reset(struct ad7606_state *st); + +enum ad7606_supported_device_ids { +	ID_AD7606_8, +	ID_AD7606_6, +	ID_AD7606_4 +}; + +int ad7606_register_ring_funcs_and_init(struct iio_dev *indio_dev); +void ad7606_ring_cleanup(struct iio_dev *indio_dev); +#endif /* IIO_ADC_AD7606_H_ */ diff --git a/drivers/staging/iio/adc/ad7606_core.c b/drivers/staging/iio/adc/ad7606_core.c new file mode 100644 index 00000000000..f0f05f195d2 --- /dev/null +++ b/drivers/staging/iio/adc/ad7606_core.c @@ -0,0 +1,603 @@ +/* + * AD7606 SPI ADC driver + * + * Copyright 2011 Analog Devices Inc. + * + * Licensed under the GPL-2. + */ + +#include <linux/interrupt.h> +#include <linux/device.h> +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/sysfs.h> +#include <linux/regulator/consumer.h> +#include <linux/err.h> +#include <linux/gpio.h> +#include <linux/delay.h> +#include <linux/sched.h> +#include <linux/module.h> + +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> +#include <linux/iio/buffer.h> + +#include "ad7606.h" + +int ad7606_reset(struct ad7606_state *st) +{ +	if (gpio_is_valid(st->pdata->gpio_reset)) { +		gpio_set_value(st->pdata->gpio_reset, 1); +		ndelay(100); /* t_reset >= 100ns */ +		gpio_set_value(st->pdata->gpio_reset, 0); +		return 0; +	} + +	return -ENODEV; +} + +static int ad7606_scan_direct(struct iio_dev *indio_dev, unsigned ch) +{ +	struct ad7606_state *st = iio_priv(indio_dev); +	int ret; + +	st->done = false; +	gpio_set_value(st->pdata->gpio_convst, 1); + +	ret = wait_event_interruptible(st->wq_data_avail, st->done); +	if (ret) +		goto error_ret; + +	if (gpio_is_valid(st->pdata->gpio_frstdata)) { +		ret = st->bops->read_block(st->dev, 1, st->data); +		if (ret) +			goto error_ret; +		if (!gpio_get_value(st->pdata->gpio_frstdata)) { +			/* This should never happen */ +			ad7606_reset(st); +			ret = -EIO; +			goto error_ret; +		} +		ret = st->bops->read_block(st->dev, +			st->chip_info->num_channels - 1, &st->data[1]); +		if (ret) +			goto error_ret; +	} else { +		ret = st->bops->read_block(st->dev, +			st->chip_info->num_channels, st->data); +		if (ret) +			goto error_ret; +	} + +	ret = st->data[ch]; + +error_ret: +	gpio_set_value(st->pdata->gpio_convst, 0); + +	return ret; +} + +static int ad7606_read_raw(struct iio_dev *indio_dev, +			   struct iio_chan_spec const *chan, +			   int *val, +			   int *val2, +			   long m) +{ +	int ret; +	struct ad7606_state *st = iio_priv(indio_dev); + +	switch (m) { +	case IIO_CHAN_INFO_RAW: +		mutex_lock(&indio_dev->mlock); +		if (iio_buffer_enabled(indio_dev)) +			ret = -EBUSY; +		else +			ret = ad7606_scan_direct(indio_dev, chan->address); +		mutex_unlock(&indio_dev->mlock); + +		if (ret < 0) +			return ret; +		*val = (short) ret; +		return IIO_VAL_INT; +	case IIO_CHAN_INFO_SCALE: +		*val = st->range * 2; +		*val2 = st->chip_info->channels[0].scan_type.realbits; +		return IIO_VAL_FRACTIONAL_LOG2; +	} +	return -EINVAL; +} + +static ssize_t ad7606_show_range(struct device *dev, +			struct device_attribute *attr, char *buf) +{ +	struct iio_dev *indio_dev = dev_to_iio_dev(dev); +	struct ad7606_state *st = iio_priv(indio_dev); + +	return sprintf(buf, "%u\n", st->range); +} + +static ssize_t ad7606_store_range(struct device *dev, +		struct device_attribute *attr, const char *buf, size_t count) +{ +	struct iio_dev *indio_dev = dev_to_iio_dev(dev); +	struct ad7606_state *st = iio_priv(indio_dev); +	unsigned long lval; +	int ret; + +	ret = kstrtoul(buf, 10, &lval); +	if (ret) +		return ret; + +	if (!(lval == 5000 || lval == 10000)) { +		dev_err(dev, "range is not supported\n"); +		return -EINVAL; +	} +	mutex_lock(&indio_dev->mlock); +	gpio_set_value(st->pdata->gpio_range, lval == 10000); +	st->range = lval; +	mutex_unlock(&indio_dev->mlock); + +	return count; +} + +static IIO_DEVICE_ATTR(in_voltage_range, S_IRUGO | S_IWUSR, \ +		       ad7606_show_range, ad7606_store_range, 0); +static IIO_CONST_ATTR(in_voltage_range_available, "5000 10000"); + +static ssize_t ad7606_show_oversampling_ratio(struct device *dev, +			struct device_attribute *attr, char *buf) +{ +	struct iio_dev *indio_dev = dev_to_iio_dev(dev); +	struct ad7606_state *st = iio_priv(indio_dev); + +	return sprintf(buf, "%u\n", st->oversampling); +} + +static int ad7606_oversampling_get_index(unsigned val) +{ +	unsigned char supported[] = {0, 2, 4, 8, 16, 32, 64}; +	int i; + +	for (i = 0; i < ARRAY_SIZE(supported); i++) +		if (val == supported[i]) +			return i; + +	return -EINVAL; +} + +static ssize_t ad7606_store_oversampling_ratio(struct device *dev, +		struct device_attribute *attr, const char *buf, size_t count) +{ +	struct iio_dev *indio_dev = dev_to_iio_dev(dev); +	struct ad7606_state *st = iio_priv(indio_dev); +	unsigned long lval; +	int ret; + +	ret = kstrtoul(buf, 10, &lval); +	if (ret) +		return ret; + +	ret = ad7606_oversampling_get_index(lval); +	if (ret < 0) { +		dev_err(dev, "oversampling %lu is not supported\n", lval); +		return ret; +	} + +	mutex_lock(&indio_dev->mlock); +	gpio_set_value(st->pdata->gpio_os0, (ret >> 0) & 1); +	gpio_set_value(st->pdata->gpio_os1, (ret >> 1) & 1); +	gpio_set_value(st->pdata->gpio_os1, (ret >> 2) & 1); +	st->oversampling = lval; +	mutex_unlock(&indio_dev->mlock); + +	return count; +} + +static IIO_DEVICE_ATTR(oversampling_ratio, S_IRUGO | S_IWUSR, +		       ad7606_show_oversampling_ratio, +		       ad7606_store_oversampling_ratio, 0); +static IIO_CONST_ATTR(oversampling_ratio_available, "0 2 4 8 16 32 64"); + +static struct attribute *ad7606_attributes_os_and_range[] = { +	&iio_dev_attr_in_voltage_range.dev_attr.attr, +	&iio_const_attr_in_voltage_range_available.dev_attr.attr, +	&iio_dev_attr_oversampling_ratio.dev_attr.attr, +	&iio_const_attr_oversampling_ratio_available.dev_attr.attr, +	NULL, +}; + +static const struct attribute_group ad7606_attribute_group_os_and_range = { +	.attrs = ad7606_attributes_os_and_range, +}; + +static struct attribute *ad7606_attributes_os[] = { +	&iio_dev_attr_oversampling_ratio.dev_attr.attr, +	&iio_const_attr_oversampling_ratio_available.dev_attr.attr, +	NULL, +}; + +static const struct attribute_group ad7606_attribute_group_os = { +	.attrs = ad7606_attributes_os, +}; + +static struct attribute *ad7606_attributes_range[] = { +	&iio_dev_attr_in_voltage_range.dev_attr.attr, +	&iio_const_attr_in_voltage_range_available.dev_attr.attr, +	NULL, +}; + +static const struct attribute_group ad7606_attribute_group_range = { +	.attrs = ad7606_attributes_range, +}; + +#define AD7606_CHANNEL(num)					\ +	{							\ +		.type = IIO_VOLTAGE,				\ +		.indexed = 1,					\ +		.channel = num,					\ +		.address = num,					\ +		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),	\ +		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),\ +		.scan_index = num,				\ +		.scan_type = {					\ +			.sign = 's',				\ +			.realbits = 16,				\ +			.storagebits = 16,			\ +			.endianness = IIO_CPU,			\ +		},						\ +	} + +static const struct iio_chan_spec ad7606_8_channels[] = { +	AD7606_CHANNEL(0), +	AD7606_CHANNEL(1), +	AD7606_CHANNEL(2), +	AD7606_CHANNEL(3), +	AD7606_CHANNEL(4), +	AD7606_CHANNEL(5), +	AD7606_CHANNEL(6), +	AD7606_CHANNEL(7), +	IIO_CHAN_SOFT_TIMESTAMP(8), +}; + +static const struct iio_chan_spec ad7606_6_channels[] = { +	AD7606_CHANNEL(0), +	AD7606_CHANNEL(1), +	AD7606_CHANNEL(2), +	AD7606_CHANNEL(3), +	AD7606_CHANNEL(4), +	AD7606_CHANNEL(5), +	IIO_CHAN_SOFT_TIMESTAMP(6), +}; + +static const struct iio_chan_spec ad7606_4_channels[] = { +	AD7606_CHANNEL(0), +	AD7606_CHANNEL(1), +	AD7606_CHANNEL(2), +	AD7606_CHANNEL(3), +	IIO_CHAN_SOFT_TIMESTAMP(4), +}; + +static const struct ad7606_chip_info ad7606_chip_info_tbl[] = { +	/* +	 * More devices added in future +	 */ +	[ID_AD7606_8] = { +		.name = "ad7606", +		.int_vref_mv = 2500, +		.channels = ad7606_8_channels, +		.num_channels = 8, +	}, +	[ID_AD7606_6] = { +		.name = "ad7606-6", +		.int_vref_mv = 2500, +		.channels = ad7606_6_channels, +		.num_channels = 6, +	}, +	[ID_AD7606_4] = { +		.name = "ad7606-4", +		.int_vref_mv = 2500, +		.channels = ad7606_4_channels, +		.num_channels = 4, +	}, +}; + +static int ad7606_request_gpios(struct ad7606_state *st) +{ +	struct gpio gpio_array[3] = { +		[0] = { +			.gpio =  st->pdata->gpio_os0, +			.flags = GPIOF_DIR_OUT | ((st->oversampling & 1) ? +				 GPIOF_INIT_HIGH : GPIOF_INIT_LOW), +			.label = "AD7606_OS0", +		}, +		[1] = { +			.gpio =  st->pdata->gpio_os1, +			.flags = GPIOF_DIR_OUT | ((st->oversampling & 2) ? +				 GPIOF_INIT_HIGH : GPIOF_INIT_LOW), +			.label = "AD7606_OS1", +		}, +		[2] = { +			.gpio =  st->pdata->gpio_os2, +			.flags = GPIOF_DIR_OUT | ((st->oversampling & 4) ? +				 GPIOF_INIT_HIGH : GPIOF_INIT_LOW), +			.label = "AD7606_OS2", +		}, +	}; +	int ret; + +	if (gpio_is_valid(st->pdata->gpio_convst)) { +		ret = gpio_request_one(st->pdata->gpio_convst, +				       GPIOF_OUT_INIT_LOW, +				       "AD7606_CONVST"); +		if (ret) { +			dev_err(st->dev, "failed to request GPIO CONVST\n"); +			goto error_ret; +		} +	} else { +		ret = -EIO; +		goto error_ret; +	} + +	if (gpio_is_valid(st->pdata->gpio_os0) && +	    gpio_is_valid(st->pdata->gpio_os1) && +	    gpio_is_valid(st->pdata->gpio_os2)) { +		ret = gpio_request_array(gpio_array, ARRAY_SIZE(gpio_array)); +		if (ret < 0) +			goto error_free_convst; +	} + +	if (gpio_is_valid(st->pdata->gpio_reset)) { +		ret = gpio_request_one(st->pdata->gpio_reset, +				       GPIOF_OUT_INIT_LOW, +				       "AD7606_RESET"); +		if (ret < 0) +			goto error_free_os; +	} + +	if (gpio_is_valid(st->pdata->gpio_range)) { +		ret = gpio_request_one(st->pdata->gpio_range, GPIOF_DIR_OUT | +				       ((st->range == 10000) ? GPIOF_INIT_HIGH : +					GPIOF_INIT_LOW), "AD7606_RANGE"); +		if (ret < 0) +			goto error_free_reset; +	} +	if (gpio_is_valid(st->pdata->gpio_stby)) { +		ret = gpio_request_one(st->pdata->gpio_stby, +				       GPIOF_OUT_INIT_HIGH, +				       "AD7606_STBY"); +		if (ret < 0) +			goto error_free_range; +	} + +	if (gpio_is_valid(st->pdata->gpio_frstdata)) { +		ret = gpio_request_one(st->pdata->gpio_frstdata, GPIOF_IN, +				       "AD7606_FRSTDATA"); +		if (ret < 0) +			goto error_free_stby; +	} + +	return 0; + +error_free_stby: +	if (gpio_is_valid(st->pdata->gpio_stby)) +		gpio_free(st->pdata->gpio_stby); +error_free_range: +	if (gpio_is_valid(st->pdata->gpio_range)) +		gpio_free(st->pdata->gpio_range); +error_free_reset: +	if (gpio_is_valid(st->pdata->gpio_reset)) +		gpio_free(st->pdata->gpio_reset); +error_free_os: +	if (gpio_is_valid(st->pdata->gpio_os0) && +	    gpio_is_valid(st->pdata->gpio_os1) && +	    gpio_is_valid(st->pdata->gpio_os2)) +		gpio_free_array(gpio_array, ARRAY_SIZE(gpio_array)); +error_free_convst: +	gpio_free(st->pdata->gpio_convst); +error_ret: +	return ret; +} + +static void ad7606_free_gpios(struct ad7606_state *st) +{ +	if (gpio_is_valid(st->pdata->gpio_frstdata)) +		gpio_free(st->pdata->gpio_frstdata); +	if (gpio_is_valid(st->pdata->gpio_stby)) +		gpio_free(st->pdata->gpio_stby); +	if (gpio_is_valid(st->pdata->gpio_range)) +		gpio_free(st->pdata->gpio_range); +	if (gpio_is_valid(st->pdata->gpio_reset)) +		gpio_free(st->pdata->gpio_reset); +	if (gpio_is_valid(st->pdata->gpio_os0) && +	    gpio_is_valid(st->pdata->gpio_os1) && +	    gpio_is_valid(st->pdata->gpio_os2)) { +		gpio_free(st->pdata->gpio_os2); +		gpio_free(st->pdata->gpio_os1); +		gpio_free(st->pdata->gpio_os0); +	} +	gpio_free(st->pdata->gpio_convst); +} + +/** + *  Interrupt handler + */ +static irqreturn_t ad7606_interrupt(int irq, void *dev_id) +{ +	struct iio_dev *indio_dev = dev_id; +	struct ad7606_state *st = iio_priv(indio_dev); + +	if (iio_buffer_enabled(indio_dev)) { +		schedule_work(&st->poll_work); +	} else { +		st->done = true; +		wake_up_interruptible(&st->wq_data_avail); +	} + +	return IRQ_HANDLED; +}; + +static const struct iio_info ad7606_info_no_os_or_range = { +	.driver_module = THIS_MODULE, +	.read_raw = &ad7606_read_raw, +}; + +static const struct iio_info ad7606_info_os_and_range = { +	.driver_module = THIS_MODULE, +	.read_raw = &ad7606_read_raw, +	.attrs = &ad7606_attribute_group_os_and_range, +}; + +static const struct iio_info ad7606_info_os = { +	.driver_module = THIS_MODULE, +	.read_raw = &ad7606_read_raw, +	.attrs = &ad7606_attribute_group_os, +}; + +static const struct iio_info ad7606_info_range = { +	.driver_module = THIS_MODULE, +	.read_raw = &ad7606_read_raw, +	.attrs = &ad7606_attribute_group_range, +}; + +struct iio_dev *ad7606_probe(struct device *dev, int irq, +			      void __iomem *base_address, +			      unsigned id, +			      const struct ad7606_bus_ops *bops) +{ +	struct ad7606_platform_data *pdata = dev->platform_data; +	struct ad7606_state *st; +	int ret; +	struct iio_dev *indio_dev; + +	indio_dev = devm_iio_device_alloc(dev, sizeof(*st)); +	if (!indio_dev) +		return ERR_PTR(-ENOMEM); + +	st = iio_priv(indio_dev); + +	st->dev = dev; +	st->bops = bops; +	st->base_address = base_address; +	st->range = pdata->default_range == 10000 ? 10000 : 5000; + +	ret = ad7606_oversampling_get_index(pdata->default_os); +	if (ret < 0) { +		dev_warn(dev, "oversampling %d is not supported\n", +			 pdata->default_os); +		st->oversampling = 0; +	} else { +		st->oversampling = pdata->default_os; +	} + +	st->reg = devm_regulator_get(dev, "vcc"); +	if (!IS_ERR(st->reg)) { +		ret = regulator_enable(st->reg); +		if (ret) +			return ERR_PTR(ret); +	} + +	st->pdata = pdata; +	st->chip_info = &ad7606_chip_info_tbl[id]; + +	indio_dev->dev.parent = dev; +	if (gpio_is_valid(st->pdata->gpio_os0) && +	    gpio_is_valid(st->pdata->gpio_os1) && +	    gpio_is_valid(st->pdata->gpio_os2)) { +		if (gpio_is_valid(st->pdata->gpio_range)) +			indio_dev->info = &ad7606_info_os_and_range; +		else +			indio_dev->info = &ad7606_info_os; +	} else { +		if (gpio_is_valid(st->pdata->gpio_range)) +			indio_dev->info = &ad7606_info_range; +		else +			indio_dev->info = &ad7606_info_no_os_or_range; +	} +	indio_dev->modes = INDIO_DIRECT_MODE; +	indio_dev->name = st->chip_info->name; +	indio_dev->channels = st->chip_info->channels; +	indio_dev->num_channels = st->chip_info->num_channels; + +	init_waitqueue_head(&st->wq_data_avail); + +	ret = ad7606_request_gpios(st); +	if (ret) +		goto error_disable_reg; + +	ret = ad7606_reset(st); +	if (ret) +		dev_warn(st->dev, "failed to RESET: no RESET GPIO specified\n"); + +	ret = request_irq(irq, ad7606_interrupt, +		IRQF_TRIGGER_FALLING, st->chip_info->name, indio_dev); +	if (ret) +		goto error_free_gpios; + +	ret = ad7606_register_ring_funcs_and_init(indio_dev); +	if (ret) +		goto error_free_irq; + +	ret = iio_device_register(indio_dev); +	if (ret) +		goto error_unregister_ring; + +	return indio_dev; +error_unregister_ring: +	ad7606_ring_cleanup(indio_dev); + +error_free_irq: +	free_irq(irq, indio_dev); + +error_free_gpios: +	ad7606_free_gpios(st); + +error_disable_reg: +	if (!IS_ERR(st->reg)) +		regulator_disable(st->reg); +	return ERR_PTR(ret); +} + +int ad7606_remove(struct iio_dev *indio_dev, int irq) +{ +	struct ad7606_state *st = iio_priv(indio_dev); + +	iio_device_unregister(indio_dev); +	ad7606_ring_cleanup(indio_dev); + +	free_irq(irq, indio_dev); +	if (!IS_ERR(st->reg)) +		regulator_disable(st->reg); + +	ad7606_free_gpios(st); + +	return 0; +} + +void ad7606_suspend(struct iio_dev *indio_dev) +{ +	struct ad7606_state *st = iio_priv(indio_dev); + +	if (gpio_is_valid(st->pdata->gpio_stby)) { +		if (gpio_is_valid(st->pdata->gpio_range)) +			gpio_set_value(st->pdata->gpio_range, 1); +		gpio_set_value(st->pdata->gpio_stby, 0); +	} +} + +void ad7606_resume(struct iio_dev *indio_dev) +{ +	struct ad7606_state *st = iio_priv(indio_dev); + +	if (gpio_is_valid(st->pdata->gpio_stby)) { +		if (gpio_is_valid(st->pdata->gpio_range)) +			gpio_set_value(st->pdata->gpio_range, +					st->range == 10000); + +		gpio_set_value(st->pdata->gpio_stby, 1); +		ad7606_reset(st); +	} +} + +MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>"); +MODULE_DESCRIPTION("Analog Devices AD7606 ADC"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/iio/adc/ad7606_par.c b/drivers/staging/iio/adc/ad7606_par.c new file mode 100644 index 00000000000..8a48d18de78 --- /dev/null +++ b/drivers/staging/iio/adc/ad7606_par.c @@ -0,0 +1,178 @@ +/* + * AD7606 Parallel Interface ADC driver + * + * Copyright 2011 Analog Devices Inc. + * + * Licensed under the GPL-2. + */ + +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/types.h> +#include <linux/err.h> +#include <linux/io.h> + +#include <linux/iio/iio.h> +#include "ad7606.h" + +static int ad7606_par16_read_block(struct device *dev, +				 int count, void *buf) +{ +	struct platform_device *pdev = to_platform_device(dev); +	struct iio_dev *indio_dev = platform_get_drvdata(pdev); +	struct ad7606_state *st = iio_priv(indio_dev); + +	insw((unsigned long) st->base_address, buf, count); + +	return 0; +} + +static const struct ad7606_bus_ops ad7606_par16_bops = { +	.read_block	= ad7606_par16_read_block, +}; + +static int ad7606_par8_read_block(struct device *dev, +				 int count, void *buf) +{ +	struct platform_device *pdev = to_platform_device(dev); +	struct iio_dev *indio_dev = platform_get_drvdata(pdev); +	struct ad7606_state *st = iio_priv(indio_dev); + +	insb((unsigned long) st->base_address, buf, count * 2); + +	return 0; +} + +static const struct ad7606_bus_ops ad7606_par8_bops = { +	.read_block	= ad7606_par8_read_block, +}; + +static int ad7606_par_probe(struct platform_device *pdev) +{ +	struct resource *res; +	struct iio_dev *indio_dev; +	void __iomem *addr; +	resource_size_t remap_size; +	int ret, irq; + +	irq = platform_get_irq(pdev, 0); +	if (irq < 0) { +		dev_err(&pdev->dev, "no irq\n"); +		return -ENODEV; +	} + +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0); +	if (!res) +		return -ENODEV; + +	remap_size = resource_size(res); + +	/* Request the regions */ +	if (!request_mem_region(res->start, remap_size, "iio-ad7606")) { +		ret = -EBUSY; +		goto out1; +	} +	addr = ioremap(res->start, remap_size); +	if (!addr) { +		ret = -ENOMEM; +		goto out1; +	} + +	indio_dev = ad7606_probe(&pdev->dev, irq, addr, +			  platform_get_device_id(pdev)->driver_data, +			  remap_size > 1 ? &ad7606_par16_bops : +			  &ad7606_par8_bops); + +	if (IS_ERR(indio_dev))  { +		ret = PTR_ERR(indio_dev); +		goto out2; +	} + +	platform_set_drvdata(pdev, indio_dev); + +	return 0; + +out2: +	iounmap(addr); +out1: +	release_mem_region(res->start, remap_size); + +	return ret; +} + +static int ad7606_par_remove(struct platform_device *pdev) +{ +	struct iio_dev *indio_dev = platform_get_drvdata(pdev); +	struct resource *res; +	struct ad7606_state *st = iio_priv(indio_dev); + +	ad7606_remove(indio_dev, platform_get_irq(pdev, 0)); + +	iounmap(st->base_address); +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0); +	release_mem_region(res->start, resource_size(res)); + +	return 0; +} + +#ifdef CONFIG_PM +static int ad7606_par_suspend(struct device *dev) +{ +	struct iio_dev *indio_dev = dev_get_drvdata(dev); + +	ad7606_suspend(indio_dev); + +	return 0; +} + +static int ad7606_par_resume(struct device *dev) +{ +	struct iio_dev *indio_dev = dev_get_drvdata(dev); + +	ad7606_resume(indio_dev); + +	return 0; +} + +static const struct dev_pm_ops ad7606_pm_ops = { +	.suspend = ad7606_par_suspend, +	.resume  = ad7606_par_resume, +}; +#define AD7606_PAR_PM_OPS (&ad7606_pm_ops) + +#else +#define AD7606_PAR_PM_OPS NULL +#endif  /* CONFIG_PM */ + +static struct platform_device_id ad7606_driver_ids[] = { +	{ +		.name		= "ad7606-8", +		.driver_data	= ID_AD7606_8, +	}, { +		.name		= "ad7606-6", +		.driver_data	= ID_AD7606_6, +	}, { +		.name		= "ad7606-4", +		.driver_data	= ID_AD7606_4, +	}, +	{ } +}; + +MODULE_DEVICE_TABLE(platform, ad7606_driver_ids); + +static struct platform_driver ad7606_driver = { +	.probe = ad7606_par_probe, +	.remove	= ad7606_par_remove, +	.id_table = ad7606_driver_ids, +	.driver = { +		.name	 = "ad7606", +		.owner	= THIS_MODULE, +		.pm    = AD7606_PAR_PM_OPS, +	}, +}; + +module_platform_driver(ad7606_driver); + +MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>"); +MODULE_DESCRIPTION("Analog Devices AD7606 ADC"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/iio/adc/ad7606_ring.c b/drivers/staging/iio/adc/ad7606_ring.c new file mode 100644 index 00000000000..3bf174cb19b --- /dev/null +++ b/drivers/staging/iio/adc/ad7606_ring.c @@ -0,0 +1,101 @@ +/* + * Copyright 2011-2012 Analog Devices Inc. + * + * Licensed under the GPL-2. + * + */ + +#include <linux/interrupt.h> +#include <linux/gpio.h> +#include <linux/device.h> +#include <linux/kernel.h> +#include <linux/slab.h> + +#include <linux/iio/iio.h> +#include <linux/iio/buffer.h> +#include <linux/iio/trigger_consumer.h> +#include <linux/iio/triggered_buffer.h> + +#include "ad7606.h" + +/** + * ad7606_trigger_handler_th() th/bh of trigger launched polling to ring buffer + * + **/ +static irqreturn_t ad7606_trigger_handler_th_bh(int irq, void *p) +{ +	struct iio_poll_func *pf = p; +	struct ad7606_state *st = iio_priv(pf->indio_dev); + +	gpio_set_value(st->pdata->gpio_convst, 1); + +	return IRQ_HANDLED; +} + +/** + * ad7606_poll_bh_to_ring() bh of trigger launched polling to ring buffer + * @work_s:	the work struct through which this was scheduled + * + * Currently there is no option in this driver to disable the saving of + * timestamps within the ring. + * I think the one copy of this at a time was to avoid problems if the + * trigger was set far too high and the reads then locked up the computer. + **/ +static void ad7606_poll_bh_to_ring(struct work_struct *work_s) +{ +	struct ad7606_state *st = container_of(work_s, struct ad7606_state, +						poll_work); +	struct iio_dev *indio_dev = iio_priv_to_dev(st); +	__u8 *buf; +	int ret; + +	buf = kzalloc(indio_dev->scan_bytes, GFP_KERNEL); +	if (buf == NULL) +		return; + +	if (gpio_is_valid(st->pdata->gpio_frstdata)) { +		ret = st->bops->read_block(st->dev, 1, buf); +		if (ret) +			goto done; +		if (!gpio_get_value(st->pdata->gpio_frstdata)) { +			/* This should never happen. However +			 * some signal glitch caused by bad PCB desgin or +			 * electrostatic discharge, could cause an extra read +			 * or clock. This allows recovery. +			 */ +			ad7606_reset(st); +			goto done; +		} +		ret = st->bops->read_block(st->dev, +			st->chip_info->num_channels - 1, buf + 2); +		if (ret) +			goto done; +	} else { +		ret = st->bops->read_block(st->dev, +			st->chip_info->num_channels, buf); +		if (ret) +			goto done; +	} + +	iio_push_to_buffers_with_timestamp(indio_dev, buf, iio_get_time_ns()); +done: +	gpio_set_value(st->pdata->gpio_convst, 0); +	iio_trigger_notify_done(indio_dev->trig); +	kfree(buf); +} + +int ad7606_register_ring_funcs_and_init(struct iio_dev *indio_dev) +{ +	struct ad7606_state *st = iio_priv(indio_dev); + +	INIT_WORK(&st->poll_work, &ad7606_poll_bh_to_ring); + +	return iio_triggered_buffer_setup(indio_dev, +		&ad7606_trigger_handler_th_bh, &ad7606_trigger_handler_th_bh, +		NULL); +} + +void ad7606_ring_cleanup(struct iio_dev *indio_dev) +{ +	iio_triggered_buffer_cleanup(indio_dev); +} diff --git a/drivers/staging/iio/adc/ad7606_spi.c b/drivers/staging/iio/adc/ad7606_spi.c new file mode 100644 index 00000000000..6a8ecd73a1a --- /dev/null +++ b/drivers/staging/iio/adc/ad7606_spi.c @@ -0,0 +1,116 @@ +/* + * AD7606 SPI ADC driver + * + * Copyright 2011 Analog Devices Inc. + * + * Licensed under the GPL-2. + */ + +#include <linux/module.h> +#include <linux/spi/spi.h> +#include <linux/types.h> +#include <linux/err.h> + +#include <linux/iio/iio.h> +#include "ad7606.h" + +#define MAX_SPI_FREQ_HZ		23500000	/* VDRIVE above 4.75 V */ + +static int ad7606_spi_read_block(struct device *dev, +				 int count, void *buf) +{ +	struct spi_device *spi = to_spi_device(dev); +	int i, ret; +	unsigned short *data = buf; + +	ret = spi_read(spi, (u8 *)buf, count * 2); +	if (ret < 0) { +		dev_err(&spi->dev, "SPI read error\n"); +		return ret; +	} + +	for (i = 0; i < count; i++) +		data[i] = be16_to_cpu(data[i]); + +	return 0; +} + +static const struct ad7606_bus_ops ad7606_spi_bops = { +	.read_block	= ad7606_spi_read_block, +}; + +static int ad7606_spi_probe(struct spi_device *spi) +{ +	struct iio_dev *indio_dev; + +	indio_dev = ad7606_probe(&spi->dev, spi->irq, NULL, +			   spi_get_device_id(spi)->driver_data, +			   &ad7606_spi_bops); + +	if (IS_ERR(indio_dev)) +		return PTR_ERR(indio_dev); + +	spi_set_drvdata(spi, indio_dev); + +	return 0; +} + +static int ad7606_spi_remove(struct spi_device *spi) +{ +	struct iio_dev *indio_dev = dev_get_drvdata(&spi->dev); + +	return ad7606_remove(indio_dev, spi->irq); +} + +#ifdef CONFIG_PM +static int ad7606_spi_suspend(struct device *dev) +{ +	struct iio_dev *indio_dev = dev_get_drvdata(dev); + +	ad7606_suspend(indio_dev); + +	return 0; +} + +static int ad7606_spi_resume(struct device *dev) +{ +	struct iio_dev *indio_dev = dev_get_drvdata(dev); + +	ad7606_resume(indio_dev); + +	return 0; +} + +static const struct dev_pm_ops ad7606_pm_ops = { +	.suspend = ad7606_spi_suspend, +	.resume  = ad7606_spi_resume, +}; +#define AD7606_SPI_PM_OPS (&ad7606_pm_ops) + +#else +#define AD7606_SPI_PM_OPS NULL +#endif + +static const struct spi_device_id ad7606_id[] = { +	{"ad7606-8", ID_AD7606_8}, +	{"ad7606-6", ID_AD7606_6}, +	{"ad7606-4", ID_AD7606_4}, +	{} +}; +MODULE_DEVICE_TABLE(spi, ad7606_id); + +static struct spi_driver ad7606_driver = { +	.driver = { +		.name = "ad7606", +		.owner = THIS_MODULE, +		.pm    = AD7606_SPI_PM_OPS, +	}, +	.probe = ad7606_spi_probe, +	.remove = ad7606_spi_remove, +	.id_table = ad7606_id, +}; +module_spi_driver(ad7606_driver); + +MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>"); +MODULE_DESCRIPTION("Analog Devices AD7606 ADC"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/iio/adc/ad7780.c b/drivers/staging/iio/adc/ad7780.c new file mode 100644 index 00000000000..273add3ed63 --- /dev/null +++ b/drivers/staging/iio/adc/ad7780.c @@ -0,0 +1,276 @@ +/* + * AD7170/AD7171 and AD7780/AD7781 SPI ADC driver + * + * Copyright 2011 Analog Devices Inc. + * + * Licensed under the GPL-2. + */ + +#include <linux/interrupt.h> +#include <linux/device.h> +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/sysfs.h> +#include <linux/spi/spi.h> +#include <linux/regulator/consumer.h> +#include <linux/err.h> +#include <linux/sched.h> +#include <linux/gpio.h> +#include <linux/module.h> + +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> +#include <linux/iio/adc/ad_sigma_delta.h> + +#include "ad7780.h" + +#define AD7780_RDY	(1 << 7) +#define AD7780_FILTER	(1 << 6) +#define AD7780_ERR	(1 << 5) +#define AD7780_ID1	(1 << 4) +#define AD7780_ID0	(1 << 3) +#define AD7780_GAIN	(1 << 2) +#define AD7780_PAT1	(1 << 1) +#define AD7780_PAT0	(1 << 0) + +struct ad7780_chip_info { +	struct iio_chan_spec	channel; +	unsigned int		pattern_mask; +	unsigned int		pattern; +}; + +struct ad7780_state { +	const struct ad7780_chip_info	*chip_info; +	struct regulator		*reg; +	int				powerdown_gpio; +	unsigned int	gain; +	u16				int_vref_mv; + +	struct ad_sigma_delta sd; +}; + +enum ad7780_supported_device_ids { +	ID_AD7170, +	ID_AD7171, +	ID_AD7780, +	ID_AD7781, +}; + +static struct ad7780_state *ad_sigma_delta_to_ad7780(struct ad_sigma_delta *sd) +{ +	return container_of(sd, struct ad7780_state, sd); +} + +static int ad7780_set_mode(struct ad_sigma_delta *sigma_delta, +	enum ad_sigma_delta_mode mode) +{ +	struct ad7780_state *st = ad_sigma_delta_to_ad7780(sigma_delta); +	unsigned val; + +	switch (mode) { +	case AD_SD_MODE_SINGLE: +	case AD_SD_MODE_CONTINUOUS: +		val = 1; +		break; +	default: +		val = 0; +		break; +	} + +	if (gpio_is_valid(st->powerdown_gpio)) +		gpio_set_value(st->powerdown_gpio, val); + +	return 0; +} + +static int ad7780_read_raw(struct iio_dev *indio_dev, +			   struct iio_chan_spec const *chan, +			   int *val, +			   int *val2, +			   long m) +{ +	struct ad7780_state *st = iio_priv(indio_dev); + +	switch (m) { +	case IIO_CHAN_INFO_RAW: +		return ad_sigma_delta_single_conversion(indio_dev, chan, val); +	case IIO_CHAN_INFO_SCALE: +		*val = st->int_vref_mv * st->gain; +		*val2 = chan->scan_type.realbits - 1; +		return IIO_VAL_FRACTIONAL_LOG2; +	case IIO_CHAN_INFO_OFFSET: +		*val -= (1 << (chan->scan_type.realbits - 1)); +		return IIO_VAL_INT; +	} + +	return -EINVAL; +} + +static int ad7780_postprocess_sample(struct ad_sigma_delta *sigma_delta, +	unsigned int raw_sample) +{ +	struct ad7780_state *st = ad_sigma_delta_to_ad7780(sigma_delta); +	const struct ad7780_chip_info *chip_info = st->chip_info; + +	if ((raw_sample & AD7780_ERR) || +		((raw_sample & chip_info->pattern_mask) != chip_info->pattern)) +		return -EIO; + +	if (raw_sample & AD7780_GAIN) +		st->gain = 1; +	else +		st->gain = 128; + +	return 0; +} + +static const struct ad_sigma_delta_info ad7780_sigma_delta_info = { +	.set_mode = ad7780_set_mode, +	.postprocess_sample = ad7780_postprocess_sample, +	.has_registers = false, +}; + +#define AD7780_CHANNEL(bits, wordsize) \ +	AD_SD_CHANNEL(1, 0, 0, bits, 32, wordsize - bits) + +static const struct ad7780_chip_info ad7780_chip_info_tbl[] = { +	[ID_AD7170] = { +		.channel = AD7780_CHANNEL(12, 24), +		.pattern = 0x5, +		.pattern_mask = 0x7, +	}, +	[ID_AD7171] = { +		.channel = AD7780_CHANNEL(16, 24), +		.pattern = 0x5, +		.pattern_mask = 0x7, +	}, +	[ID_AD7780] = { +		.channel = AD7780_CHANNEL(24, 32), +		.pattern = 0x1, +		.pattern_mask = 0x3, +	}, +	[ID_AD7781] = { +		.channel = AD7780_CHANNEL(20, 32), +		.pattern = 0x1, +		.pattern_mask = 0x3, +	}, +}; + +static const struct iio_info ad7780_info = { +	.read_raw = &ad7780_read_raw, +	.driver_module = THIS_MODULE, +}; + +static int ad7780_probe(struct spi_device *spi) +{ +	struct ad7780_platform_data *pdata = spi->dev.platform_data; +	struct ad7780_state *st; +	struct iio_dev *indio_dev; +	int ret, voltage_uv = 0; + +	indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); +	if (indio_dev == NULL) +		return -ENOMEM; + +	st = iio_priv(indio_dev); +	st->gain = 1; + +	ad_sd_init(&st->sd, indio_dev, spi, &ad7780_sigma_delta_info); + +	st->reg = devm_regulator_get(&spi->dev, "vcc"); +	if (!IS_ERR(st->reg)) { +		ret = regulator_enable(st->reg); +		if (ret) +			return ret; + +		voltage_uv = regulator_get_voltage(st->reg); +	} + +	st->chip_info = +		&ad7780_chip_info_tbl[spi_get_device_id(spi)->driver_data]; + +	if (pdata && pdata->vref_mv) +		st->int_vref_mv = pdata->vref_mv; +	else if (voltage_uv) +		st->int_vref_mv = voltage_uv / 1000; +	else +		dev_warn(&spi->dev, "reference voltage unspecified\n"); + +	spi_set_drvdata(spi, indio_dev); + +	indio_dev->dev.parent = &spi->dev; +	indio_dev->name = spi_get_device_id(spi)->name; +	indio_dev->modes = INDIO_DIRECT_MODE; +	indio_dev->channels = &st->chip_info->channel; +	indio_dev->num_channels = 1; +	indio_dev->info = &ad7780_info; + +	if (pdata && gpio_is_valid(pdata->gpio_pdrst)) { + +		ret = devm_gpio_request_one(&spi->dev, pdata->gpio_pdrst, +					GPIOF_OUT_INIT_LOW, "AD7780 /PDRST"); +		if (ret) { +			dev_err(&spi->dev, "failed to request GPIO PDRST\n"); +			goto error_disable_reg; +		} +		st->powerdown_gpio = pdata->gpio_pdrst; +	} else { +		st->powerdown_gpio = -1; +	} + +	ret = ad_sd_setup_buffer_and_trigger(indio_dev); +	if (ret) +		goto error_disable_reg; + +	ret = iio_device_register(indio_dev); +	if (ret) +		goto error_cleanup_buffer_and_trigger; + +	return 0; + +error_cleanup_buffer_and_trigger: +	ad_sd_cleanup_buffer_and_trigger(indio_dev); +error_disable_reg: +	if (!IS_ERR(st->reg)) +		regulator_disable(st->reg); + +	return ret; +} + +static int ad7780_remove(struct spi_device *spi) +{ +	struct iio_dev *indio_dev = spi_get_drvdata(spi); +	struct ad7780_state *st = iio_priv(indio_dev); + +	iio_device_unregister(indio_dev); +	ad_sd_cleanup_buffer_and_trigger(indio_dev); + +	if (!IS_ERR(st->reg)) +		regulator_disable(st->reg); + +	return 0; +} + +static const struct spi_device_id ad7780_id[] = { +	{"ad7170", ID_AD7170}, +	{"ad7171", ID_AD7171}, +	{"ad7780", ID_AD7780}, +	{"ad7781", ID_AD7781}, +	{} +}; +MODULE_DEVICE_TABLE(spi, ad7780_id); + +static struct spi_driver ad7780_driver = { +	.driver = { +		.name	= "ad7780", +		.owner	= THIS_MODULE, +	}, +	.probe		= ad7780_probe, +	.remove		= ad7780_remove, +	.id_table	= ad7780_id, +}; +module_spi_driver(ad7780_driver); + +MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>"); +MODULE_DESCRIPTION("Analog Devices AD7780 and similar ADCs"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/iio/adc/ad7780.h b/drivers/staging/iio/adc/ad7780.h new file mode 100644 index 00000000000..67e511c3d6f --- /dev/null +++ b/drivers/staging/iio/adc/ad7780.h @@ -0,0 +1,30 @@ +/* + * AD7780/AD7781 SPI ADC driver + * + * Copyright 2011 Analog Devices Inc. + * + * Licensed under the GPL-2. + */ +#ifndef IIO_ADC_AD7780_H_ +#define IIO_ADC_AD7780_H_ + +/* + * TODO: struct ad7780_platform_data needs to go into include/linux/iio + */ + +/* NOTE: + * The AD7780 doesn't feature a dedicated SPI chip select, in addition it + * features a dual use data out ready DOUT/RDY output. + * In order to avoid contentions on the SPI bus, it's therefore necessary + * to use spi bus locking combined with a dedicated GPIO to control the + * power down reset signal of the AD7780. + * + * The DOUT/RDY output must also be wired to an interrupt capable GPIO. + */ + +struct ad7780_platform_data { +	u16				vref_mv; +	int				gpio_pdrst; +}; + +#endif /* IIO_ADC_AD7780_H_ */ diff --git a/drivers/staging/iio/adc/ad7816.c b/drivers/staging/iio/adc/ad7816.c new file mode 100644 index 00000000000..158d770f961 --- /dev/null +++ b/drivers/staging/iio/adc/ad7816.c @@ -0,0 +1,447 @@ +/* + * AD7816 digital temperature sensor driver supporting AD7816/7/8 + * + * Copyright 2010 Analog Devices Inc. + * + * Licensed under the GPL-2 or later. + */ + +#include <linux/interrupt.h> +#include <linux/gpio.h> +#include <linux/device.h> +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/sysfs.h> +#include <linux/list.h> +#include <linux/spi/spi.h> +#include <linux/module.h> + +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> +#include <linux/iio/events.h> + +/* + * AD7816 config masks + */ +#define AD7816_FULL			0x1 +#define AD7816_PD			0x2 +#define AD7816_CS_MASK			0x7 +#define AD7816_CS_MAX			0x4 + +/* + * AD7816 temperature masks + */ +#define AD7816_VALUE_OFFSET		6 +#define AD7816_BOUND_VALUE_BASE		0x8 +#define AD7816_BOUND_VALUE_MIN		-95 +#define AD7816_BOUND_VALUE_MAX		152 +#define AD7816_TEMP_FLOAT_OFFSET	2 +#define AD7816_TEMP_FLOAT_MASK		0x3 + + +/* + * struct ad7816_chip_info - chip specific information + */ + +struct ad7816_chip_info { +	struct spi_device *spi_dev; +	u16 rdwr_pin; +	u16 convert_pin; +	u16 busy_pin; +	u8  oti_data[AD7816_CS_MAX+1]; +	u8  channel_id;	/* 0 always be temperature */ +	u8  mode; +}; + +/* + * ad7816 data access by SPI + */ +static int ad7816_spi_read(struct ad7816_chip_info *chip, u16 *data) +{ +	struct spi_device *spi_dev = chip->spi_dev; +	int ret = 0; + +	gpio_set_value(chip->rdwr_pin, 1); +	gpio_set_value(chip->rdwr_pin, 0); +	ret = spi_write(spi_dev, &chip->channel_id, sizeof(chip->channel_id)); +	if (ret < 0) { +		dev_err(&spi_dev->dev, "SPI channel setting error\n"); +		return ret; +	} +	gpio_set_value(chip->rdwr_pin, 1); + + +	if (chip->mode == AD7816_PD) { /* operating mode 2 */ +		gpio_set_value(chip->convert_pin, 1); +		gpio_set_value(chip->convert_pin, 0); +	} else { /* operating mode 1 */ +		gpio_set_value(chip->convert_pin, 0); +		gpio_set_value(chip->convert_pin, 1); +	} + +	while (gpio_get_value(chip->busy_pin)) +		cpu_relax(); + +	gpio_set_value(chip->rdwr_pin, 0); +	gpio_set_value(chip->rdwr_pin, 1); +	ret = spi_read(spi_dev, (u8 *)data, sizeof(*data)); +	if (ret < 0) { +		dev_err(&spi_dev->dev, "SPI data read error\n"); +		return ret; +	} + +	*data = be16_to_cpu(*data); + +	return ret; +} + +static int ad7816_spi_write(struct ad7816_chip_info *chip, u8 data) +{ +	struct spi_device *spi_dev = chip->spi_dev; +	int ret = 0; + +	gpio_set_value(chip->rdwr_pin, 1); +	gpio_set_value(chip->rdwr_pin, 0); +	ret = spi_write(spi_dev, &data, sizeof(data)); +	if (ret < 0) +		dev_err(&spi_dev->dev, "SPI oti data write error\n"); + +	return ret; +} + +static ssize_t ad7816_show_mode(struct device *dev, +		struct device_attribute *attr, +		char *buf) +{ +	struct iio_dev *indio_dev = dev_to_iio_dev(dev); +	struct ad7816_chip_info *chip = iio_priv(indio_dev); + +	if (chip->mode) +		return sprintf(buf, "power-save\n"); +	else +		return sprintf(buf, "full\n"); +} + +static ssize_t ad7816_store_mode(struct device *dev, +		struct device_attribute *attr, +		const char *buf, +		size_t len) +{ +	struct iio_dev *indio_dev = dev_to_iio_dev(dev); +	struct ad7816_chip_info *chip = iio_priv(indio_dev); + +	if (strcmp(buf, "full")) { +		gpio_set_value(chip->rdwr_pin, 1); +		chip->mode = AD7816_FULL; +	} else { +		gpio_set_value(chip->rdwr_pin, 0); +		chip->mode = AD7816_PD; +	} + +	return len; +} + +static IIO_DEVICE_ATTR(mode, S_IRUGO | S_IWUSR, +		ad7816_show_mode, +		ad7816_store_mode, +		0); + +static ssize_t ad7816_show_available_modes(struct device *dev, +		struct device_attribute *attr, +		char *buf) +{ +	return sprintf(buf, "full\npower-save\n"); +} + +static IIO_DEVICE_ATTR(available_modes, S_IRUGO, ad7816_show_available_modes, NULL, 0); + +static ssize_t ad7816_show_channel(struct device *dev, +		struct device_attribute *attr, +		char *buf) +{ +	struct iio_dev *indio_dev = dev_to_iio_dev(dev); +	struct ad7816_chip_info *chip = iio_priv(indio_dev); + +	return sprintf(buf, "%d\n", chip->channel_id); +} + +static ssize_t ad7816_store_channel(struct device *dev, +		struct device_attribute *attr, +		const char *buf, +		size_t len) +{ +	struct iio_dev *indio_dev = dev_to_iio_dev(dev); +	struct ad7816_chip_info *chip = iio_priv(indio_dev); +	unsigned long data; +	int ret; + +	ret = kstrtoul(buf, 10, &data); +	if (ret) +		return ret; + +	if (data > AD7816_CS_MAX && data != AD7816_CS_MASK) { +		dev_err(&chip->spi_dev->dev, "Invalid channel id %lu for %s.\n", +			data, indio_dev->name); +		return -EINVAL; +	} else if (strcmp(indio_dev->name, "ad7818") == 0 && data > 1) { +		dev_err(&chip->spi_dev->dev, +			"Invalid channel id %lu for ad7818.\n", data); +		return -EINVAL; +	} else if (strcmp(indio_dev->name, "ad7816") == 0 && data > 0) { +		dev_err(&chip->spi_dev->dev, +			"Invalid channel id %lu for ad7816.\n", data); +		return -EINVAL; +	} + +	chip->channel_id = data; + +	return len; +} + +static IIO_DEVICE_ATTR(channel, S_IRUGO | S_IWUSR, +		ad7816_show_channel, +		ad7816_store_channel, +		0); + + +static ssize_t ad7816_show_value(struct device *dev, +		struct device_attribute *attr, +		char *buf) +{ +	struct iio_dev *indio_dev = dev_to_iio_dev(dev); +	struct ad7816_chip_info *chip = iio_priv(indio_dev); +	u16 data; +	s8 value; +	int ret; + +	ret = ad7816_spi_read(chip, &data); +	if (ret) +		return -EIO; + +	data >>= AD7816_VALUE_OFFSET; + +	if (chip->channel_id == 0) { +		value = (s8)((data >> AD7816_TEMP_FLOAT_OFFSET) - 103); +		data &= AD7816_TEMP_FLOAT_MASK; +		if (value < 0) +			data = (1 << AD7816_TEMP_FLOAT_OFFSET) - data; +		return sprintf(buf, "%d.%.2d\n", value, data * 25); +	} else +		return sprintf(buf, "%u\n", data); +} + +static IIO_DEVICE_ATTR(value, S_IRUGO, ad7816_show_value, NULL, 0); + +static struct attribute *ad7816_attributes[] = { +	&iio_dev_attr_available_modes.dev_attr.attr, +	&iio_dev_attr_mode.dev_attr.attr, +	&iio_dev_attr_channel.dev_attr.attr, +	&iio_dev_attr_value.dev_attr.attr, +	NULL, +}; + +static const struct attribute_group ad7816_attribute_group = { +	.attrs = ad7816_attributes, +}; + +/* + * temperature bound events + */ + +#define IIO_EVENT_CODE_AD7816_OTI IIO_UNMOD_EVENT_CODE(IIO_TEMP,	\ +						       0,		\ +						       IIO_EV_TYPE_THRESH, \ +						       IIO_EV_DIR_FALLING) + +static irqreturn_t ad7816_event_handler(int irq, void *private) +{ +	iio_push_event(private, IIO_EVENT_CODE_AD7816_OTI, iio_get_time_ns()); +	return IRQ_HANDLED; +} + +static ssize_t ad7816_show_oti(struct device *dev, +		struct device_attribute *attr, +		char *buf) +{ +	struct iio_dev *indio_dev = dev_to_iio_dev(dev); +	struct ad7816_chip_info *chip = iio_priv(indio_dev); +	int value; + +	if (chip->channel_id > AD7816_CS_MAX) { +		dev_err(dev, "Invalid oti channel id %d.\n", chip->channel_id); +		return -EINVAL; +	} else if (chip->channel_id == 0) { +		value = AD7816_BOUND_VALUE_MIN + +			(chip->oti_data[chip->channel_id] - +			AD7816_BOUND_VALUE_BASE); +		return sprintf(buf, "%d\n", value); +	} else +		return sprintf(buf, "%u\n", chip->oti_data[chip->channel_id]); +} + +static inline ssize_t ad7816_set_oti(struct device *dev, +		struct device_attribute *attr, +		const char *buf, +		size_t len) +{ +	struct iio_dev *indio_dev = dev_to_iio_dev(dev); +	struct ad7816_chip_info *chip = iio_priv(indio_dev); +	long value; +	u8 data; +	int ret; + +	ret = kstrtol(buf, 10, &value); +	if (ret) +		return ret; + +	if (chip->channel_id > AD7816_CS_MAX) { +		dev_err(dev, "Invalid oti channel id %d.\n", chip->channel_id); +		return -EINVAL; +	} else if (chip->channel_id == 0) { +		if (ret || value < AD7816_BOUND_VALUE_MIN || +			value > AD7816_BOUND_VALUE_MAX) +			return -EINVAL; + +		data = (u8)(value - AD7816_BOUND_VALUE_MIN + +			AD7816_BOUND_VALUE_BASE); +	} else { +		if (ret || value < AD7816_BOUND_VALUE_BASE || value > 255) +			return -EINVAL; + +		data = (u8)value; +	} + +	ret = ad7816_spi_write(chip, data); +	if (ret) +		return -EIO; + +	chip->oti_data[chip->channel_id] = data; + +	return len; +} + +static IIO_DEVICE_ATTR(oti, S_IRUGO | S_IWUSR, +		       ad7816_show_oti, ad7816_set_oti, 0); + +static struct attribute *ad7816_event_attributes[] = { +	&iio_dev_attr_oti.dev_attr.attr, +	NULL, +}; + +static struct attribute_group ad7816_event_attribute_group = { +	.attrs = ad7816_event_attributes, +	.name = "events", +}; + +static const struct iio_info ad7816_info = { +	.attrs = &ad7816_attribute_group, +	.event_attrs = &ad7816_event_attribute_group, +	.driver_module = THIS_MODULE, +}; + +/* + * device probe and remove + */ + +static int ad7816_probe(struct spi_device *spi_dev) +{ +	struct ad7816_chip_info *chip; +	struct iio_dev *indio_dev; +	unsigned short *pins = spi_dev->dev.platform_data; +	int ret = 0; +	int i; + +	if (!pins) { +		dev_err(&spi_dev->dev, "No necessary GPIO platform data.\n"); +		return -EINVAL; +	} + +	indio_dev = devm_iio_device_alloc(&spi_dev->dev, sizeof(*chip)); +	if (!indio_dev) +		return -ENOMEM; +	chip = iio_priv(indio_dev); +	/* this is only used for device removal purposes */ +	dev_set_drvdata(&spi_dev->dev, indio_dev); + +	chip->spi_dev = spi_dev; +	for (i = 0; i <= AD7816_CS_MAX; i++) +		chip->oti_data[i] = 203; +	chip->rdwr_pin = pins[0]; +	chip->convert_pin = pins[1]; +	chip->busy_pin = pins[2]; + +	ret = devm_gpio_request(&spi_dev->dev, chip->rdwr_pin, +					spi_get_device_id(spi_dev)->name); +	if (ret) { +		dev_err(&spi_dev->dev, "Fail to request rdwr gpio PIN %d.\n", +			chip->rdwr_pin); +		return ret; +	} +	gpio_direction_input(chip->rdwr_pin); +	ret = devm_gpio_request(&spi_dev->dev, chip->convert_pin, +					spi_get_device_id(spi_dev)->name); +	if (ret) { +		dev_err(&spi_dev->dev, "Fail to request convert gpio PIN %d.\n", +			chip->convert_pin); +		return ret; +	} +	gpio_direction_input(chip->convert_pin); +	ret = devm_gpio_request(&spi_dev->dev, chip->busy_pin, +					spi_get_device_id(spi_dev)->name); +	if (ret) { +		dev_err(&spi_dev->dev, "Fail to request busy gpio PIN %d.\n", +			chip->busy_pin); +		return ret; +	} +	gpio_direction_input(chip->busy_pin); + +	indio_dev->name = spi_get_device_id(spi_dev)->name; +	indio_dev->dev.parent = &spi_dev->dev; +	indio_dev->info = &ad7816_info; +	indio_dev->modes = INDIO_DIRECT_MODE; + +	if (spi_dev->irq) { +		/* Only low trigger is supported in ad7816/7/8 */ +		ret = devm_request_threaded_irq(&spi_dev->dev, spi_dev->irq, +						NULL, +						&ad7816_event_handler, +						IRQF_TRIGGER_LOW | IRQF_ONESHOT, +						indio_dev->name, +						indio_dev); +		if (ret) +			return ret; +	} + +	ret = devm_iio_device_register(&spi_dev->dev, indio_dev); +	if (ret) +		return ret; + +	dev_info(&spi_dev->dev, "%s temperature sensor and ADC registered.\n", +			 indio_dev->name); + +	return 0; +} + +static const struct spi_device_id ad7816_id[] = { +	{ "ad7816", 0 }, +	{ "ad7817", 0 }, +	{ "ad7818", 0 }, +	{} +}; + +MODULE_DEVICE_TABLE(spi, ad7816_id); + +static struct spi_driver ad7816_driver = { +	.driver = { +		.name = "ad7816", +		.owner = THIS_MODULE, +	}, +	.probe = ad7816_probe, +	.id_table = ad7816_id, +}; +module_spi_driver(ad7816_driver); + +MODULE_AUTHOR("Sonic Zhang <sonic.zhang@analog.com>"); +MODULE_DESCRIPTION("Analog Devices AD7816/7/8 digital" +			" temperature sensor driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/iio/adc/ad799x.h b/drivers/staging/iio/adc/ad799x.h deleted file mode 100644 index 81a20d524b7..00000000000 --- a/drivers/staging/iio/adc/ad799x.h +++ /dev/null @@ -1,159 +0,0 @@ -/* - * Copyright (C) 2010 Michael Hennerich, Analog Devices Inc. - * Copyright (C) 2008-2010 Jonathan Cameron - * - * 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. - * - * ad799x.h - */ - -#ifndef _AD799X_H_ -#define  _AD799X_H_ - -#define AD799X_CHANNEL_SHIFT			4 -#define AD799X_STORAGEBITS			16 -/* - * AD7991, AD7995 and AD7999 defines - */ - -#define AD7991_REF_SEL				0x08 -#define AD7991_FLTR				0x04 -#define AD7991_BIT_TRIAL_DELAY			0x02 -#define AD7991_SAMPLE_DELAY			0x01 - -/* - * AD7992, AD7993, AD7994, AD7997 and AD7998 defines - */ - -#define AD7998_FLTR				0x08 -#define AD7998_ALERT_EN				0x04 -#define AD7998_BUSY_ALERT			0x02 -#define AD7998_BUSY_ALERT_POL			0x01 - -#define AD7998_CONV_RES_REG			0x0 -#define AD7998_ALERT_STAT_REG			0x1 -#define AD7998_CONF_REG				0x2 -#define AD7998_CYCLE_TMR_REG			0x3 -#define AD7998_DATALOW_CH1_REG			0x4 -#define AD7998_DATAHIGH_CH1_REG			0x5 -#define AD7998_HYST_CH1_REG			0x6 -#define AD7998_DATALOW_CH2_REG			0x7 -#define AD7998_DATAHIGH_CH2_REG			0x8 -#define AD7998_HYST_CH2_REG			0x9 -#define AD7998_DATALOW_CH3_REG			0xA -#define AD7998_DATAHIGH_CH3_REG			0xB -#define AD7998_HYST_CH3_REG			0xC -#define AD7998_DATALOW_CH4_REG			0xD -#define AD7998_DATAHIGH_CH4_REG			0xE -#define AD7998_HYST_CH4_REG			0xF - -#define AD7998_CYC_MASK				0x7 -#define AD7998_CYC_DIS				0x0 -#define AD7998_CYC_TCONF_32			0x1 -#define AD7998_CYC_TCONF_64			0x2 -#define AD7998_CYC_TCONF_128			0x3 -#define AD7998_CYC_TCONF_256			0x4 -#define AD7998_CYC_TCONF_512			0x5 -#define AD7998_CYC_TCONF_1024			0x6 -#define AD7998_CYC_TCONF_2048			0x7 - -#define AD7998_ALERT_STAT_CLEAR			0xFF - -/* - * AD7997 and AD7997 defines - */ - -#define AD7997_8_READ_SINGLE			0x80 -#define AD7997_8_READ_SEQUENCE			0x70 - -enum { -	ad7991, -	ad7995, -	ad7999, -	ad7992, -	ad7993, -	ad7994, -	ad7997, -	ad7998 -}; - -struct ad799x_state; - -/** - * struct ad799x_chip_info - chip specifc information - * @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 - * @default_config:	device default configuration - * @dev_attrs:		pointer to the device attribute group - * @scan_attrs:		pointer to the scan element attribute group - * @event_attrs:	pointer to the monitor event attribute group - * @ad799x_set_scan_mode: function pointer to the device specific mode function - - */ -struct ad799x_chip_info { -	u8				num_inputs; -	u8				bits; -	u8				storagebits; -	char				sign; -	u16				int_vref_mv; -	bool				monitor_mode; -	u16				default_config; -	struct attribute_group		*dev_attrs; -	struct attribute_group		*scan_attrs; -	struct attribute_group		*event_attrs; -	int (*ad799x_set_scan_mode)	(struct ad799x_state *st, -					unsigned mask); -}; - -struct ad799x_state { -	struct iio_dev			*indio_dev; -	struct i2c_client		*client; -	const struct ad799x_chip_info	*chip_info; -	struct work_struct		poll_work; -	struct work_struct		work_thresh; -	atomic_t			protect_ring; -	struct iio_trigger		*trig; -	struct regulator		*reg; -	s64				last_timestamp; -	u16				int_vref_mv; -	unsigned			id; -	char				*name; -	u16				config; -}; - -/* - * TODO: struct ad799x_platform_data needs to go into include/linux/iio - */ - -struct ad799x_platform_data { -	u16				vref_mv; -}; - -int ad799x_set_scan_mode(struct ad799x_state *st, unsigned mask); - -#ifdef CONFIG_AD799X_RING_BUFFER -int ad799x_single_channel_from_ring(struct ad799x_state *st, long mask); -int ad799x_register_ring_funcs_and_init(struct iio_dev *indio_dev); -void ad799x_ring_cleanup(struct iio_dev *indio_dev); -#else /* CONFIG_AD799X_RING_BUFFER */ -int ad799x_single_channel_from_ring(struct ad799x_state *st, long mask) -{ -	return -EINVAL; -} - - -static inline int -ad799x_register_ring_funcs_and_init(struct iio_dev *indio_dev) -{ -	return 0; -} - -static inline void ad799x_ring_cleanup(struct iio_dev *indio_dev) -{ -} -#endif /* CONFIG_AD799X_RING_BUFFER */ -#endif /* _AD799X_H_ */ diff --git a/drivers/staging/iio/adc/ad799x_core.c b/drivers/staging/iio/adc/ad799x_core.c deleted file mode 100644 index 6309d521a86..00000000000 --- a/drivers/staging/iio/adc/ad799x_core.c +++ /dev/null @@ -1,923 +0,0 @@ -/* - * iio/adc/ad799x.c - * Copyright (C) 2010 Michael Hennerich, Analog Devices Inc. - * - * based on iio/adc/max1363 - * 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. - * - * ad799x.c - * - * Support for ad7991, ad7995, ad7999, ad7992, ad7993, ad7994, ad7997, - * ad7998 and similar chips. - * - */ - -#include <linux/interrupt.h> -#include <linux/workqueue.h> -#include <linux/device.h> -#include <linux/kernel.h> -#include <linux/sysfs.h> -#include <linux/list.h> -#include <linux/i2c.h> -#include <linux/regulator/consumer.h> -#include <linux/slab.h> -#include <linux/types.h> -#include <linux/err.h> - -#include "../iio.h" -#include "../sysfs.h" - -#include "../ring_generic.h" -#include "adc.h" -#include "ad799x.h" - -/* - * ad799x register access by I2C - */ -static int ad799x_i2c_read16(struct ad799x_state *st, u8 reg, u16 *data) -{ -	struct i2c_client *client = st->client; -	int ret = 0; - -	ret = i2c_smbus_read_word_data(client, reg); -	if (ret < 0) { -		dev_err(&client->dev, "I2C read error\n"); -		return ret; -	} - -	*data = swab16((u16)ret); - -	return 0; -} - -static int ad799x_i2c_read8(struct ad799x_state *st, u8 reg, u8 *data) -{ -	struct i2c_client *client = st->client; -	int ret = 0; - -	ret = i2c_smbus_read_byte_data(client, reg); -	if (ret < 0) { -		dev_err(&client->dev, "I2C read error\n"); -		return ret; -	} - -	*data = (u8)ret; - -	return 0; -} - -static int ad799x_i2c_write16(struct ad799x_state *st, u8 reg, u16 data) -{ -	struct i2c_client *client = st->client; -	int ret = 0; - -	ret = i2c_smbus_write_word_data(client, reg, swab16(data)); -	if (ret < 0) -		dev_err(&client->dev, "I2C write error\n"); - -	return ret; -} - -static int ad799x_i2c_write8(struct ad799x_state *st, u8 reg, u8 data) -{ -	struct i2c_client *client = st->client; -	int ret = 0; - -	ret = i2c_smbus_write_byte_data(client, reg, data); -	if (ret < 0) -		dev_err(&client->dev, "I2C write error\n"); - -	return ret; -} - -static int ad799x_scan_el_set_state(struct iio_scan_el *scan_el, -				       struct iio_dev *indio_dev, -				       bool state) -{ -	struct ad799x_state *st = indio_dev->dev_data; -	return ad799x_set_scan_mode(st, st->indio_dev->ring->scan_mask); -} - -/* Here we claim all are 16 bits. This currently does no harm and saves - * us a lot of scan element listings */ - -#define AD799X_SCAN_EL(number)						\ -	IIO_SCAN_EL_C(in##number, number, 0, ad799x_scan_el_set_state); - -static AD799X_SCAN_EL(0); -static AD799X_SCAN_EL(1); -static AD799X_SCAN_EL(2); -static AD799X_SCAN_EL(3); -static AD799X_SCAN_EL(4); -static AD799X_SCAN_EL(5); -static AD799X_SCAN_EL(6); -static AD799X_SCAN_EL(7); - -static ssize_t ad799x_show_type(struct device *dev, -				struct device_attribute *attr, -				char *buf) -{ -	struct iio_ring_buffer *ring = dev_get_drvdata(dev); -	struct iio_dev *indio_dev = ring->indio_dev; -	struct ad799x_state *st = indio_dev->dev_data; - -	return sprintf(buf, "%c%d/%d\n", st->chip_info->sign, -		       st->chip_info->bits, AD799X_STORAGEBITS); -} -static IIO_DEVICE_ATTR(in_type, S_IRUGO, ad799x_show_type, NULL, 0); - -static int ad7991_5_9_set_scan_mode(struct ad799x_state *st, unsigned mask) -{ -	return i2c_smbus_write_byte(st->client, -		st->config | (mask << AD799X_CHANNEL_SHIFT)); -} - -static int ad7992_3_4_set_scan_mode(struct ad799x_state *st, unsigned mask) -{ -	return ad799x_i2c_write8(st, AD7998_CONF_REG, -		st->config | (mask << AD799X_CHANNEL_SHIFT)); -} - -static int ad7997_8_set_scan_mode(struct ad799x_state *st, unsigned mask) -{ -	return ad799x_i2c_write16(st, AD7998_CONF_REG, -		st->config | (mask << AD799X_CHANNEL_SHIFT)); -} - -int ad799x_set_scan_mode(struct ad799x_state *st, unsigned mask) -{ -	int ret; - -	if (st->chip_info->ad799x_set_scan_mode != NULL) { -		ret = st->chip_info->ad799x_set_scan_mode(st, mask); -		return (ret > 0) ? 0 : ret; -	} - -	return 0; -} - -static ssize_t ad799x_read_single_channel(struct device *dev, -				   struct device_attribute *attr, -				   char *buf) -{ -	struct iio_dev *dev_info = dev_get_drvdata(dev); -	struct ad799x_state *st = iio_dev_get_devdata(dev_info); -	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); -	int ret = 0, len = 0; -	u32 data ; -	u16 rxbuf[1]; -	u8 cmd; -	long mask; - -	mutex_lock(&dev_info->mlock); -	mask = 1 << this_attr->address; -	/* If ring buffer capture is occuring, query the buffer */ -	if (iio_ring_enabled(dev_info)) { -		data = ret = ad799x_single_channel_from_ring(st, mask); -		if (ret < 0) -			goto error_ret; -		ret = 0; -	} else { -		switch (st->id) { -		case ad7991: -		case ad7995: -		case ad7999: -			cmd = st->config | (mask << AD799X_CHANNEL_SHIFT); -			break; -		case ad7992: -		case ad7993: -		case ad7994: -			cmd = mask << AD799X_CHANNEL_SHIFT; -			break; -		case ad7997: -		case ad7998: -			cmd = (this_attr->address << -				AD799X_CHANNEL_SHIFT) | AD7997_8_READ_SINGLE; -			break; -		default: -			cmd = 0; - -		} -		ret = ad799x_i2c_read16(st, cmd, rxbuf); -		if (ret < 0) -			goto error_ret; - -		data = rxbuf[0]; -	} - -	/* Pretty print the result */ -	len = sprintf(buf, "%u\n", data & ((1 << (st->chip_info->bits)) - 1)); - -error_ret: -	mutex_unlock(&dev_info->mlock); -	return ret ? ret : len; -} - -static ssize_t ad799x_read_frequency(struct device *dev, -					struct device_attribute *attr, -					char *buf) -{ -	struct iio_dev *dev_info = dev_get_drvdata(dev); -	struct ad799x_state *st = iio_dev_get_devdata(dev_info); - -	int ret, len = 0; -	u8 val; -	ret = ad799x_i2c_read8(st, AD7998_CYCLE_TMR_REG, &val); -	if (ret) -		return ret; - -	val &= AD7998_CYC_MASK; - -	switch (val) { -	case AD7998_CYC_DIS: -		len = sprintf(buf, "0\n"); -		break; -	case AD7998_CYC_TCONF_32: -		len = sprintf(buf, "15625\n"); -		break; -	case AD7998_CYC_TCONF_64: -		len = sprintf(buf, "7812\n"); -		break; -	case AD7998_CYC_TCONF_128: -		len = sprintf(buf, "3906\n"); -		break; -	case AD7998_CYC_TCONF_256: -		len = sprintf(buf, "1953\n"); -		break; -	case AD7998_CYC_TCONF_512: -		len = sprintf(buf, "976\n"); -		break; -	case AD7998_CYC_TCONF_1024: -		len = sprintf(buf, "488\n"); -		break; -	case AD7998_CYC_TCONF_2048: -		len = sprintf(buf, "244\n"); -		break; -	} -	return len; -} - -static ssize_t ad799x_write_frequency(struct device *dev, -					 struct device_attribute *attr, -					 const char *buf, -					 size_t len) -{ -	struct iio_dev *dev_info = dev_get_drvdata(dev); -	struct ad799x_state *st = iio_dev_get_devdata(dev_info); - -	long val; -	int ret; -	u8 t; - -	ret = strict_strtol(buf, 10, &val); -	if (ret) -		return ret; - -	mutex_lock(&dev_info->mlock); -	ret = ad799x_i2c_read8(st, AD7998_CYCLE_TMR_REG, &t); -	if (ret) -		goto error_ret_mutex; -	/* Wipe the bits clean */ -	t &= ~AD7998_CYC_MASK; - -	switch (val) { -	case 15625: -		t |= AD7998_CYC_TCONF_32; -		break; -	case 7812: -		t |= AD7998_CYC_TCONF_64; -		break; -	case 3906: -		t |= AD7998_CYC_TCONF_128; -		break; -	case 1953: -		t |= AD7998_CYC_TCONF_256; -		break; -	case 976: -		t |= AD7998_CYC_TCONF_512; -		break; -	case 488: -		t |= AD7998_CYC_TCONF_1024; -		break; -	case 244: -		t |= AD7998_CYC_TCONF_2048; -		break; -	case  0: -		t |= AD7998_CYC_DIS; -		break; -	default: -		ret = -EINVAL; -		goto error_ret_mutex; -	} - -	ret = ad799x_i2c_write8(st, AD7998_CYCLE_TMR_REG, t); - -error_ret_mutex: -	mutex_unlock(&dev_info->mlock); - -	return ret ? ret : len; -} - - -static ssize_t ad799x_read_channel_config(struct device *dev, -					struct device_attribute *attr, -					char *buf) -{ -	struct iio_dev *dev_info = dev_get_drvdata(dev); -	struct ad799x_state *st = iio_dev_get_devdata(dev_info); -	struct iio_event_attr *this_attr = to_iio_event_attr(attr); - -	int ret; -	u16 val; -	ret = ad799x_i2c_read16(st, this_attr->mask, &val); -	if (ret) -		return ret; - -	return sprintf(buf, "%d\n", val); -} - -static ssize_t ad799x_write_channel_config(struct device *dev, -					 struct device_attribute *attr, -					 const char *buf, -					 size_t len) -{ -	struct iio_dev *dev_info = dev_get_drvdata(dev); -	struct ad799x_state *st = iio_dev_get_devdata(dev_info); -	struct iio_event_attr *this_attr = to_iio_event_attr(attr); - -	long val; -	int ret; - -	ret = strict_strtol(buf, 10, &val); -	if (ret) -		return ret; - -	mutex_lock(&dev_info->mlock); -	ret = ad799x_i2c_write16(st, this_attr->mask, val); -	mutex_unlock(&dev_info->mlock); - -	return ret ? ret : len; -} - -static void ad799x_interrupt_bh(struct work_struct *work_s) -{ -	struct ad799x_state *st = container_of(work_s, -		struct ad799x_state, work_thresh); -	u8 status; -	int i; - -	if (ad799x_i2c_read8(st, AD7998_ALERT_STAT_REG, &status)) -		goto err_out; - -	if (!status) -		goto err_out; - -	ad799x_i2c_write8(st, AD7998_ALERT_STAT_REG, AD7998_ALERT_STAT_CLEAR); - -	for (i = 0; i < 8; i++) { -		if (status & (1 << i)) -			iio_push_event(st->indio_dev, 0, -				i & 0x1 ? -				IIO_EVENT_CODE_IN_HIGH_THRESH(i >> 1) : -				IIO_EVENT_CODE_IN_LOW_THRESH(i >> 1), -				st->last_timestamp); -	} - -err_out: -	enable_irq(st->client->irq); -} - -static int ad799x_interrupt(struct iio_dev *dev_info, -		int index, -		s64 timestamp, -		int no_test) -{ -	struct ad799x_state *st = dev_info->dev_data; - -	st->last_timestamp = timestamp; -	schedule_work(&st->work_thresh); -	return 0; -} - -IIO_EVENT_SH(ad799x, &ad799x_interrupt); - -/* Direct read attribtues */ -static IIO_DEV_ATTR_IN_RAW(0, ad799x_read_single_channel, 0); -static IIO_DEV_ATTR_IN_RAW(1, ad799x_read_single_channel, 1); -static IIO_DEV_ATTR_IN_RAW(2, ad799x_read_single_channel, 2); -static IIO_DEV_ATTR_IN_RAW(3, ad799x_read_single_channel, 3); -static IIO_DEV_ATTR_IN_RAW(4, ad799x_read_single_channel, 4); -static IIO_DEV_ATTR_IN_RAW(5, ad799x_read_single_channel, 5); -static IIO_DEV_ATTR_IN_RAW(6, ad799x_read_single_channel, 6); -static IIO_DEV_ATTR_IN_RAW(7, ad799x_read_single_channel, 7); - -static ssize_t ad799x_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 ad799x_state *st = iio_dev_get_devdata(dev_info); - -	/* Corresponds to Vref / 2^(bits) */ -	unsigned int scale_uv = (st->int_vref_mv * 1000) >> st->chip_info->bits; - -	return sprintf(buf, "%d.%d\n", scale_uv / 1000, scale_uv % 1000); -} - -static IIO_DEVICE_ATTR(in_scale, S_IRUGO, ad799x_show_scale, NULL, 0); - -static ssize_t ad799x_show_name(struct device *dev, -				 struct device_attribute *attr, -				 char *buf) -{ -	struct iio_dev *dev_info = dev_get_drvdata(dev); -	struct ad799x_state *st = iio_dev_get_devdata(dev_info); -	return sprintf(buf, "%s\n", st->client->name); -} - -static IIO_DEVICE_ATTR(name, S_IRUGO, ad799x_show_name, NULL, 0); - -static struct attribute *ad7991_5_9_3_4_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_name.dev_attr.attr, -	&iio_dev_attr_in_scale.dev_attr.attr, -	NULL -}; - -static struct attribute_group ad7991_5_9_3_4_dev_attr_group = { -	.attrs = ad7991_5_9_3_4_device_attrs, -}; - -static struct attribute *ad7991_5_9_3_4_scan_el_attrs[] = { -	&iio_scan_el_in0.dev_attr.attr, -	&iio_const_attr_in0_index.dev_attr.attr, -	&iio_scan_el_in1.dev_attr.attr, -	&iio_const_attr_in1_index.dev_attr.attr, -	&iio_scan_el_in2.dev_attr.attr, -	&iio_const_attr_in2_index.dev_attr.attr, -	&iio_scan_el_in3.dev_attr.attr, -	&iio_const_attr_in3_index.dev_attr.attr, -	&iio_dev_attr_in_type.dev_attr.attr, -	NULL, -}; - -static struct attribute_group ad7991_5_9_3_4_scan_el_group = { -	.name = "scan_elements", -	.attrs = ad7991_5_9_3_4_scan_el_attrs, -}; - -static struct attribute *ad7992_device_attrs[] = { -	&iio_dev_attr_in0_raw.dev_attr.attr, -	&iio_dev_attr_in1_raw.dev_attr.attr, -	&iio_dev_attr_name.dev_attr.attr, -	&iio_dev_attr_in_scale.dev_attr.attr, -	NULL -}; - -static struct attribute_group ad7992_dev_attr_group = { -	.attrs = ad7992_device_attrs, -}; - -static struct attribute *ad7992_scan_el_attrs[] = { -	&iio_scan_el_in0.dev_attr.attr, -	&iio_const_attr_in0_index.dev_attr.attr, -	&iio_scan_el_in1.dev_attr.attr, -	&iio_const_attr_in1_index.dev_attr.attr, -	&iio_dev_attr_in_type.dev_attr.attr, -	NULL, -}; - -static struct attribute_group ad7992_scan_el_group = { -	.name = "scan_elements", -	.attrs = ad7992_scan_el_attrs, -}; - -static struct attribute *ad7997_8_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_name.dev_attr.attr, -	&iio_dev_attr_in_scale.dev_attr.attr, -	NULL -}; - -static struct attribute_group ad7997_8_dev_attr_group = { -	.attrs = ad7997_8_device_attrs, -}; - -static struct attribute *ad7997_8_scan_el_attrs[] = { -	&iio_scan_el_in0.dev_attr.attr, -	&iio_const_attr_in0_index.dev_attr.attr, -	&iio_scan_el_in1.dev_attr.attr, -	&iio_const_attr_in1_index.dev_attr.attr, -	&iio_scan_el_in2.dev_attr.attr, -	&iio_const_attr_in2_index.dev_attr.attr, -	&iio_scan_el_in3.dev_attr.attr, -	&iio_const_attr_in3_index.dev_attr.attr, -	&iio_scan_el_in4.dev_attr.attr, -	&iio_const_attr_in4_index.dev_attr.attr, -	&iio_scan_el_in5.dev_attr.attr, -	&iio_const_attr_in5_index.dev_attr.attr, -	&iio_scan_el_in6.dev_attr.attr, -	&iio_const_attr_in6_index.dev_attr.attr, -	&iio_scan_el_in7.dev_attr.attr, -	&iio_const_attr_in7_index.dev_attr.attr, -	&iio_dev_attr_in_type.dev_attr.attr, -	NULL, -}; - -static struct attribute_group ad7997_8_scan_el_group = { -	.name = "scan_elements", -	.attrs = ad7997_8_scan_el_attrs, -}; - -IIO_EVENT_ATTR_SH(in0_thresh_low_value, -		  iio_event_ad799x, -		  ad799x_read_channel_config, -		  ad799x_write_channel_config, -		  AD7998_DATALOW_CH1_REG); - -IIO_EVENT_ATTR_SH(in0_thresh_high_value, -		  iio_event_ad799x, -		  ad799x_read_channel_config, -		  ad799x_write_channel_config, -		  AD7998_DATAHIGH_CH1_REG); - -IIO_EVENT_ATTR_SH(in0_thresh_both_hyst_raw, -		  iio_event_ad799x, -		  ad799x_read_channel_config, -		  ad799x_write_channel_config, -		  AD7998_HYST_CH1_REG); - -IIO_EVENT_ATTR_SH(in1_thresh_low_value, -		  iio_event_ad799x, -		  ad799x_read_channel_config, -		  ad799x_write_channel_config, -		  AD7998_DATALOW_CH2_REG); - -IIO_EVENT_ATTR_SH(in1_thresh_high_value, -		  iio_event_ad799x, -		  ad799x_read_channel_config, -		  ad799x_write_channel_config, -		  AD7998_DATAHIGH_CH2_REG); - -IIO_EVENT_ATTR_SH(in1_thresh_both_hyst_raw, -		  iio_event_ad799x, -		  ad799x_read_channel_config, -		  ad799x_write_channel_config, -		  AD7998_HYST_CH2_REG); - -IIO_EVENT_ATTR_SH(in2_thresh_low_value, -		  iio_event_ad799x, -		  ad799x_read_channel_config, -		  ad799x_write_channel_config, -		  AD7998_DATALOW_CH3_REG); - -IIO_EVENT_ATTR_SH(in2_thresh_high_value, -		  iio_event_ad799x, -		  ad799x_read_channel_config, -		  ad799x_write_channel_config, -		  AD7998_DATAHIGH_CH3_REG); - -IIO_EVENT_ATTR_SH(in2_thresh_both_hyst_raw, -		  iio_event_ad799x, -		  ad799x_read_channel_config, -		  ad799x_write_channel_config, -		  AD7998_HYST_CH3_REG); - -IIO_EVENT_ATTR_SH(in3_thresh_low_value, -		  iio_event_ad799x, -		  ad799x_read_channel_config, -		  ad799x_write_channel_config, -		  AD7998_DATALOW_CH4_REG); - -IIO_EVENT_ATTR_SH(in3_thresh_high_value, -		  iio_event_ad799x, -		  ad799x_read_channel_config, -		  ad799x_write_channel_config, -		  AD7998_DATAHIGH_CH4_REG); - -IIO_EVENT_ATTR_SH(in3_thresh_both_hyst_raw, -		  iio_event_ad799x, -		  ad799x_read_channel_config, -		  ad799x_write_channel_config, -		  AD7998_HYST_CH4_REG); - -static IIO_DEV_ATTR_SAMP_FREQ(S_IWUSR | S_IRUGO, -			      ad799x_read_frequency, -			      ad799x_write_frequency); -static IIO_CONST_ATTR_SAMP_FREQ_AVAIL("15625 7812 3906 1953 976 488 244 0"); - -static struct attribute *ad7993_4_7_8_event_attributes[] = { -	&iio_event_attr_in0_thresh_low_value.dev_attr.attr, -	&iio_event_attr_in0_thresh_high_value.dev_attr.attr, -	&iio_event_attr_in0_thresh_both_hyst_raw.dev_attr.attr, -	&iio_event_attr_in1_thresh_low_value.dev_attr.attr, -	&iio_event_attr_in1_thresh_high_value.dev_attr.attr, -	&iio_event_attr_in1_thresh_both_hyst_raw.dev_attr.attr, -	&iio_event_attr_in2_thresh_low_value.dev_attr.attr, -	&iio_event_attr_in2_thresh_high_value.dev_attr.attr, -	&iio_event_attr_in2_thresh_both_hyst_raw.dev_attr.attr, -	&iio_event_attr_in3_thresh_low_value.dev_attr.attr, -	&iio_event_attr_in3_thresh_high_value.dev_attr.attr, -	&iio_event_attr_in3_thresh_both_hyst_raw.dev_attr.attr, -	&iio_dev_attr_sampling_frequency.dev_attr.attr, -	&iio_const_attr_sampling_frequency_available.dev_attr.attr, -	NULL, -}; - -static struct attribute_group ad7993_4_7_8_event_attrs_group = { -	.attrs = ad7993_4_7_8_event_attributes, -}; - -static struct attribute *ad7992_event_attributes[] = { -	&iio_event_attr_in0_thresh_low_value.dev_attr.attr, -	&iio_event_attr_in0_thresh_high_value.dev_attr.attr, -	&iio_event_attr_in0_thresh_both_hyst_raw.dev_attr.attr, -	&iio_event_attr_in1_thresh_low_value.dev_attr.attr, -	&iio_event_attr_in1_thresh_high_value.dev_attr.attr, -	&iio_event_attr_in1_thresh_both_hyst_raw.dev_attr.attr, -	&iio_dev_attr_sampling_frequency.dev_attr.attr, -	&iio_const_attr_sampling_frequency_available.dev_attr.attr, -	NULL, -}; - -static struct attribute_group ad7992_event_attrs_group = { -	.attrs = ad7992_event_attributes, -}; - -static const struct ad799x_chip_info ad799x_chip_info_tbl[] = { -	[ad7991] = { -		.num_inputs = 4, -		.bits = 12, -		.sign = IIO_SCAN_EL_TYPE_UNSIGNED, -		.int_vref_mv = 4096, -		.dev_attrs = &ad7991_5_9_3_4_dev_attr_group, -		.scan_attrs = &ad7991_5_9_3_4_scan_el_group, -		.ad799x_set_scan_mode = ad7991_5_9_set_scan_mode, -	}, -	[ad7995] = { -		.num_inputs = 4, -		.bits = 10, -		.sign = IIO_SCAN_EL_TYPE_UNSIGNED, -		.int_vref_mv = 1024, -		.dev_attrs = &ad7991_5_9_3_4_dev_attr_group, -		.scan_attrs = &ad7991_5_9_3_4_scan_el_group, -		.ad799x_set_scan_mode = ad7991_5_9_set_scan_mode, -	}, -	[ad7999] = { -		.num_inputs = 4, -		.bits = 10, -		.sign = IIO_SCAN_EL_TYPE_UNSIGNED, -		.int_vref_mv = 1024, -		.dev_attrs = &ad7991_5_9_3_4_dev_attr_group, -		.scan_attrs = &ad7991_5_9_3_4_scan_el_group, -		.ad799x_set_scan_mode = ad7991_5_9_set_scan_mode, -	}, -	[ad7992] = { -		.num_inputs = 2, -		.bits = 12, -		.sign = IIO_SCAN_EL_TYPE_UNSIGNED, -		.int_vref_mv = 4096, -		.monitor_mode = true, -		.default_config = AD7998_ALERT_EN, -		.dev_attrs = &ad7992_dev_attr_group, -		.scan_attrs = &ad7992_scan_el_group, -		.event_attrs = &ad7992_event_attrs_group, -		.ad799x_set_scan_mode = ad7992_3_4_set_scan_mode, -	}, -	[ad7993] = { -		.num_inputs = 4, -		.bits = 10, -		.sign = IIO_SCAN_EL_TYPE_UNSIGNED, -		.int_vref_mv = 1024, -		.monitor_mode = true, -		.default_config = AD7998_ALERT_EN, -		.dev_attrs = &ad7991_5_9_3_4_dev_attr_group, -		.scan_attrs = &ad7991_5_9_3_4_scan_el_group, -		.event_attrs = &ad7993_4_7_8_event_attrs_group, -		.ad799x_set_scan_mode = ad7992_3_4_set_scan_mode, -	}, -	[ad7994] = { -		.num_inputs = 4, -		.bits = 12, -		.sign = IIO_SCAN_EL_TYPE_UNSIGNED, -		.int_vref_mv = 4096, -		.monitor_mode = true, -		.default_config = AD7998_ALERT_EN, -		.dev_attrs = &ad7991_5_9_3_4_dev_attr_group, -		.scan_attrs = &ad7991_5_9_3_4_scan_el_group, -		.event_attrs = &ad7993_4_7_8_event_attrs_group, -		.ad799x_set_scan_mode = ad7992_3_4_set_scan_mode, -	}, -	[ad7997] = { -		.num_inputs = 8, -		.bits = 10, -		.sign = IIO_SCAN_EL_TYPE_UNSIGNED, -		.int_vref_mv = 1024, -		.monitor_mode = true, -		.default_config = AD7998_ALERT_EN, -		.dev_attrs = &ad7997_8_dev_attr_group, -		.scan_attrs = &ad7997_8_scan_el_group, -		.event_attrs = &ad7993_4_7_8_event_attrs_group, -		.ad799x_set_scan_mode = ad7997_8_set_scan_mode, -	}, -	[ad7998] = { -		.num_inputs = 8, -		.bits = 12, -		.sign = IIO_SCAN_EL_TYPE_UNSIGNED, -		.int_vref_mv = 4096, -		.monitor_mode = true, -		.default_config = AD7998_ALERT_EN, -		.dev_attrs = &ad7997_8_dev_attr_group, -		.scan_attrs = &ad7997_8_scan_el_group, -		.event_attrs = &ad7993_4_7_8_event_attrs_group, -		.ad799x_set_scan_mode = ad7997_8_set_scan_mode, -	}, -}; - -static int __devinit ad799x_probe(struct i2c_client *client, -				   const struct i2c_device_id *id) -{ -	int ret, regdone = 0; -	struct ad799x_platform_data *pdata = client->dev.platform_data; -	struct ad799x_state *st = kzalloc(sizeof(*st), GFP_KERNEL); -	if (st == NULL) { -		ret = -ENOMEM; -		goto error_ret; -	} - -	/* this is only used for device removal purposes */ -	i2c_set_clientdata(client, st); - -	atomic_set(&st->protect_ring, 0); -	st->id = id->driver_data; -	st->chip_info = &ad799x_chip_info_tbl[st->id]; -	st->config = st->chip_info->default_config; - -	/* TODO: Add pdata options for filtering and bit delay */ - -	if (pdata) -		st->int_vref_mv = pdata->vref_mv; -	else -		st->int_vref_mv = st->chip_info->int_vref_mv; - -	st->reg = regulator_get(&client->dev, "vcc"); -	if (!IS_ERR(st->reg)) { -		ret = regulator_enable(st->reg); -		if (ret) -			goto error_put_reg; -	} -	st->client = client; - -	st->indio_dev = iio_allocate_device(); -	if (st->indio_dev == NULL) { -		ret = -ENOMEM; -		goto error_disable_reg; -	} - -	/* Estabilish that the iio_dev is a child of the i2c device */ -	st->indio_dev->dev.parent = &client->dev; -	st->indio_dev->attrs = st->chip_info->dev_attrs; -	st->indio_dev->event_attrs = st->chip_info->event_attrs; - -	st->indio_dev->dev_data = (void *)(st); -	st->indio_dev->driver_module = THIS_MODULE; -	st->indio_dev->modes = INDIO_DIRECT_MODE; -	st->indio_dev->num_interrupt_lines = 1; - -	ret = ad799x_set_scan_mode(st, 0); -	if (ret) -		goto error_free_device; - -	ret = ad799x_register_ring_funcs_and_init(st->indio_dev); -	if (ret) -		goto error_free_device; - -	ret = iio_device_register(st->indio_dev); -	if (ret) -		goto error_cleanup_ring; -	regdone = 1; - -	ret = iio_ring_buffer_register(st->indio_dev->ring, 0); -	if (ret) -		goto error_cleanup_ring; - -	if (client->irq > 0 && st->chip_info->monitor_mode) { -		INIT_WORK(&st->work_thresh, ad799x_interrupt_bh); - -		ret = iio_register_interrupt_line(client->irq, -				st->indio_dev, -				0, -				IRQF_TRIGGER_FALLING, -				client->name); -		if (ret) -			goto error_cleanup_ring; - -		/* -		 * The event handler list element refer to iio_event_ad799x. -		 * All event attributes bind to the same event handler. -		 * So, only register event handler once. -		 */ -		iio_add_event_to_list(&iio_event_ad799x, -				&st->indio_dev->interrupts[0]->ev_list); -	} - -	return 0; -error_cleanup_ring: -	ad799x_ring_cleanup(st->indio_dev); -error_free_device: -	if (!regdone) -		iio_free_device(st->indio_dev); -	else -		iio_device_unregister(st->indio_dev); -error_disable_reg: -	if (!IS_ERR(st->reg)) -		regulator_disable(st->reg); -error_put_reg: -	if (!IS_ERR(st->reg)) -		regulator_put(st->reg); -	kfree(st); -error_ret: -	return ret; -} - -static __devexit int ad799x_remove(struct i2c_client *client) -{ -	struct ad799x_state *st = i2c_get_clientdata(client); -	struct iio_dev *indio_dev = st->indio_dev; - -	if (client->irq > 0 && st->chip_info->monitor_mode) -		iio_unregister_interrupt_line(indio_dev, 0); - -	iio_ring_buffer_unregister(indio_dev->ring); -	ad799x_ring_cleanup(indio_dev); -	iio_device_unregister(indio_dev); -	if (!IS_ERR(st->reg)) { -		regulator_disable(st->reg); -		regulator_put(st->reg); -	} -	kfree(st); - -	return 0; -} - -static const struct i2c_device_id ad799x_id[] = { -	{ "ad7991", ad7991 }, -	{ "ad7995", ad7995 }, -	{ "ad7999", ad7999 }, -	{ "ad7992", ad7992 }, -	{ "ad7993", ad7993 }, -	{ "ad7994", ad7994 }, -	{ "ad7997", ad7997 }, -	{ "ad7998", ad7998 }, -	{} -}; - -MODULE_DEVICE_TABLE(i2c, ad799x_id); - -static struct i2c_driver ad799x_driver = { -	.driver = { -		.name = "ad799x", -	}, -	.probe = ad799x_probe, -	.remove = __devexit_p(ad799x_remove), -	.id_table = ad799x_id, -}; - -static __init int ad799x_init(void) -{ -	return i2c_add_driver(&ad799x_driver); -} - -static __exit void ad799x_exit(void) -{ -	i2c_del_driver(&ad799x_driver); -} - -MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>"); -MODULE_DESCRIPTION("Analog Devices AD799x ADC"); -MODULE_LICENSE("GPL v2"); -MODULE_ALIAS("i2c:ad799x"); - -module_init(ad799x_init); -module_exit(ad799x_exit); diff --git a/drivers/staging/iio/adc/ad799x_ring.c b/drivers/staging/iio/adc/ad799x_ring.c deleted file mode 100644 index 975cdcbf083..00000000000 --- a/drivers/staging/iio/adc/ad799x_ring.c +++ /dev/null @@ -1,240 +0,0 @@ -/* - * Copyright (C) 2010 Michael Hennerich, Analog Devices Inc. - * Copyright (C) 2008-2010 Jonathan Cameron - * - * 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. - * - * ad799x_ring.c - */ - -#include <linux/interrupt.h> -#include <linux/workqueue.h> -#include <linux/device.h> -#include <linux/slab.h> -#include <linux/kernel.h> -#include <linux/sysfs.h> -#include <linux/list.h> -#include <linux/i2c.h> -#include <linux/bitops.h> - -#include "../iio.h" -#include "../ring_generic.h" -#include "../ring_sw.h" -#include "../trigger.h" -#include "../sysfs.h" - -#include "ad799x.h" - -int ad799x_single_channel_from_ring(struct ad799x_state *st, long mask) -{ -	struct iio_ring_buffer *ring = st->indio_dev->ring; -	int count = 0, ret; -	u16 *ring_data; - -	if (!(ring->scan_mask & mask)) { -		ret = -EBUSY; -		goto error_ret; -	} - -	ring_data = kmalloc(ring->access.get_bytes_per_datum(ring), GFP_KERNEL); -	if (ring_data == NULL) { -		ret = -ENOMEM; -		goto error_ret; -	} -	ret = ring->access.read_last(ring, (u8 *) ring_data); -	if (ret) -		goto error_free_ring_data; -	/* Need a count of channels prior to this one */ -	mask >>= 1; -	while (mask) { -		if (mask & ring->scan_mask) -			count++; -		mask >>= 1; -	} - -	ret = be16_to_cpu(ring_data[count]); - -error_free_ring_data: -	kfree(ring_data); -error_ret: -	return ret; -} - -/** - * ad799x_ring_preenable() setup the parameters of the ring before enabling - * - * The complex nature of the setting of the nuber of bytes per datum is due - * to this driver currently ensuring that the timestamp is stored at an 8 - * byte boundary. - **/ -static int ad799x_ring_preenable(struct iio_dev *indio_dev) -{ -	struct iio_ring_buffer *ring = indio_dev->ring; -	struct ad799x_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 -	 */ - -	if (st->id == ad7997 || st->id == ad7998) -		ad799x_set_scan_mode(st, ring->scan_mask); - -	numvals = ring->scan_count; - -	if (ring->access.set_bytes_per_datum) { -		d_size = numvals*2 + sizeof(s64); -		if (d_size % 8) -			d_size += 8 - (d_size % 8); -		ring->access.set_bytes_per_datum(ring, d_size); -	} - -	return 0; -} - -/** - * ad799x_poll_func_th() th of trigger launched polling to ring buffer - * - * As sampling only occurs on i2c comms occuring, leave timestamping until - * then.  Some triggers will generate their own time stamp.  Currently - * there is no way of notifying them when no one cares. - **/ -static void ad799x_poll_func_th(struct iio_dev *indio_dev, s64 time) -{ -	struct ad799x_state *st = indio_dev->dev_data; - -	schedule_work(&st->poll_work); - -	return; -} -/** - * ad799x_poll_bh_to_ring() bh of trigger launched polling to ring buffer - * @work_s:	the work struct through which this was scheduled - * - * Currently there is no option in this driver to disable the saving of - * timestamps within the ring. - * I think the one copy of this at a time was to avoid problems if the - * trigger was set far too high and the reads then locked up the computer. - **/ -static void ad799x_poll_bh_to_ring(struct work_struct *work_s) -{ -	struct ad799x_state *st = container_of(work_s, struct ad799x_state, -						  poll_work); -	struct iio_dev *indio_dev = st->indio_dev; -	struct iio_ring_buffer *ring = indio_dev->ring; -	struct iio_sw_ring_buffer *ring_sw = iio_to_sw_ring(indio_dev->ring); -	s64 time_ns; -	__u8 *rxbuf; -	int b_sent; -	size_t d_size; -	u8 cmd; - -	unsigned long numvals = ring->scan_count; - -	/* Ensure the timestamp is 8 byte aligned */ -	d_size = numvals*2 + sizeof(s64); - -	if (d_size % sizeof(s64)) -		d_size += sizeof(s64) - (d_size % sizeof(s64)); - -	/* Ensure only one copy of this function running at a time */ -	if (atomic_inc_return(&st->protect_ring) > 1) -		return; - -	/* Monitor mode prevents reading. Whilst not currently implemented -	 * might as well have this test in here in the meantime as it does -	 * no harm. -	 */ -	if (numvals == 0) -		return; - -	rxbuf = kmalloc(d_size,	GFP_KERNEL); -	if (rxbuf == NULL) -		return; - -	switch (st->id) { -	case ad7991: -	case ad7995: -	case ad7999: -		cmd = st->config | (ring->scan_mask << AD799X_CHANNEL_SHIFT); -		break; -	case ad7992: -	case ad7993: -	case ad7994: -		cmd = (ring->scan_mask << AD799X_CHANNEL_SHIFT) | -			AD7998_CONV_RES_REG; -		break; -	case ad7997: -	case ad7998: -		cmd = AD7997_8_READ_SEQUENCE | AD7998_CONV_RES_REG; -		break; -	default: -		cmd = 0; -	} - -	b_sent = i2c_smbus_read_i2c_block_data(st->client, -			cmd, numvals*2, rxbuf); -	if (b_sent < 0) -		goto done; - -	time_ns = iio_get_time_ns(); - -	memcpy(rxbuf + d_size - sizeof(s64), &time_ns, sizeof(time_ns)); - -	ring->access.store_to(&ring_sw->buf, rxbuf, time_ns); -done: -	kfree(rxbuf); -	atomic_dec(&st->protect_ring); -} - - -int ad799x_register_ring_funcs_and_init(struct iio_dev *indio_dev) -{ -	struct ad799x_state *st = indio_dev->dev_data; -	int ret = 0; - -	indio_dev->ring = iio_sw_rb_allocate(indio_dev); -	if (!indio_dev->ring) { -		ret = -ENOMEM; -		goto error_ret; -	} -	/* Effectively select the ring buffer implementation */ -	iio_ring_sw_register_funcs(&st->indio_dev->ring->access); -	ret = iio_alloc_pollfunc(indio_dev, NULL, &ad799x_poll_func_th); -	if (ret) -		goto error_deallocate_sw_rb; - -	/* Ring buffer functions - here trigger setup related */ - -	indio_dev->ring->preenable = &ad799x_ring_preenable; -	indio_dev->ring->postenable = &iio_triggered_ring_postenable; -	indio_dev->ring->predisable = &iio_triggered_ring_predisable; - -	INIT_WORK(&st->poll_work, &ad799x_poll_bh_to_ring); - -	indio_dev->ring->scan_el_attrs = st->chip_info->scan_attrs; - -	/* Flag that polled ring buffering is possible */ -	indio_dev->modes |= INDIO_RING_TRIGGERED; -	return 0; -error_deallocate_sw_rb: -	iio_sw_rb_free(indio_dev->ring); -error_ret: -	return ret; -} - -void ad799x_ring_cleanup(struct iio_dev *indio_dev) -{ -	/* ensure that the trigger has been detached */ -	if (indio_dev->trig) { -		iio_put_trigger(indio_dev->trig); -		iio_trigger_dettach_poll_func(indio_dev->trig, -					      indio_dev->pollfunc); -	} -	kfree(indio_dev->pollfunc); -	iio_sw_rb_free(indio_dev->ring); -} diff --git a/drivers/staging/iio/adc/adc.h b/drivers/staging/iio/adc/adc.h deleted file mode 100644 index 40c5949880b..00000000000 --- a/drivers/staging/iio/adc/adc.h +++ /dev/null @@ -1,42 +0,0 @@ -/* - * adc.h - sysfs attributes associated with ADCs - * - * 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. - * - * Copyright (c) 2008 Jonathan Cameron <jic23@cam.ac.uk> - * - */ - -/* 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(_num, _name, _show, _addr)		\ -	IIO_DEVICE_ATTR(in##_num##_##_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) - - -#define IIO_CONST_ATTR_IN_NAMED_OFFSET(_num, _name, _string)	\ -	IIO_CONST_ATTR(in##_num##_##_name##_offset, _string) - -#define IIO_CONST_ATTR_IN_NAMED_SCALE(_num, _name, _string)	\ -	IIO_CONST_ATTR(in##_num##_##_name##_scale, _string) - -#define IIO_EVENT_CODE_IN_HIGH_THRESH(a)				\ -	IIO_UNMOD_EVENT_CODE(IIO_EV_CLASS_IN, a, IIO_EV_TYPE_THRESH,	\ -			     IIO_EV_DIR_RISING) -#define IIO_EVENT_CODE_IN_LOW_THRESH(a)				     \ -	IIO_UNMOD_EVENT_CODE(IIO_EV_CLASS_IN, a, IIO_EV_TYPE_THRESH, \ -			     IIO_EV_DIR_FALLING) diff --git a/drivers/staging/iio/adc/lpc32xx_adc.c b/drivers/staging/iio/adc/lpc32xx_adc.c new file mode 100644 index 00000000000..a876ce75535 --- /dev/null +++ b/drivers/staging/iio/adc/lpc32xx_adc.c @@ -0,0 +1,216 @@ +/* + *  lpc32xx_adc.c - Support for ADC in LPC32XX + * + *  3-channel, 10-bit ADC + * + *  Copyright (C) 2011, 2012 Roland Stigge <stigge@antcom.de> + * + *  This program is free software; you can redistribute it and/or modify + *  it under the terms of the GNU General Public License as published by + *  the Free Software Foundation; either version 2 of the License, or + *  (at your option) any later version. + * + *  This program is distributed in the hope that it will be useful, + *  but WITHOUT ANY WARRANTY; without even the implied warranty of + *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + *  GNU General Public License for more details. + * + *  You should have received a copy of the GNU General Public License + *  along with this program; if not, write to the Free Software + *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/interrupt.h> +#include <linux/device.h> +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/io.h> +#include <linux/clk.h> +#include <linux/err.h> +#include <linux/completion.h> +#include <linux/of.h> + +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> + +/* + * LPC32XX registers definitions + */ +#define LPC32XX_ADC_SELECT(x)	((x) + 0x04) +#define LPC32XX_ADC_CTRL(x)	((x) + 0x08) +#define LPC32XX_ADC_VALUE(x)	((x) + 0x48) + +/* Bit definitions for LPC32XX_ADC_SELECT: */ +#define AD_REFm         0x00000200 /* constant, always write this value! */ +#define AD_REFp		0x00000080 /* constant, always write this value! */ +#define AD_IN		0x00000010 /* multiple of this is the */ +				   /* channel number: 0, 1, 2 */ +#define AD_INTERNAL	0x00000004 /* constant, always write this value! */ + +/* Bit definitions for LPC32XX_ADC_CTRL: */ +#define AD_STROBE	0x00000002 +#define AD_PDN_CTRL	0x00000004 + +/* Bit definitions for LPC32XX_ADC_VALUE: */ +#define ADC_VALUE_MASK	0x000003FF + +#define MOD_NAME "lpc32xx-adc" + +struct lpc32xx_adc_info { +	void __iomem *adc_base; +	struct clk *clk; +	struct completion completion; + +	u32 value; +}; + +static int lpc32xx_read_raw(struct iio_dev *indio_dev, +				struct iio_chan_spec const *chan, +				int *val, +				int *val2, +				long mask) +{ +	struct lpc32xx_adc_info *info = iio_priv(indio_dev); + +	if (mask == IIO_CHAN_INFO_RAW) { +		mutex_lock(&indio_dev->mlock); +		clk_enable(info->clk); +		/* Measurement setup */ +		__raw_writel(AD_INTERNAL | (chan->address) | AD_REFp | AD_REFm, +			LPC32XX_ADC_SELECT(info->adc_base)); +		/* Trigger conversion */ +		__raw_writel(AD_PDN_CTRL | AD_STROBE, +			LPC32XX_ADC_CTRL(info->adc_base)); +		wait_for_completion(&info->completion); /* set by ISR */ +		clk_disable(info->clk); +		*val = info->value; +		mutex_unlock(&indio_dev->mlock); + +		return IIO_VAL_INT; +	} + +	return -EINVAL; +} + +static const struct iio_info lpc32xx_adc_iio_info = { +	.read_raw = &lpc32xx_read_raw, +	.driver_module = THIS_MODULE, +}; + +#define LPC32XX_ADC_CHANNEL(_index) {			\ +	.type = IIO_VOLTAGE,				\ +	.indexed = 1,					\ +	.channel = _index,				\ +	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),	\ +	.address = AD_IN * _index,			\ +	.scan_index = _index,				\ +} + +static const struct iio_chan_spec lpc32xx_adc_iio_channels[] = { +	LPC32XX_ADC_CHANNEL(0), +	LPC32XX_ADC_CHANNEL(1), +	LPC32XX_ADC_CHANNEL(2), +}; + +static irqreturn_t lpc32xx_adc_isr(int irq, void *dev_id) +{ +	struct lpc32xx_adc_info *info = (struct lpc32xx_adc_info *) dev_id; + +	/* Read value and clear irq */ +	info->value = __raw_readl(LPC32XX_ADC_VALUE(info->adc_base)) & +				ADC_VALUE_MASK; +	complete(&info->completion); + +	return IRQ_HANDLED; +} + +static int lpc32xx_adc_probe(struct platform_device *pdev) +{ +	struct lpc32xx_adc_info *info = NULL; +	struct resource *res; +	int retval = -ENODEV; +	struct iio_dev *iodev = NULL; +	int irq; + +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0); +	if (!res) { +		dev_err(&pdev->dev, "failed to get platform I/O memory\n"); +		return -EBUSY; +	} + +	iodev = devm_iio_device_alloc(&pdev->dev, sizeof(*info)); +	if (!iodev) +		return -ENOMEM; + +	info = iio_priv(iodev); + +	info->adc_base = devm_ioremap(&pdev->dev, res->start, +						resource_size(res)); +	if (!info->adc_base) { +		dev_err(&pdev->dev, "failed mapping memory\n"); +		return -EBUSY; +	} + +	info->clk = devm_clk_get(&pdev->dev, NULL); +	if (IS_ERR(info->clk)) { +		dev_err(&pdev->dev, "failed getting clock\n"); +		return PTR_ERR(info->clk); +	} + +	irq = platform_get_irq(pdev, 0); +	if (irq <= 0) { +		dev_err(&pdev->dev, "failed getting interrupt resource\n"); +		return -EINVAL; +	} + +	retval = devm_request_irq(&pdev->dev, irq, lpc32xx_adc_isr, 0, +								MOD_NAME, info); +	if (retval < 0) { +		dev_err(&pdev->dev, "failed requesting interrupt\n"); +		return retval; +	} + +	platform_set_drvdata(pdev, iodev); + +	init_completion(&info->completion); + +	iodev->name = MOD_NAME; +	iodev->dev.parent = &pdev->dev; +	iodev->info = &lpc32xx_adc_iio_info; +	iodev->modes = INDIO_DIRECT_MODE; +	iodev->channels = lpc32xx_adc_iio_channels; +	iodev->num_channels = ARRAY_SIZE(lpc32xx_adc_iio_channels); + +	retval = devm_iio_device_register(&pdev->dev, iodev); +	if (retval) +		return retval; + +	dev_info(&pdev->dev, "LPC32XX ADC driver loaded, IRQ %d\n", irq); + +	return 0; +} + +#ifdef CONFIG_OF +static const struct of_device_id lpc32xx_adc_match[] = { +	{ .compatible = "nxp,lpc3220-adc" }, +	{}, +}; +MODULE_DEVICE_TABLE(of, lpc32xx_adc_match); +#endif + +static struct platform_driver lpc32xx_adc_driver = { +	.probe		= lpc32xx_adc_probe, +	.driver		= { +		.name	= MOD_NAME, +		.owner	= THIS_MODULE, +		.of_match_table = of_match_ptr(lpc32xx_adc_match), +	}, +}; + +module_platform_driver(lpc32xx_adc_driver); + +MODULE_AUTHOR("Roland Stigge <stigge@antcom.de>"); +MODULE_DESCRIPTION("LPC32XX ADC driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/staging/iio/adc/max1363.h b/drivers/staging/iio/adc/max1363.h deleted file mode 100644 index 8f0fe1ced2c..00000000000 --- a/drivers/staging/iio/adc/max1363.h +++ /dev/null @@ -1,250 +0,0 @@ -#ifndef _MAX1363_H_ -#define  _MAX1363_H_ - -#define MAX1363_SETUP_BYTE(a) ((a) | 0x80) - -/* There is a fair bit more defined here than currently - * used, but the intention is to support everything these - * chips do in the long run */ - -/* see data sheets */ -/* max1363 and max1236, max1237, max1238, max1239 */ -#define MAX1363_SETUP_AIN3_IS_AIN3_REF_IS_VDD	0x00 -#define MAX1363_SETUP_AIN3_IS_REF_EXT_TO_REF	0x20 -#define MAX1363_SETUP_AIN3_IS_AIN3_REF_IS_INT	0x40 -#define MAX1363_SETUP_AIN3_IS_REF_REF_IS_INT	0x60 -#define MAX1363_SETUP_POWER_UP_INT_REF		0x10 -#define MAX1363_SETUP_POWER_DOWN_INT_REF	0x00 - -/* think about includeing max11600 etc - more settings */ -#define MAX1363_SETUP_EXT_CLOCK			0x08 -#define MAX1363_SETUP_INT_CLOCK			0x00 -#define MAX1363_SETUP_UNIPOLAR			0x00 -#define MAX1363_SETUP_BIPOLAR			0x04 -#define MAX1363_SETUP_RESET			0x00 -#define MAX1363_SETUP_NORESET			0x02 -/* max1363 only - though don't care on others. - * For now monitor modes are not implemented as the relevant - * line is not connected on my test board. - * The definitions are here as I intend to add this soon. - */ -#define MAX1363_SETUP_MONITOR_SETUP		0x01 - -/* Specific to the max1363 */ -#define MAX1363_MON_RESET_CHAN(a) (1 << ((a) + 4)) -#define MAX1363_MON_INT_ENABLE			0x01 - -/* defined for readability reasons */ -/* All chips */ -#define MAX1363_CONFIG_BYTE(a) ((a)) - -#define MAX1363_CONFIG_SE			0x01 -#define MAX1363_CONFIG_DE			0x00 -#define MAX1363_CONFIG_SCAN_TO_CS		0x00 -#define MAX1363_CONFIG_SCAN_SINGLE_8		0x20 -#define MAX1363_CONFIG_SCAN_MONITOR_MODE	0x40 -#define MAX1363_CONFIG_SCAN_SINGLE_1		0x60 -/* max123{6-9} only */ -#define MAX1236_SCAN_MID_TO_CHANNEL		0x40 - -/* max1363 only - merely part of channel selects or don't care for others*/ -#define MAX1363_CONFIG_EN_MON_MODE_READ 0x18 - -#define MAX1363_CHANNEL_SEL(a) ((a) << 1) - -/* max1363 strictly 0x06 - but doesn't matter */ -#define MAX1363_CHANNEL_SEL_MASK		0x1E -#define MAX1363_SCAN_MASK			0x60 -#define MAX1363_SE_DE_MASK			0x01 - -/** - * struct max1363_mode - scan mode information - * @conf:	The corresponding value of the configuration register - * @modemask:	Bit mask corresponding to channels enabled in this mode - */ -struct max1363_mode { -	int8_t		conf; -	long		modemask; -}; - -#define MAX1363_MODE_SINGLE(_num, _mask) {				\ -		.conf = MAX1363_CHANNEL_SEL(_num)			\ -			| MAX1363_CONFIG_SCAN_SINGLE_1			\ -			| MAX1363_CONFIG_SE,				\ -			.modemask = _mask,				\ -			} - -#define MAX1363_MODE_SCAN_TO_CHANNEL(_num, _mask) {			\ -		.conf = MAX1363_CHANNEL_SEL(_num)			\ -			| MAX1363_CONFIG_SCAN_TO_CS			\ -			| MAX1363_CONFIG_SE,				\ -			.modemask = _mask,				\ -			} - - -/* note not available for max1363 hence naming */ -#define MAX1236_MODE_SCAN_MID_TO_CHANNEL(_mid, _num, _mask) {		\ -		.conf = MAX1363_CHANNEL_SEL(_num)			\ -			| MAX1236_SCAN_MID_TO_CHANNEL			\ -			| MAX1363_CONFIG_SE,				\ -			.modemask = _mask				\ -} - -#define MAX1363_MODE_DIFF_SINGLE(_nump, _numm, _mask) {			\ -		.conf = MAX1363_CHANNEL_SEL(_nump)			\ -			| MAX1363_CONFIG_SCAN_SINGLE_1			\ -			| MAX1363_CONFIG_DE,				\ -			.modemask = _mask				\ -			} - -/* Can't think how to automate naming so specify for now */ -#define MAX1363_MODE_DIFF_SCAN_TO_CHANNEL(_num, _numvals, _mask) { \ -		.conf = MAX1363_CHANNEL_SEL(_num)			\ -			| MAX1363_CONFIG_SCAN_TO_CS			\ -			| MAX1363_CONFIG_DE,				\ -			.modemask = _mask				\ -			} - -/* note only available for max1363 hence naming */ -#define MAX1236_MODE_DIFF_SCAN_MID_TO_CHANNEL(_num, _numvals, _mask) { \ -		.conf = MAX1363_CHANNEL_SEL(_num)			\ -			| MAX1236_SCAN_MID_TO_CHANNEL			\ -			| MAX1363_CONFIG_SE,				\ -			.modemask = _mask				\ -} - -/* This may seem an overly long winded way to do this, but at least it makes - * 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, -	/* Differential single read */ -	d0m1, d2m3, d4m5, d6m7, d8m9, d10m11, -	d1m0, d3m2, d5m4, d7m6, d9m8, d11m10, -	/* 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 - * @num_modes:		the number of scan modes available - * @default_mode:	the scan mode in which the chip starts up - */ -struct max1363_chip_info { -	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; -}; - -/** - * struct max1363_state - driver instance specific data - * @indio_dev:		the industrial I/O device - * @client:		i2c_client - * @setupbyte:		cache of current device setup byte - * @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 - * @monitor_on:		whether monitor mode is enabled - * @monitor_speed:	parameter corresponding to device monitor speed setting - * @mask_high:		bitmask for enabled high thresholds - * @mask_low:		bitmask for enabled low thresholds - * @thresh_high:	high threshold values - * @thresh_low:		low threshold values - * @last_timestamp:	timestamp of last event interrupt - * @thresh_work:	bh work structure for event handling - */ -struct max1363_state { -	struct iio_dev			*indio_dev; -	struct i2c_client		*client; -	u8				setupbyte; -	u8				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; - -	/* Using monitor modes and buffer at the same time is -	   currently not supported */ -	bool				monitor_on; -	unsigned int			monitor_speed:3; -	u8				mask_high; -	u8				mask_low; -	/* 4x unipolar first then the fours bipolar ones */ -	s16				thresh_high[8]; -	s16				thresh_low[8]; -	s64				last_timestamp; -	struct work_struct		thresh_work; -}; - -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 - -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); - -#else /* CONFIG_MAX1363_RING_BUFFER */ - -int max1363_single_channel_from_ring(long mask, struct max1363_state *st) -{ -	return -EINVAL; -} - -static inline int -max1363_register_ring_funcs_and_init(struct iio_dev *indio_dev) -{ -	return 0; -} - -static inline void max1363_ring_cleanup(struct iio_dev *indio_dev) -{ -} -#endif /* CONFIG_MAX1363_RING_BUFFER */ -#endif /* _MAX1363_H_ */ diff --git a/drivers/staging/iio/adc/max1363_core.c b/drivers/staging/iio/adc/max1363_core.c deleted file mode 100644 index dde097afb43..00000000000 --- a/drivers/staging/iio/adc/max1363_core.c +++ /dev/null @@ -1,1866 +0,0 @@ - /* -  * 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. -  * -  * - Control of internal reference. -  */ - -#include <linux/interrupt.h> -#include <linux/workqueue.h> -#include <linux/device.h> -#include <linux/kernel.h> -#include <linux/sysfs.h> -#include <linux/list.h> -#include <linux/i2c.h> -#include <linux/regulator/consumer.h> -#include <linux/slab.h> -#include <linux/err.h> - -#include "../iio.h" -#include "../sysfs.h" - -#include "../ring_generic.h" -#include "adc.h" -#include "max1363.h" - -/* 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, 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, 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_u(struct device *dev, -				struct device_attribute *attr, -				char *buf) -{ -	struct iio_ring_buffer *ring = dev_get_drvdata(dev); -	struct iio_dev *dev_info = ring->indio_dev; -	struct max1363_state *st = iio_dev_get_devdata(dev_info); -	return sprintf(buf, "u%d/16\n", st->chip_info->bits); -} - -static ssize_t max1363_show_precision_s(struct device *dev, -				struct device_attribute *attr, -				char *buf) -{ -	struct iio_ring_buffer *ring = dev_get_drvdata(dev); -	struct iio_dev *dev_info = ring->indio_dev; -	struct max1363_state *st = iio_dev_get_devdata(dev_info); -	return sprintf(buf, "s%d/16\n", st->chip_info->bits); -} - -#define MAX1363_SCAN_TYPE(n)						\ -	DEVICE_ATTR(in##n##_type, S_IRUGO,				\ -		    max1363_show_precision_u, NULL); -#define MAX1363_SCAN_TYPE_D(p, n)					\ -	struct device_attribute dev_attr_in##p##m##in##n##_type =	\ -		__ATTR(in##p-in##n##_type, S_IRUGO,			\ -		       max1363_show_precision_s, NULL); - -static MAX1363_SCAN_TYPE(0); -static MAX1363_SCAN_TYPE(1); -static MAX1363_SCAN_TYPE(2); -static MAX1363_SCAN_TYPE(3); -static MAX1363_SCAN_TYPE(4); -static MAX1363_SCAN_TYPE(5); -static MAX1363_SCAN_TYPE(6); -static MAX1363_SCAN_TYPE(7); -static MAX1363_SCAN_TYPE(8); -static MAX1363_SCAN_TYPE(9); -static MAX1363_SCAN_TYPE(10); -static MAX1363_SCAN_TYPE(11); - -static MAX1363_SCAN_TYPE_D(0, 1); -static MAX1363_SCAN_TYPE_D(2, 3); -static MAX1363_SCAN_TYPE_D(4, 5); -static MAX1363_SCAN_TYPE_D(6, 7); -static MAX1363_SCAN_TYPE_D(8, 9); -static MAX1363_SCAN_TYPE_D(10, 11); -static MAX1363_SCAN_TYPE_D(1, 0); -static MAX1363_SCAN_TYPE_D(3, 2); -static MAX1363_SCAN_TYPE_D(5, 4); -static MAX1363_SCAN_TYPE_D(7, 6); -static MAX1363_SCAN_TYPE_D(9, 8); -static MAX1363_SCAN_TYPE_D(11, 10); - -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 monitor mode is enabled, the method for reading a single -	 * channel will have to be rather different and has not yet -	 * been implemented. -	 */ -	if (st->monitor_on) { -		ret = -EBUSY; -		goto error_ret; -	} - -	/* 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->client->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, -	s0to1, s0to2, s0to3, -	d0m1, d2m3, d1m0, d3m2, -	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,	&dev_attr_in0_type.attr, -	&iio_const_attr_in0_index.dev_attr.attr, -	&iio_scan_el_in1.dev_attr.attr,	&dev_attr_in1_type.attr, -	&iio_const_attr_in1_index.dev_attr.attr, -	&iio_scan_el_in2.dev_attr.attr,	&dev_attr_in2_type.attr, -	&iio_const_attr_in2_index.dev_attr.attr, -	&iio_scan_el_in3.dev_attr.attr,	&dev_attr_in3_type.attr, -	&iio_const_attr_in3_index.dev_attr.attr, -	&iio_scan_el_in0min1.dev_attr.attr,	&dev_attr_in0min1_type.attr, -	&iio_const_attr_in0min1_index.dev_attr.attr, -	&iio_scan_el_in2min3.dev_attr.attr,	&dev_attr_in2min3_type.attr, -	&iio_const_attr_in2min3_index.dev_attr.attr, -	&iio_scan_el_in1min0.dev_attr.attr,	&dev_attr_in1min0_type.attr, -	&iio_const_attr_in1min0_index.dev_attr.attr, -	&iio_scan_el_in3min2.dev_attr.attr,	&dev_attr_in3min2_type.attr, -	&iio_const_attr_in3min2_index.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, -	s0to1, s0to2, s0to3, -	d0m1, d2m3, d1m0, d3m2, -	d0m1to2m3, d1m0to3m2, -	s2to3, -}; - -/* Applies to max1238, max1239 */ -static const enum max1363_modes max1238_mode_list[] = { -	_s0, _s1, _s2, _s3, _s4, _s5, _s6, _s7, _s8, _s9, _s10, _s11, -	s0to1, s0to2, s0to3, s0to4, s0to5, s0to6, -	s0to7, s0to8, s0to9, s0to10, s0to11, -	d0m1, d2m3, d4m5, d6m7, d8m9, d10m11, -	d1m0, d3m2, d5m4, d7m6, d9m8, d11m10, -	d0m1to2m3, d0m1to4m5, d0m1to6m7, d0m1to8m9, d0m1to10m11, -	d1m0to3m2, d1m0to5m4, d1m0to7m6, d1m0to9m8, d1m0to11m10, -	s6to7, s6to8, s6to9, s6to10, s6to11, -	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,	&dev_attr_in0_type.attr, -	&iio_const_attr_in0_index.dev_attr.attr, -	&iio_scan_el_in1.dev_attr.attr,	&dev_attr_in1_type.attr, -	&iio_const_attr_in1_index.dev_attr.attr, -	&iio_scan_el_in2.dev_attr.attr,	&dev_attr_in2_type.attr, -	&iio_const_attr_in2_index.dev_attr.attr, -	&iio_scan_el_in3.dev_attr.attr,	&dev_attr_in3_type.attr, -	&iio_const_attr_in3_index.dev_attr.attr, -	&iio_scan_el_in4.dev_attr.attr,	&dev_attr_in4_type.attr, -	&iio_const_attr_in4_index.dev_attr.attr, -	&iio_scan_el_in5.dev_attr.attr,	&dev_attr_in5_type.attr, -	&iio_const_attr_in5_index.dev_attr.attr, -	&iio_scan_el_in6.dev_attr.attr,	&dev_attr_in6_type.attr, -	&iio_const_attr_in6_index.dev_attr.attr, -	&iio_scan_el_in7.dev_attr.attr,	&dev_attr_in7_type.attr, -	&iio_const_attr_in7_index.dev_attr.attr, -	&iio_scan_el_in8.dev_attr.attr,	&dev_attr_in8_type.attr, -	&iio_const_attr_in8_index.dev_attr.attr, -	&iio_scan_el_in9.dev_attr.attr,	&dev_attr_in9_type.attr, -	&iio_const_attr_in9_index.dev_attr.attr, -	&iio_scan_el_in10.dev_attr.attr,	&dev_attr_in10_type.attr, -	&iio_const_attr_in10_index.dev_attr.attr, -	&iio_scan_el_in11.dev_attr.attr,	&dev_attr_in11_type.attr, -	&iio_const_attr_in11_index.dev_attr.attr, -	&iio_scan_el_in0min1.dev_attr.attr,	&dev_attr_in0min1_type.attr, -	&iio_const_attr_in0min1_index.dev_attr.attr, -	&iio_scan_el_in2min3.dev_attr.attr,	&dev_attr_in2min3_type.attr, -	&iio_const_attr_in2min3_index.dev_attr.attr, -	&iio_scan_el_in4min5.dev_attr.attr,	&dev_attr_in4min5_type.attr, -	&iio_const_attr_in4min5_index.dev_attr.attr, -	&iio_scan_el_in6min7.dev_attr.attr,	&dev_attr_in6min7_type.attr, -	&iio_const_attr_in6min7_index.dev_attr.attr, -	&iio_scan_el_in8min9.dev_attr.attr,	&dev_attr_in8min9_type.attr, -	&iio_const_attr_in8min9_index.dev_attr.attr, -	&iio_scan_el_in10min11.dev_attr.attr,	&dev_attr_in10min11_type.attr, -	&iio_const_attr_in10min11_index.dev_attr.attr, -	&iio_scan_el_in1min0.dev_attr.attr,	&dev_attr_in1min0_type.attr, -	&iio_const_attr_in1min0_index.dev_attr.attr, -	&iio_scan_el_in3min2.dev_attr.attr,	&dev_attr_in3min2_type.attr, -	&iio_const_attr_in3min2_index.dev_attr.attr, -	&iio_scan_el_in5min4.dev_attr.attr,	&dev_attr_in5min4_type.attr, -	&iio_const_attr_in5min4_index.dev_attr.attr, -	&iio_scan_el_in7min6.dev_attr.attr,	&dev_attr_in7min6_type.attr, -	&iio_const_attr_in7min6_index.dev_attr.attr, -	&iio_scan_el_in9min8.dev_attr.attr,	&dev_attr_in9min8_type.attr, -	&iio_const_attr_in9min8_index.dev_attr.attr, -	&iio_scan_el_in11min10.dev_attr.attr,	&dev_attr_in11min10_type.attr, -	&iio_const_attr_in11min10_index.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,	&dev_attr_in0_type.attr, -	&iio_const_attr_in0_index.dev_attr.attr, -	&iio_scan_el_in1.dev_attr.attr,	&dev_attr_in1_type.attr, -	&iio_const_attr_in1_index.dev_attr.attr, -	&iio_scan_el_in2.dev_attr.attr,	&dev_attr_in2_type.attr, -	&iio_const_attr_in2_index.dev_attr.attr, -	&iio_scan_el_in3.dev_attr.attr,	&dev_attr_in3_type.attr, -	&iio_const_attr_in3_index.dev_attr.attr, -	&iio_scan_el_in4.dev_attr.attr,	&dev_attr_in4_type.attr, -	&iio_const_attr_in4_index.dev_attr.attr, -	&iio_scan_el_in5.dev_attr.attr,	&dev_attr_in5_type.attr, -	&iio_const_attr_in5_index.dev_attr.attr, -	&iio_scan_el_in6.dev_attr.attr,	&dev_attr_in6_type.attr, -	&iio_const_attr_in6_index.dev_attr.attr, -	&iio_scan_el_in7.dev_attr.attr,	&dev_attr_in7_type.attr, -	&iio_const_attr_in7_index.dev_attr.attr, -	&iio_scan_el_in0min1.dev_attr.attr,	&dev_attr_in0min1_type.attr, -	&iio_const_attr_in0min1_index.dev_attr.attr, -	&iio_scan_el_in2min3.dev_attr.attr,	&dev_attr_in2min3_type.attr, -	&iio_const_attr_in2min3_index.dev_attr.attr, -	&iio_scan_el_in4min5.dev_attr.attr,	&dev_attr_in4min5_type.attr, -	&iio_const_attr_in4min5_index.dev_attr.attr, -	&iio_scan_el_in6min7.dev_attr.attr,	&dev_attr_in6min7_type.attr, -	&iio_const_attr_in6min7_index.dev_attr.attr, -	&iio_scan_el_in1min0.dev_attr.attr,	&dev_attr_in1min0_type.attr, -	&iio_const_attr_in1min0_index.dev_attr.attr, -	&iio_scan_el_in3min2.dev_attr.attr,	&dev_attr_in3min2_type.attr, -	&iio_const_attr_in3min2_index.dev_attr.attr, -	&iio_scan_el_in5min4.dev_attr.attr,	&dev_attr_in5min4_type.attr, -	&iio_const_attr_in5min4_index.dev_attr.attr, -	&iio_scan_el_in7min6.dev_attr.attr,	&dev_attr_in7min6_type.attr, -	&iio_const_attr_in7min6_index.dev_attr.attr, -	NULL -}; - -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, -       max1139, -       max1236, -       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 */ -static const struct max1363_chip_info max1363_chip_info_tbl[] = { -	[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, -	}, -	[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, -	}, -	[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, -	}, -	[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, -	}, -	[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, -	}, -	[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, -	}, -	[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, -	}, -	[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, -	}, -	[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, -	}, -	[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, -	}, -	[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, -	}, -	[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, -	}, -	[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, -	}, -	[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, -	}, -	[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, -	}, -	[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, -	}, -	[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, -	}, -	[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, -	}, -	[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, -	}, -	[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, -	}, -	[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, -	}, -	[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, -	}, -	[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, -	}, -	[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, -	}, -	[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, -	}, -	[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, -	}, -	[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, -	}, -	[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, -	}, -	[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, -	}, -	[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, -	}, -	[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, -	}, -	[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, -	}, -	[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, -	}, -	[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 const int max1363_monitor_speeds[] = { 133000, 665000, 33300, 16600, -					      8300, 4200, 2000, 1000 }; - -static ssize_t max1363_monitor_show_freq(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", max1363_monitor_speeds[st->monitor_speed]); -} - -static ssize_t max1363_monitor_store_freq(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 = iio_dev_get_devdata(dev_info); -	int i, ret; -	unsigned long val; -	bool found = false; - -	ret = strict_strtoul(buf, 10, &val); -	if (ret) -		return -EINVAL; -	for (i = 0; i < ARRAY_SIZE(max1363_monitor_speeds); i++) -		if (val == max1363_monitor_speeds[i]) { -			found = true; -			break; -		} -	if (!found) -		return -EINVAL; - -	mutex_lock(&dev_info->mlock); -	st->monitor_speed = i; -	mutex_unlock(&dev_info->mlock); - -	return 0; -} - -static IIO_DEV_ATTR_SAMP_FREQ(S_IRUGO | S_IWUSR, -			max1363_monitor_show_freq, -			max1363_monitor_store_freq); - -static IIO_CONST_ATTR(sampling_frequency_available, -		"133000 665000 33300 16600 8300 4200 2000 1000"); - -static ssize_t max1363_show_thresh(struct device *dev, -				struct device_attribute *attr, -				char *buf, -				bool high) -{ -	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); - -	if (high) -		return sprintf(buf, "%d\n", -			st->thresh_high[this_attr->address]); -	else -		return sprintf(buf, "%d\n", -			st->thresh_low[this_attr->address & 0x7]); -} - -static ssize_t max1363_show_thresh_low(struct device *dev, -				struct device_attribute *attr, -				char *buf) -{ -	return max1363_show_thresh(dev, attr, buf, false); -} - -static ssize_t max1363_show_thresh_high(struct device *dev, -					struct device_attribute *attr, -					char *buf) -{ -	return max1363_show_thresh(dev, attr, buf, true); -} - -static ssize_t max1363_store_thresh_unsigned(struct device *dev, -					struct device_attribute *attr, -					const char *buf, -					size_t len, -					bool high) -{ -	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); -	unsigned long val; -	int ret; - -	ret = strict_strtoul(buf, 10, &val); -	if (ret) -		return -EINVAL; -	switch (st->chip_info->bits) { -	case 10: -		if (val > 0x3FF) -			return -EINVAL; -		break; -	case 12: -		if (val > 0xFFF) -			return -EINVAL; -		break; -	} - -	switch (high) { -	case 1: -		st->thresh_high[this_attr->address] = val; -		break; -	case 0: -		st->thresh_low[this_attr->address & 0x7] = val; -		break; -	} - -	return len; -} - -static ssize_t max1363_store_thresh_high_unsigned(struct device *dev, -						struct device_attribute *attr, -						const char *buf, -						size_t len) -{ -	return max1363_store_thresh_unsigned(dev, attr, buf, len, true); -} - -static ssize_t max1363_store_thresh_low_unsigned(struct device *dev, -						struct device_attribute *attr, -						const char *buf, -						size_t len) -{ -	return max1363_store_thresh_unsigned(dev, attr, buf, len, false); -} - -static ssize_t max1363_store_thresh_signed(struct device *dev, -					struct device_attribute *attr, -					const char *buf, -					size_t len, -					bool high) -{ -	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); -	long val; -	int ret; - -	ret = strict_strtol(buf, 10, &val); -	if (ret) -		return -EINVAL; -	switch (st->chip_info->bits) { -	case 10: -		if (val < -512 || val > 511) -			return -EINVAL; -		break; -	case 12: -		if (val < -2048 || val > 2047) -			return -EINVAL; -		break; -	} - -	switch (high) { -	case 1: -		st->thresh_high[this_attr->address] = val; -		break; -	case 0: -		st->thresh_low[this_attr->address & 0x7] = val; -		break; -	} - -	return len; -} - -static ssize_t max1363_store_thresh_high_signed(struct device *dev, -						struct device_attribute *attr, -						const char *buf, -						size_t len) -{ -	return max1363_store_thresh_signed(dev, attr, buf, len, true); -} - -static ssize_t max1363_store_thresh_low_signed(struct device *dev, -						struct device_attribute *attr, -						const char *buf, -						size_t len) -{ -	return max1363_store_thresh_signed(dev, attr, buf, len, false); -} - -static IIO_DEVICE_ATTR(in0_thresh_high_value, S_IRUGO | S_IWUSR, -		max1363_show_thresh_high, -		max1363_store_thresh_high_unsigned, 0); -static IIO_DEVICE_ATTR(in0_thresh_low_value, S_IRUGO | S_IWUSR, -		max1363_show_thresh_low, -		max1363_store_thresh_low_unsigned, 0); -static IIO_DEVICE_ATTR(in1_thresh_high_value, S_IRUGO | S_IWUSR, -		max1363_show_thresh_high, -		max1363_store_thresh_high_unsigned, 1); -static IIO_DEVICE_ATTR(in1_thresh_low_value, S_IRUGO | S_IWUSR, -		max1363_show_thresh_low, -		max1363_store_thresh_low_unsigned, 1); -static IIO_DEVICE_ATTR(in2_thresh_high_value, S_IRUGO | S_IWUSR, -		max1363_show_thresh_high, -		max1363_store_thresh_high_unsigned, 2); -static IIO_DEVICE_ATTR(in2_thresh_low_value, S_IRUGO | S_IWUSR, -		max1363_show_thresh_low, -		max1363_store_thresh_low_unsigned, 2); -static IIO_DEVICE_ATTR(in3_thresh_high_value, S_IRUGO | S_IWUSR, -		max1363_show_thresh_high, -		max1363_store_thresh_high_unsigned, 3); -static IIO_DEVICE_ATTR(in3_thresh_low_value, S_IRUGO | S_IWUSR, -		max1363_show_thresh_low, -		max1363_store_thresh_low_unsigned, 3); - -static IIO_DEVICE_ATTR_NAMED(in0min1_thresh_high_value, -			in0-in1_thresh_high_value, -			S_IRUGO | S_IWUSR, max1363_show_thresh_high, -			max1363_store_thresh_high_signed, 4); -static IIO_DEVICE_ATTR_NAMED(in0min1_thresh_low_value, -			in0-in1_thresh_low_value, -			S_IRUGO | S_IWUSR, max1363_show_thresh_low, -			max1363_store_thresh_low_signed, 4); -static IIO_DEVICE_ATTR_NAMED(in2min3_thresh_high_value, -			in2-in3_thresh_high_value, -			S_IRUGO | S_IWUSR, max1363_show_thresh_high, -			max1363_store_thresh_high_signed, 5); -static IIO_DEVICE_ATTR_NAMED(in2min3_thresh_low_value, -			in2-in3_thresh_low_value, -			S_IRUGO | S_IWUSR, max1363_show_thresh_low, -			max1363_store_thresh_low_signed, 5); -static IIO_DEVICE_ATTR_NAMED(in1min0_thresh_high_value, -			in1-in0_thresh_high_value, -			S_IRUGO | S_IWUSR, max1363_show_thresh_high, -			max1363_store_thresh_high_signed, 6); -static IIO_DEVICE_ATTR_NAMED(in1min0_thresh_low_value, -			in1-in0_thresh_low_value, -			S_IRUGO | S_IWUSR, max1363_show_thresh_low, -			max1363_store_thresh_low_signed, 6); -static IIO_DEVICE_ATTR_NAMED(in3min2_thresh_high_value, -			in3-in2_thresh_high_value, -			S_IRUGO | S_IWUSR, max1363_show_thresh_high, -			max1363_store_thresh_high_signed, 7); -static IIO_DEVICE_ATTR_NAMED(in3min2_thresh_low_value, -			in3-in2_thresh_low_value, -			S_IRUGO | S_IWUSR, max1363_show_thresh_low, -			max1363_store_thresh_low_signed, 7); - -static int max1363_int_th(struct iio_dev *dev_info, -			int index, -			s64 timestamp, -			int not_test) -{ -	struct max1363_state *st = dev_info->dev_data; - -	st->last_timestamp = timestamp; -	schedule_work(&st->thresh_work); -	return 0; -} - -static void max1363_thresh_handler_bh(struct work_struct *work_s) -{ -	struct max1363_state *st = container_of(work_s, struct max1363_state, -						thresh_work); -	u8 rx; -	u8 tx[2] = { st->setupbyte, -		     MAX1363_MON_INT_ENABLE | (st->monitor_speed << 1) | 0xF0 }; - -	i2c_master_recv(st->client, &rx, 1); -	if (rx & (1 << 0)) -		iio_push_event(st->indio_dev, 0, -			IIO_EVENT_CODE_IN_LOW_THRESH(3), -			st->last_timestamp); -	if (rx & (1 << 1)) -		iio_push_event(st->indio_dev, 0, -			IIO_EVENT_CODE_IN_HIGH_THRESH(3), -			st->last_timestamp); -	if (rx & (1 << 2)) -		iio_push_event(st->indio_dev, 0, -			IIO_EVENT_CODE_IN_LOW_THRESH(2), -			st->last_timestamp); -	if (rx & (1 << 3)) -		iio_push_event(st->indio_dev, 0, -			IIO_EVENT_CODE_IN_HIGH_THRESH(2), -			st->last_timestamp); -	if (rx & (1 << 4)) -		iio_push_event(st->indio_dev, 0, -			IIO_EVENT_CODE_IN_LOW_THRESH(1), -			st->last_timestamp); -	if (rx & (1 << 5)) -		iio_push_event(st->indio_dev, 0, -			IIO_EVENT_CODE_IN_HIGH_THRESH(1), -			st->last_timestamp); -	if (rx & (1 << 6)) -		iio_push_event(st->indio_dev, 0, -			IIO_EVENT_CODE_IN_LOW_THRESH(0), -			st->last_timestamp); -	if (rx & (1 << 7)) -		iio_push_event(st->indio_dev, 0, -			IIO_EVENT_CODE_IN_HIGH_THRESH(0), -			st->last_timestamp); -	enable_irq(st->client->irq); -	i2c_master_send(st->client, tx, 2); -} - -static ssize_t max1363_read_interrupt_config(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_event_attr *this_attr = to_iio_event_attr(attr); -	int val; - -	mutex_lock(&dev_info->mlock); -	if (this_attr->mask & 0x8) -		val = (1 << (this_attr->mask & 0x7)) & st->mask_low; -	else -		val = (1 << this_attr->mask) & st->mask_high; -	mutex_unlock(&dev_info->mlock); - -	return sprintf(buf, "%d\n", !!val); -} - -static int max1363_monitor_mode_update(struct max1363_state *st, int enabled) -{ -	u8 *tx_buf; -	int ret, i = 3, j; -	unsigned long numelements; -	int len; -	long modemask; - -	if (!enabled) { -		/* transition to ring capture is not currently supported */ -		st->setupbyte &= ~MAX1363_SETUP_MONITOR_SETUP; -		st->configbyte &= ~MAX1363_SCAN_MASK; -		st->monitor_on = false; -		return max1363_write_basic_config(st->client, -						st->setupbyte, -						st->configbyte); -	} - -	/* Ensure we are in the relevant mode */ -	st->setupbyte |= MAX1363_SETUP_MONITOR_SETUP; -	st->configbyte &= ~(MAX1363_CHANNEL_SEL_MASK -			    | MAX1363_SCAN_MASK -			| MAX1363_SE_DE_MASK); -	st->configbyte |= MAX1363_CONFIG_SCAN_MONITOR_MODE; -	if ((st->mask_low | st->mask_high) & 0x0F) { -		st->configbyte |= max1363_mode_table[s0to3].conf; -		modemask = max1363_mode_table[s0to3].modemask; -	} else if ((st->mask_low | st->mask_high) & 0x30) { -		st->configbyte |= max1363_mode_table[d0m1to2m3].conf; -		modemask = max1363_mode_table[d0m1to2m3].modemask; -	} else { -		st->configbyte |= max1363_mode_table[d1m0to3m2].conf; -		modemask = max1363_mode_table[d1m0to3m2].modemask; -	} -	numelements = hweight_long(modemask); -	len = 3 * numelements + 3; -	tx_buf = kmalloc(len, GFP_KERNEL); -	if (!tx_buf) { -		ret = -ENOMEM; -		goto error_ret; -	} -	tx_buf[0] = st->configbyte; -	tx_buf[1] = st->setupbyte; -	tx_buf[2] = (st->monitor_speed << 1); - -	/* -	 * So we need to do yet another bit of nefarious scan mode -	 * setup to match what we need. -	 */ -	for (j = 0; j < 8; j++) -		if (modemask & (1 << j)) { -			/* Establish the mode is in the scan */ -			if (st->mask_low & (1 << j)) { -				tx_buf[i] = (st->thresh_low[j] >> 4) & 0xFF; -				tx_buf[i + 1] = (st->thresh_low[j] << 4) & 0xF0; -			} else if (j < 4) { -				tx_buf[i] = 0; -				tx_buf[i + 1] = 0; -			} else { -				tx_buf[i] = 0x80; -				tx_buf[i + 1] = 0; -			} -			if (st->mask_high & (1 << j)) { -				tx_buf[i + 1] |= -					(st->thresh_high[j] >> 8) & 0x0F; -				tx_buf[i + 2] = st->thresh_high[j] & 0xFF; -			} else if (j < 4) { -				tx_buf[i + 1] |= 0x0F; -				tx_buf[i + 2] = 0xFF; -			} else { -				tx_buf[i + 1] |= 0x07; -				tx_buf[i + 2] = 0xFF; -			} -			i += 3; -		} - - -	ret = i2c_master_send(st->client, tx_buf, len); -	if (ret < 0) -		goto error_ret; -	if (ret != len) { -		ret = -EIO; -		goto error_ret; -	} - -	/* -	 * Now that we hopefully have sensible thresholds in place it is -	 * time to turn the interrupts on. -	 * It is unclear from the data sheet if this should be necessary -	 * (i.e. whether monitor mode setup is atomic) but it appears to -	 * be in practice. -	 */ -	tx_buf[0] = st->setupbyte; -	tx_buf[1] = MAX1363_MON_INT_ENABLE | (st->monitor_speed << 1) | 0xF0; -	ret = i2c_master_send(st->client, tx_buf, 2); -	if (ret < 0) -		goto error_ret; -	if (ret != 2) { -		ret = -EIO; -		goto error_ret; -	} -	ret = 0; -	st->monitor_on = true; -error_ret: - -	kfree(tx_buf); - -	return ret; -} - -/* - * To keep this managable we always use one of 3 scan modes. - * Scan 0...3, 0-1,2-3 and 1-0,3-2 - */ -static inline int __max1363_check_event_mask(int thismask, int checkmask) -{ -	int ret = 0; -	/* Is it unipolar */ -	if (thismask < 4) { -		if (checkmask & ~0x0F) { -			ret = -EBUSY; -			goto error_ret; -		} -	} else if (thismask < 6) { -		if (checkmask & ~0x30) { -			ret = -EBUSY; -			goto error_ret; -		} -	} else if (checkmask & ~0xC0) -		ret = -EBUSY; -error_ret: -	return ret; -} - -static ssize_t max1363_write_interrupt_config(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 = iio_dev_get_devdata(dev_info); -	struct iio_event_attr *this_attr = to_iio_event_attr(attr); -	unsigned long val; -	int ret; -	u16 unifiedmask; -	ret = strict_strtoul(buf, 10, &val); -	if (ret) -		return -EINVAL; -	mutex_lock(&st->indio_dev->mlock); -	unifiedmask = st->mask_low | st->mask_high; -	if (this_attr->mask & 0x08) { -		/* If we are disabling no need to test */ -		if (val == 0) -			st->mask_low &= ~(1 << (this_attr->mask & 0x7)); -		else { -			ret = __max1363_check_event_mask(this_attr->mask & 0x7, -							unifiedmask); -			if (ret) -				goto error_ret; -			st->mask_low |= (1 << (this_attr->mask & 0x7)); -		} -	} else { -		if (val == 0) -			st->mask_high &= ~(1 << (this_attr->mask)); -		else { -			ret = __max1363_check_event_mask(this_attr->mask, -							unifiedmask); -			if (ret) -				goto error_ret; -			st->mask_high |= (1 << this_attr->mask); -		} -	} -	if (st->monitor_on && !st->mask_high && !st->mask_low) -		iio_remove_event_from_list(this_attr->listel, -					&dev_info->interrupts[0]->ev_list); -	if (!st->monitor_on && val) -		iio_add_event_to_list(this_attr->listel, -				&dev_info->interrupts[0]->ev_list); - -	max1363_monitor_mode_update(st, !!(st->mask_high | st->mask_low)); -error_ret: -	mutex_unlock(&st->indio_dev->mlock); - -	return len; -} - -IIO_EVENT_SH(max1363_thresh, max1363_int_th); - -#define MAX1363_HIGH_THRESH(a) a -#define MAX1363_LOW_THRESH(a) (a | 0x8) - -IIO_EVENT_ATTR_SH(in0_thresh_high_en, -		iio_event_max1363_thresh, -		max1363_read_interrupt_config, -		max1363_write_interrupt_config, -		MAX1363_HIGH_THRESH(0)); - -IIO_EVENT_ATTR_SH(in0_thresh_low_en, -		iio_event_max1363_thresh, -		max1363_read_interrupt_config, -		max1363_write_interrupt_config, -		MAX1363_LOW_THRESH(0)); - -IIO_EVENT_ATTR_SH(in1_thresh_high_en, -		iio_event_max1363_thresh, -		max1363_read_interrupt_config, -		max1363_write_interrupt_config, -		MAX1363_HIGH_THRESH(1)); - -IIO_EVENT_ATTR_SH(in1_thresh_low_en, -		iio_event_max1363_thresh, -		max1363_read_interrupt_config, -		max1363_write_interrupt_config, -		MAX1363_LOW_THRESH(1)); - -IIO_EVENT_ATTR_SH(in2_thresh_high_en, -		iio_event_max1363_thresh, -		max1363_read_interrupt_config, -		max1363_write_interrupt_config, -		MAX1363_HIGH_THRESH(2)); - -IIO_EVENT_ATTR_SH(in2_thresh_low_en, -		iio_event_max1363_thresh, -		max1363_read_interrupt_config, -		max1363_write_interrupt_config, -		MAX1363_LOW_THRESH(2)); - -IIO_EVENT_ATTR_SH(in3_thresh_high_en, -		iio_event_max1363_thresh, -		max1363_read_interrupt_config, -		max1363_write_interrupt_config, -		MAX1363_HIGH_THRESH(3)); - -IIO_EVENT_ATTR_SH(in3_thresh_low_en, -		iio_event_max1363_thresh, -		max1363_read_interrupt_config, -		max1363_write_interrupt_config, -		MAX1363_LOW_THRESH(3)); - -IIO_EVENT_ATTR_NAMED_SH(in0min1_thresh_high_en, -			in0-in1_thresh_high_en, -			iio_event_max1363_thresh, -			max1363_read_interrupt_config, -			max1363_write_interrupt_config, -			MAX1363_HIGH_THRESH(4)); - -IIO_EVENT_ATTR_NAMED_SH(in0min1_thresh_low_en, -			in0-in1_thresh_low_en, -			iio_event_max1363_thresh, -			max1363_read_interrupt_config, -			max1363_write_interrupt_config, -			MAX1363_LOW_THRESH(4)); - -IIO_EVENT_ATTR_NAMED_SH(in3min2_thresh_high_en, -			in3-in2_thresh_high_en, -			iio_event_max1363_thresh, -			max1363_read_interrupt_config, -			max1363_write_interrupt_config, -			MAX1363_HIGH_THRESH(5)); - -IIO_EVENT_ATTR_NAMED_SH(in3min2_thresh_low_en, -			in3-in2_thresh_low_en, -			iio_event_max1363_thresh, -			max1363_read_interrupt_config, -			max1363_write_interrupt_config, -			MAX1363_LOW_THRESH(5)); - -IIO_EVENT_ATTR_NAMED_SH(in1min0_thresh_high_en, -			in1-in0_thresh_high_en, -			iio_event_max1363_thresh, -			max1363_read_interrupt_config, -			max1363_write_interrupt_config, -			MAX1363_HIGH_THRESH(6)); - -IIO_EVENT_ATTR_NAMED_SH(in1min0_thresh_low_en, -			in1-in0_thresh_low_en, -			iio_event_max1363_thresh, -			max1363_read_interrupt_config, -			max1363_write_interrupt_config, -			MAX1363_LOW_THRESH(6)); - -IIO_EVENT_ATTR_NAMED_SH(in2min3_thresh_high_en, -			in2-in3_thresh_high_en, -			iio_event_max1363_thresh, -			max1363_read_interrupt_config, -			max1363_write_interrupt_config, -			MAX1363_HIGH_THRESH(7)); - -IIO_EVENT_ATTR_NAMED_SH(in2min3_thresh_low_en, -			in2-in3_thresh_low_en, -			iio_event_max1363_thresh, -			max1363_read_interrupt_config, -			max1363_write_interrupt_config, -			MAX1363_LOW_THRESH(7)); - -/* - * As with scan_elements, only certain sets of these can - * be combined. - */ -static struct attribute *max1363_event_attributes[] = { -	&iio_dev_attr_in0_thresh_high_value.dev_attr.attr, -	&iio_dev_attr_in0_thresh_low_value.dev_attr.attr, -	&iio_dev_attr_in1_thresh_high_value.dev_attr.attr, -	&iio_dev_attr_in1_thresh_low_value.dev_attr.attr, -	&iio_dev_attr_in2_thresh_high_value.dev_attr.attr, -	&iio_dev_attr_in2_thresh_low_value.dev_attr.attr, -	&iio_dev_attr_in3_thresh_high_value.dev_attr.attr, -	&iio_dev_attr_in3_thresh_low_value.dev_attr.attr, -	&iio_dev_attr_in0min1_thresh_high_value.dev_attr.attr, -	&iio_dev_attr_in0min1_thresh_low_value.dev_attr.attr, -	&iio_dev_attr_in2min3_thresh_high_value.dev_attr.attr, -	&iio_dev_attr_in2min3_thresh_low_value.dev_attr.attr, -	&iio_dev_attr_in1min0_thresh_high_value.dev_attr.attr, -	&iio_dev_attr_in1min0_thresh_low_value.dev_attr.attr, -	&iio_dev_attr_in3min2_thresh_high_value.dev_attr.attr, -	&iio_dev_attr_in3min2_thresh_low_value.dev_attr.attr, -	&iio_dev_attr_sampling_frequency.dev_attr.attr, -	&iio_const_attr_sampling_frequency_available.dev_attr.attr, -	&iio_event_attr_in0_thresh_high_en.dev_attr.attr, -	&iio_event_attr_in0_thresh_low_en.dev_attr.attr, -	&iio_event_attr_in1_thresh_high_en.dev_attr.attr, -	&iio_event_attr_in1_thresh_low_en.dev_attr.attr, -	&iio_event_attr_in2_thresh_high_en.dev_attr.attr, -	&iio_event_attr_in2_thresh_low_en.dev_attr.attr, -	&iio_event_attr_in3_thresh_high_en.dev_attr.attr, -	&iio_event_attr_in3_thresh_low_en.dev_attr.attr, -	&iio_event_attr_in0min1_thresh_high_en.dev_attr.attr, -	&iio_event_attr_in0min1_thresh_low_en.dev_attr.attr, -	&iio_event_attr_in3min2_thresh_high_en.dev_attr.attr, -	&iio_event_attr_in3min2_thresh_low_en.dev_attr.attr, -	&iio_event_attr_in1min0_thresh_high_en.dev_attr.attr, -	&iio_event_attr_in1min0_thresh_low_en.dev_attr.attr, -	&iio_event_attr_in2min3_thresh_high_en.dev_attr.attr, -	&iio_event_attr_in2min3_thresh_low_en.dev_attr.attr, -	NULL, -}; - -static struct attribute_group max1363_event_attribute_group = { -	.attrs = max1363_event_attributes, -}; - -static int max1363_initial_setup(struct max1363_state *st) -{ -	st->setupbyte = MAX1363_SETUP_AIN3_IS_AIN3_REF_IS_VDD -		| MAX1363_SETUP_POWER_UP_INT_REF -		| MAX1363_SETUP_INT_CLOCK -		| MAX1363_SETUP_UNIPOLAR -		| MAX1363_SETUP_NORESET; - -	/* Set scan mode writes the config anyway so wait until then*/ -	st->setupbyte = MAX1363_SETUP_BYTE(st->setupbyte); -	st->current_mode = &max1363_mode_table[st->chip_info->default_mode]; -	st->configbyte = MAX1363_CONFIG_BYTE(st->configbyte); - -	return max1363_set_scan_mode(st); -} - -static int __devinit max1363_probe(struct i2c_client *client, -				   const struct i2c_device_id *id) -{ -	int ret, i, regdone = 0; -	struct max1363_state *st = kzalloc(sizeof(*st), GFP_KERNEL); -	if (st == NULL) { -		ret = -ENOMEM; -		goto error_ret; -	} - -	/* this is only used for device removal purposes */ -	i2c_set_clientdata(client, st); - -	atomic_set(&st->protect_ring, 0); - -	st->chip_info = &max1363_chip_info_tbl[id->driver_data]; -	st->reg = regulator_get(&client->dev, "vcc"); -	if (!IS_ERR(st->reg)) { -		ret = regulator_enable(st->reg); -		if (ret) -			goto error_put_reg; -	} -	st->client = client; - -	st->indio_dev = iio_allocate_device(); -	if (st->indio_dev == NULL) { -		ret = -ENOMEM; -		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 = st->chip_info->dev_attrs; - -	/* Todo: this shouldn't be here. */ -	st->indio_dev->dev_data = (void *)(st); -	st->indio_dev->driver_module = THIS_MODULE; -	st->indio_dev->modes = INDIO_DIRECT_MODE; -	if (st->chip_info->monitor_mode && client->irq) { -		st->indio_dev->num_interrupt_lines = 1; -		st->indio_dev->event_attrs -			= &max1363_event_attribute_group; -	} - -	ret = max1363_initial_setup(st); -	if (ret) -		goto error_free_available_scan_masks; - -	ret = max1363_register_ring_funcs_and_init(st->indio_dev); -	if (ret) -		goto error_free_available_scan_masks; - -	ret = iio_device_register(st->indio_dev); -	if (ret) -		goto error_cleanup_ring; -	regdone = 1; -	ret = iio_ring_buffer_register(st->indio_dev->ring, 0); -	if (ret) -		goto error_cleanup_ring; - -	if (st->chip_info->monitor_mode && client->irq) { -		ret = iio_register_interrupt_line(client->irq, -						st->indio_dev, -						0, -						IRQF_TRIGGER_RISING, -						client->name); -		if (ret) -			goto error_uninit_ring; - -		INIT_WORK(&st->thresh_work, max1363_thresh_handler_bh); -	} - -	return 0; -error_uninit_ring: -	iio_ring_buffer_unregister(st->indio_dev->ring); -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); -	else -		iio_device_unregister(st->indio_dev); -error_disable_reg: -	if (!IS_ERR(st->reg)) -		regulator_disable(st->reg); -error_put_reg: -	if (!IS_ERR(st->reg)) -		regulator_put(st->reg); -	kfree(st); - -error_ret: -	return ret; -} - -static int max1363_remove(struct i2c_client *client) -{ -	struct max1363_state *st = i2c_get_clientdata(client); -	struct iio_dev *indio_dev = st->indio_dev; - -	if (st->chip_info->monitor_mode && client->irq) -		iio_unregister_interrupt_line(st->indio_dev, 0); -	iio_ring_buffer_unregister(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); -	} -	kfree(st); - -	return 0; -} - -static const struct i2c_device_id max1363_id[] = { -	{ "max1361", max1361 }, -	{ "max1362", max1362 }, -	{ "max1363", max1363 }, -	{ "max1364", max1364 }, -	{ "max1036", max1036 }, -	{ "max1037", max1037 }, -	{ "max1038", max1038 }, -	{ "max1039", max1039 }, -	{ "max1136", max1136 }, -	{ "max1137", max1137 }, -	{ "max1138", max1138 }, -	{ "max1139", max1139 }, -	{ "max1236", max1236 }, -	{ "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 }, -	{} -}; - -MODULE_DEVICE_TABLE(i2c, max1363_id); - -static struct i2c_driver max1363_driver = { -	.driver = { -		.name = "max1363", -	}, -	.probe = max1363_probe, -	.remove = max1363_remove, -	.id_table = max1363_id, -}; - -static __init int max1363_init(void) -{ -	return i2c_add_driver(&max1363_driver); -} - -static __exit void max1363_exit(void) -{ -	i2c_del_driver(&max1363_driver); -} - -MODULE_AUTHOR("Jonathan Cameron <jic23@cam.ac.uk>"); -MODULE_DESCRIPTION("Maxim 1363 ADC"); -MODULE_LICENSE("GPL v2"); - -module_init(max1363_init); -module_exit(max1363_exit); diff --git a/drivers/staging/iio/adc/max1363_ring.c b/drivers/staging/iio/adc/max1363_ring.c deleted file mode 100644 index 5532f3e466b..00000000000 --- a/drivers/staging/iio/adc/max1363_ring.c +++ /dev/null @@ -1,226 +0,0 @@ -/* - * Copyright (C) 2008 Jonathan Cameron - * - * 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_ring.c - */ - -#include <linux/interrupt.h> -#include <linux/gpio.h> -#include <linux/workqueue.h> -#include <linux/device.h> -#include <linux/slab.h> -#include <linux/kernel.h> -#include <linux/sysfs.h> -#include <linux/list.h> -#include <linux/i2c.h> -#include <linux/bitops.h> - -#include "../iio.h" -#include "../ring_generic.h" -#include "../ring_sw.h" -#include "../trigger.h" -#include "../sysfs.h" - -#include "max1363.h" - -/* Todo: test this */ -int max1363_single_channel_from_ring(long mask, struct max1363_state *st) -{ -	struct iio_ring_buffer *ring = st->indio_dev->ring; -	int count = 0, ret; -	u8 *ring_data; -	if (!(st->current_mode->modemask & mask)) { -		ret = -EBUSY; -		goto error_ret; -	} - -	ring_data = kmalloc(ring->access.get_bytes_per_datum(ring), GFP_KERNEL); -	if (ring_data == NULL) { -		ret = -ENOMEM; -		goto error_ret; -	} -	ret = ring->access.read_last(ring, ring_data); -	if (ret) -		goto error_free_ring_data; -	/* 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); -error_ret: -	return ret; -} - -/** - * max1363_ring_preenable() - setup the parameters of the ring before enabling - * - * The complex nature of the setting of the nuber of bytes per datum is due - * to this driver currently ensuring that the timestamp is stored at an 8 - * byte boundary. - **/ -static int max1363_ring_preenable(struct iio_dev *indio_dev) -{ -	struct max1363_state *st = indio_dev->dev_data; -	struct iio_ring_buffer *ring = indio_dev->ring; -	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(ring->scan_mask, -					st->chip_info); -	if (!st->current_mode) -		return -EINVAL; - -	max1363_set_scan_mode(st); - -	numvals = hweight_long(st->current_mode->modemask); -	if (ring->access.set_bytes_per_datum) { -		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); -		ring->access.set_bytes_per_datum(ring, d_size); -	} - -	return 0; -} - - -/** - * max1363_poll_func_th() - th of trigger launched polling to ring buffer - * - * As sampling only occurs on i2c comms occuring, leave timestamping until - * then.  Some triggers will generate their own time stamp.  Currently - * there is no way of notifying them when no one cares. - **/ -static void max1363_poll_func_th(struct iio_dev *indio_dev, s64 time) -{ -	struct max1363_state *st = indio_dev->dev_data; - -	schedule_work(&st->poll_work); - -	return; -} -/** - * max1363_poll_bh_to_ring() - bh of trigger launched polling to ring buffer - * @work_s:	the work struct through which this was scheduled - * - * Currently there is no option in this driver to disable the saving of - * timestamps within the ring. - * I think the one copy of this at a time was to avoid problems if the - * trigger was set far too high and the reads then locked up the computer. - **/ -static void max1363_poll_bh_to_ring(struct work_struct *work_s) -{ -	struct max1363_state *st = container_of(work_s, struct max1363_state, -						  poll_work); -	struct iio_dev *indio_dev = st->indio_dev; -	struct iio_sw_ring_buffer *sw_ring = iio_to_sw_ring(indio_dev->ring); -	s64 time_ns; -	__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 */ -	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)); - -	/* Ensure only one copy of this function running at a time */ -	if (atomic_inc_return(&st->protect_ring) > 1) -		return; - -	/* Monitor mode prevents reading. Whilst not currently implemented -	 * might as well have this test in here in the meantime as it does -	 * no harm. -	 */ -	if (numvals == 0) -		return; - -	rxbuf = kmalloc(d_size,	GFP_KERNEL); -	if (rxbuf == NULL) -		return; -	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; - -	time_ns = iio_get_time_ns(); - -	memcpy(rxbuf + d_size - sizeof(s64), &time_ns, sizeof(time_ns)); - -	indio_dev->ring->access.store_to(&sw_ring->buf, rxbuf, time_ns); -done: -	kfree(rxbuf); -	atomic_dec(&st->protect_ring); -} - - -int max1363_register_ring_funcs_and_init(struct iio_dev *indio_dev) -{ -	struct max1363_state *st = indio_dev->dev_data; -	int ret = 0; - -	indio_dev->ring = iio_sw_rb_allocate(indio_dev); -	if (!indio_dev->ring) { -		ret = -ENOMEM; -		goto error_ret; -	} -	/* Effectively select the ring buffer implementation */ -	iio_ring_sw_register_funcs(&indio_dev->ring->access); -	ret = iio_alloc_pollfunc(indio_dev, NULL, &max1363_poll_func_th); -	if (ret) -		goto error_deallocate_sw_rb; - -	/* Ring buffer functions - here trigger setup related */ -	indio_dev->ring->scan_el_attrs = st->chip_info->scan_attrs; -	indio_dev->ring->postenable = &iio_triggered_ring_postenable; -	indio_dev->ring->preenable = &max1363_ring_preenable; -	indio_dev->ring->predisable = &iio_triggered_ring_predisable; -	INIT_WORK(&st->poll_work, &max1363_poll_bh_to_ring); - -	/* Flag that polled ring buffering is possible */ -	indio_dev->modes |= INDIO_RING_TRIGGERED; -	return 0; -error_deallocate_sw_rb: -	iio_sw_rb_free(indio_dev->ring); -error_ret: -	return ret; -} - -void max1363_ring_cleanup(struct iio_dev *indio_dev) -{ -	/* ensure that the trigger has been detached */ -	if (indio_dev->trig) { -		iio_put_trigger(indio_dev->trig); -		iio_trigger_dettach_poll_func(indio_dev->trig, -					      indio_dev->pollfunc); -	} -	kfree(indio_dev->pollfunc); -	iio_sw_rb_free(indio_dev->ring); -} diff --git a/drivers/staging/iio/adc/mxs-lradc.c b/drivers/staging/iio/adc/mxs-lradc.c new file mode 100644 index 00000000000..52d7517b342 --- /dev/null +++ b/drivers/staging/iio/adc/mxs-lradc.c @@ -0,0 +1,1685 @@ +/* + * Freescale i.MX28 LRADC driver + * + * Copyright (c) 2012 DENX Software Engineering, GmbH. + * Marek Vasut <marex@denx.de> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/err.h> +#include <linux/interrupt.h> +#include <linux/device.h> +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/sysfs.h> +#include <linux/list.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/spinlock.h> +#include <linux/wait.h> +#include <linux/sched.h> +#include <linux/stmp_device.h> +#include <linux/bitops.h> +#include <linux/completion.h> +#include <linux/delay.h> +#include <linux/input.h> +#include <linux/clk.h> + +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> +#include <linux/iio/buffer.h> +#include <linux/iio/trigger.h> +#include <linux/iio/trigger_consumer.h> +#include <linux/iio/triggered_buffer.h> + +#define DRIVER_NAME		"mxs-lradc" + +#define LRADC_MAX_DELAY_CHANS	4 +#define LRADC_MAX_MAPPED_CHANS	8 +#define LRADC_MAX_TOTAL_CHANS	16 + +#define LRADC_DELAY_TIMER_HZ	2000 + +/* + * Make this runtime configurable if necessary. Currently, if the buffered mode + * is enabled, the LRADC takes LRADC_DELAY_TIMER_LOOP samples of data before + * triggering IRQ. The sampling happens every (LRADC_DELAY_TIMER_PER / 2000) + * seconds. The result is that the samples arrive every 500mS. + */ +#define LRADC_DELAY_TIMER_PER	200 +#define LRADC_DELAY_TIMER_LOOP	5 + +/* + * Once the pen touches the touchscreen, the touchscreen switches from + * IRQ-driven mode to polling mode to prevent interrupt storm. The polling + * is realized by worker thread, which is called every 20 or so milliseconds. + * This gives the touchscreen enough fluence and does not strain the system + * too much. + */ +#define LRADC_TS_SAMPLE_DELAY_MS	5 + +/* + * The LRADC reads the following amount of samples from each touchscreen + * channel and the driver then computes avarage of these. + */ +#define LRADC_TS_SAMPLE_AMOUNT		4 + +enum mxs_lradc_id { +	IMX23_LRADC, +	IMX28_LRADC, +}; + +static const char * const mx23_lradc_irq_names[] = { +	"mxs-lradc-touchscreen", +	"mxs-lradc-channel0", +	"mxs-lradc-channel1", +	"mxs-lradc-channel2", +	"mxs-lradc-channel3", +	"mxs-lradc-channel4", +	"mxs-lradc-channel5", +	"mxs-lradc-channel6", +	"mxs-lradc-channel7", +}; + +static const char * const mx28_lradc_irq_names[] = { +	"mxs-lradc-touchscreen", +	"mxs-lradc-thresh0", +	"mxs-lradc-thresh1", +	"mxs-lradc-channel0", +	"mxs-lradc-channel1", +	"mxs-lradc-channel2", +	"mxs-lradc-channel3", +	"mxs-lradc-channel4", +	"mxs-lradc-channel5", +	"mxs-lradc-channel6", +	"mxs-lradc-channel7", +	"mxs-lradc-button0", +	"mxs-lradc-button1", +}; + +struct mxs_lradc_of_config { +	const int		irq_count; +	const char * const	*irq_name; +	const uint32_t		*vref_mv; +}; + +#define VREF_MV_BASE 1850 + +static const uint32_t mx23_vref_mv[LRADC_MAX_TOTAL_CHANS] = { +	VREF_MV_BASE,		/* CH0 */ +	VREF_MV_BASE,		/* CH1 */ +	VREF_MV_BASE,		/* CH2 */ +	VREF_MV_BASE,		/* CH3 */ +	VREF_MV_BASE,		/* CH4 */ +	VREF_MV_BASE,		/* CH5 */ +	VREF_MV_BASE * 2,	/* CH6 VDDIO */ +	VREF_MV_BASE * 4,	/* CH7 VBATT */ +	VREF_MV_BASE,		/* CH8 Temp sense 0 */ +	VREF_MV_BASE,		/* CH9 Temp sense 1 */ +	VREF_MV_BASE,		/* CH10 */ +	VREF_MV_BASE,		/* CH11 */ +	VREF_MV_BASE,		/* CH12 USB_DP */ +	VREF_MV_BASE,		/* CH13 USB_DN */ +	VREF_MV_BASE,		/* CH14 VBG */ +	VREF_MV_BASE * 4,	/* CH15 VDD5V */ +}; + +static const uint32_t mx28_vref_mv[LRADC_MAX_TOTAL_CHANS] = { +	VREF_MV_BASE,		/* CH0 */ +	VREF_MV_BASE,		/* CH1 */ +	VREF_MV_BASE,		/* CH2 */ +	VREF_MV_BASE,		/* CH3 */ +	VREF_MV_BASE,		/* CH4 */ +	VREF_MV_BASE,		/* CH5 */ +	VREF_MV_BASE,		/* CH6 */ +	VREF_MV_BASE * 4,	/* CH7 VBATT */ +	VREF_MV_BASE,		/* CH8 Temp sense 0 */ +	VREF_MV_BASE,		/* CH9 Temp sense 1 */ +	VREF_MV_BASE * 2,	/* CH10 VDDIO */ +	VREF_MV_BASE,		/* CH11 VTH */ +	VREF_MV_BASE * 2,	/* CH12 VDDA */ +	VREF_MV_BASE,		/* CH13 VDDD */ +	VREF_MV_BASE,		/* CH14 VBG */ +	VREF_MV_BASE * 4,	/* CH15 VDD5V */ +}; + +static const struct mxs_lradc_of_config mxs_lradc_of_config[] = { +	[IMX23_LRADC] = { +		.irq_count	= ARRAY_SIZE(mx23_lradc_irq_names), +		.irq_name	= mx23_lradc_irq_names, +		.vref_mv	= mx23_vref_mv, +	}, +	[IMX28_LRADC] = { +		.irq_count	= ARRAY_SIZE(mx28_lradc_irq_names), +		.irq_name	= mx28_lradc_irq_names, +		.vref_mv	= mx28_vref_mv, +	}, +}; + +enum mxs_lradc_ts { +	MXS_LRADC_TOUCHSCREEN_NONE = 0, +	MXS_LRADC_TOUCHSCREEN_4WIRE, +	MXS_LRADC_TOUCHSCREEN_5WIRE, +}; + +/* + * Touchscreen handling + */ +enum lradc_ts_plate { +	LRADC_TOUCH = 0, +	LRADC_SAMPLE_X, +	LRADC_SAMPLE_Y, +	LRADC_SAMPLE_PRESSURE, +	LRADC_SAMPLE_VALID, +}; + +enum mxs_lradc_divbytwo { +	MXS_LRADC_DIV_DISABLED = 0, +	MXS_LRADC_DIV_ENABLED, +}; + +struct mxs_lradc_scale { +	unsigned int		integer; +	unsigned int		nano; +}; + +struct mxs_lradc { +	struct device		*dev; +	void __iomem		*base; +	int			irq[13]; + +	struct clk		*clk; + +	uint32_t		*buffer; +	struct iio_trigger	*trig; + +	struct mutex		lock; + +	struct completion	completion; + +	const uint32_t		*vref_mv; +	struct mxs_lradc_scale	scale_avail[LRADC_MAX_TOTAL_CHANS][2]; +	unsigned long		is_divided; + +	/* +	 * Touchscreen LRADC channels receives a private slot in the CTRL4 +	 * register, the slot #7. Therefore only 7 slots instead of 8 in the +	 * CTRL4 register can be mapped to LRADC channels when using the +	 * touchscreen. +	 * +	 * Furthermore, certain LRADC channels are shared between touchscreen +	 * and/or touch-buttons and generic LRADC block. Therefore when using +	 * either of these, these channels are not available for the regular +	 * sampling. The shared channels are as follows: +	 * +	 * CH0 -- Touch button #0 +	 * CH1 -- Touch button #1 +	 * CH2 -- Touch screen XPUL +	 * CH3 -- Touch screen YPLL +	 * CH4 -- Touch screen XNUL +	 * CH5 -- Touch screen YNLR +	 * CH6 -- Touch screen WIPER (5-wire only) +	 * +	 * The bitfields below represents which parts of the LRADC block are +	 * switched into special mode of operation. These channels can not +	 * be sampled as regular LRADC channels. The driver will refuse any +	 * attempt to sample these channels. +	 */ +#define CHAN_MASK_TOUCHBUTTON		(0x3 << 0) +#define CHAN_MASK_TOUCHSCREEN_4WIRE	(0xf << 2) +#define CHAN_MASK_TOUCHSCREEN_5WIRE	(0x1f << 2) +	enum mxs_lradc_ts	use_touchscreen; +	bool			use_touchbutton; + +	struct input_dev	*ts_input; + +	enum mxs_lradc_id	soc; +	enum lradc_ts_plate	cur_plate; /* statemachine */ +	bool			ts_valid; +	unsigned		ts_x_pos; +	unsigned		ts_y_pos; +	unsigned		ts_pressure; + +	/* handle touchscreen's physical behaviour */ +	/* samples per coordinate */ +	unsigned		over_sample_cnt; +	/* time clocks between samples */ +	unsigned		over_sample_delay; +	/* time in clocks to wait after the plates where switched */ +	unsigned		settling_delay; +}; + +#define	LRADC_CTRL0				0x00 +# define LRADC_CTRL0_MX28_TOUCH_DETECT_ENABLE	(1 << 23) +# define LRADC_CTRL0_MX28_TOUCH_SCREEN_TYPE	(1 << 22) +# define LRADC_CTRL0_MX28_YNNSW	/* YM */	(1 << 21) +# define LRADC_CTRL0_MX28_YPNSW	/* YP */	(1 << 20) +# define LRADC_CTRL0_MX28_YPPSW	/* YP */	(1 << 19) +# define LRADC_CTRL0_MX28_XNNSW	/* XM */	(1 << 18) +# define LRADC_CTRL0_MX28_XNPSW	/* XM */	(1 << 17) +# define LRADC_CTRL0_MX28_XPPSW	/* XP */	(1 << 16) + +# define LRADC_CTRL0_MX23_TOUCH_DETECT_ENABLE	(1 << 20) +# define LRADC_CTRL0_MX23_YM			(1 << 19) +# define LRADC_CTRL0_MX23_XM			(1 << 18) +# define LRADC_CTRL0_MX23_YP			(1 << 17) +# define LRADC_CTRL0_MX23_XP			(1 << 16) + +# define LRADC_CTRL0_MX28_PLATE_MASK \ +		(LRADC_CTRL0_MX28_TOUCH_DETECT_ENABLE | \ +		LRADC_CTRL0_MX28_YNNSW | LRADC_CTRL0_MX28_YPNSW | \ +		LRADC_CTRL0_MX28_YPPSW | LRADC_CTRL0_MX28_XNNSW | \ +		LRADC_CTRL0_MX28_XNPSW | LRADC_CTRL0_MX28_XPPSW) + +# define LRADC_CTRL0_MX23_PLATE_MASK \ +		(LRADC_CTRL0_MX23_TOUCH_DETECT_ENABLE | \ +		LRADC_CTRL0_MX23_YM | LRADC_CTRL0_MX23_XM | \ +		LRADC_CTRL0_MX23_YP | LRADC_CTRL0_MX23_XP) + +#define	LRADC_CTRL1				0x10 +#define	LRADC_CTRL1_TOUCH_DETECT_IRQ_EN		(1 << 24) +#define	LRADC_CTRL1_LRADC_IRQ_EN(n)		(1 << ((n) + 16)) +#define	LRADC_CTRL1_MX28_LRADC_IRQ_EN_MASK	(0x1fff << 16) +#define	LRADC_CTRL1_MX23_LRADC_IRQ_EN_MASK	(0x01ff << 16) +#define	LRADC_CTRL1_LRADC_IRQ_EN_OFFSET		16 +#define	LRADC_CTRL1_TOUCH_DETECT_IRQ		(1 << 8) +#define	LRADC_CTRL1_LRADC_IRQ(n)		(1 << (n)) +#define	LRADC_CTRL1_MX28_LRADC_IRQ_MASK		0x1fff +#define	LRADC_CTRL1_MX23_LRADC_IRQ_MASK		0x01ff +#define	LRADC_CTRL1_LRADC_IRQ_OFFSET		0 + +#define	LRADC_CTRL2				0x20 +#define	LRADC_CTRL2_DIVIDE_BY_TWO_OFFSET	24 +#define	LRADC_CTRL2_TEMPSENSE_PWD		(1 << 15) + +#define	LRADC_STATUS				0x40 +#define	LRADC_STATUS_TOUCH_DETECT_RAW		(1 << 0) + +#define	LRADC_CH(n)				(0x50 + (0x10 * (n))) +#define	LRADC_CH_ACCUMULATE			(1 << 29) +#define	LRADC_CH_NUM_SAMPLES_MASK		(0x1f << 24) +#define	LRADC_CH_NUM_SAMPLES_OFFSET		24 +#define	LRADC_CH_NUM_SAMPLES(x) \ +				((x) << LRADC_CH_NUM_SAMPLES_OFFSET) +#define	LRADC_CH_VALUE_MASK			0x3ffff +#define	LRADC_CH_VALUE_OFFSET			0 + +#define	LRADC_DELAY(n)				(0xd0 + (0x10 * (n))) +#define	LRADC_DELAY_TRIGGER_LRADCS_MASK		(0xff << 24) +#define	LRADC_DELAY_TRIGGER_LRADCS_OFFSET	24 +#define	LRADC_DELAY_TRIGGER(x) \ +				(((x) << LRADC_DELAY_TRIGGER_LRADCS_OFFSET) & \ +				LRADC_DELAY_TRIGGER_LRADCS_MASK) +#define	LRADC_DELAY_KICK			(1 << 20) +#define	LRADC_DELAY_TRIGGER_DELAYS_MASK		(0xf << 16) +#define	LRADC_DELAY_TRIGGER_DELAYS_OFFSET	16 +#define	LRADC_DELAY_TRIGGER_DELAYS(x) \ +				(((x) << LRADC_DELAY_TRIGGER_DELAYS_OFFSET) & \ +				LRADC_DELAY_TRIGGER_DELAYS_MASK) +#define	LRADC_DELAY_LOOP_COUNT_MASK		(0x1f << 11) +#define	LRADC_DELAY_LOOP_COUNT_OFFSET		11 +#define	LRADC_DELAY_LOOP(x) \ +				(((x) << LRADC_DELAY_LOOP_COUNT_OFFSET) & \ +				LRADC_DELAY_LOOP_COUNT_MASK) +#define	LRADC_DELAY_DELAY_MASK			0x7ff +#define	LRADC_DELAY_DELAY_OFFSET		0 +#define	LRADC_DELAY_DELAY(x) \ +				(((x) << LRADC_DELAY_DELAY_OFFSET) & \ +				LRADC_DELAY_DELAY_MASK) + +#define	LRADC_CTRL4				0x140 +#define	LRADC_CTRL4_LRADCSELECT_MASK(n)		(0xf << ((n) * 4)) +#define	LRADC_CTRL4_LRADCSELECT_OFFSET(n)	((n) * 4) + +#define LRADC_RESOLUTION			12 +#define LRADC_SINGLE_SAMPLE_MASK		((1 << LRADC_RESOLUTION) - 1) + +static void mxs_lradc_reg_set(struct mxs_lradc *lradc, u32 val, u32 reg) +{ +	writel(val, lradc->base + reg + STMP_OFFSET_REG_SET); +} + +static void mxs_lradc_reg_clear(struct mxs_lradc *lradc, u32 val, u32 reg) +{ +	writel(val, lradc->base + reg + STMP_OFFSET_REG_CLR); +} + +static void mxs_lradc_reg_wrt(struct mxs_lradc *lradc, u32 val, u32 reg) +{ +	writel(val, lradc->base + reg); +} + +static u32 mxs_lradc_plate_mask(struct mxs_lradc *lradc) +{ +	if (lradc->soc == IMX23_LRADC) +		return LRADC_CTRL0_MX23_PLATE_MASK; +	else +		return LRADC_CTRL0_MX28_PLATE_MASK; +} + +static u32 mxs_lradc_irq_en_mask(struct mxs_lradc *lradc) +{ +	if (lradc->soc == IMX23_LRADC) +		return LRADC_CTRL1_MX23_LRADC_IRQ_EN_MASK; +	else +		return LRADC_CTRL1_MX28_LRADC_IRQ_EN_MASK; +} + +static u32 mxs_lradc_irq_mask(struct mxs_lradc *lradc) +{ +	if (lradc->soc == IMX23_LRADC) +		return LRADC_CTRL1_MX23_LRADC_IRQ_MASK; +	else +		return LRADC_CTRL1_MX28_LRADC_IRQ_MASK; +} + +static u32 mxs_lradc_touch_detect_bit(struct mxs_lradc *lradc) +{ +	if (lradc->soc == IMX23_LRADC) +		return LRADC_CTRL0_MX23_TOUCH_DETECT_ENABLE; +	else +		return LRADC_CTRL0_MX28_TOUCH_DETECT_ENABLE; +} + +static u32 mxs_lradc_drive_x_plate(struct mxs_lradc *lradc) +{ +	if (lradc->soc == IMX23_LRADC) +		return LRADC_CTRL0_MX23_XP | LRADC_CTRL0_MX23_XM; +	else +		return LRADC_CTRL0_MX28_XPPSW | LRADC_CTRL0_MX28_XNNSW; +} + +static u32 mxs_lradc_drive_y_plate(struct mxs_lradc *lradc) +{ +	if (lradc->soc == IMX23_LRADC) +		return LRADC_CTRL0_MX23_YP | LRADC_CTRL0_MX23_YM; +	else +		return LRADC_CTRL0_MX28_YPPSW | LRADC_CTRL0_MX28_YNNSW; +} + +static u32 mxs_lradc_drive_pressure(struct mxs_lradc *lradc) +{ +	if (lradc->soc == IMX23_LRADC) +		return LRADC_CTRL0_MX23_YP | LRADC_CTRL0_MX23_XM; +	else +		return LRADC_CTRL0_MX28_YPPSW | LRADC_CTRL0_MX28_XNNSW; +} + +static bool mxs_lradc_check_touch_event(struct mxs_lradc *lradc) +{ +	return !!(readl(lradc->base + LRADC_STATUS) & +					LRADC_STATUS_TOUCH_DETECT_RAW); +} + +static void mxs_lradc_setup_ts_channel(struct mxs_lradc *lradc, unsigned ch) +{ +	/* +	 * prepare for oversampling conversion +	 * +	 * from the datasheet: +	 * "The ACCUMULATE bit in the appropriate channel register +	 * HW_LRADC_CHn must be set to 1 if NUM_SAMPLES is greater then 0; +	 * otherwise, the IRQs will not fire." +	 */ +	mxs_lradc_reg_wrt(lradc, LRADC_CH_ACCUMULATE | +			LRADC_CH_NUM_SAMPLES(lradc->over_sample_cnt - 1), +			LRADC_CH(ch)); + +	/* from the datasheet: +	 * "Software must clear this register in preparation for a +	 * multi-cycle accumulation. +	 */ +	mxs_lradc_reg_clear(lradc, LRADC_CH_VALUE_MASK, LRADC_CH(ch)); + +	/* prepare the delay/loop unit according to the oversampling count */ +	mxs_lradc_reg_wrt(lradc, LRADC_DELAY_TRIGGER(1 << ch) | +		LRADC_DELAY_TRIGGER_DELAYS(0) | +		LRADC_DELAY_LOOP(lradc->over_sample_cnt - 1) | +		LRADC_DELAY_DELAY(lradc->over_sample_delay - 1), +			LRADC_DELAY(3)); + +	mxs_lradc_reg_clear(lradc, LRADC_CTRL1_LRADC_IRQ(2) | +			LRADC_CTRL1_LRADC_IRQ(3) | LRADC_CTRL1_LRADC_IRQ(4) | +			LRADC_CTRL1_LRADC_IRQ(5), LRADC_CTRL1); + +	/* wake us again, when the complete conversion is done */ +	mxs_lradc_reg_set(lradc, LRADC_CTRL1_LRADC_IRQ_EN(ch), LRADC_CTRL1); +	/* +	 * after changing the touchscreen plates setting +	 * the signals need some initial time to settle. Start the +	 * SoC's delay unit and start the conversion later +	 * and automatically. +	 */ +	mxs_lradc_reg_wrt(lradc, LRADC_DELAY_TRIGGER(0) | /* don't trigger ADC */ +		LRADC_DELAY_TRIGGER_DELAYS(1 << 3) | /* trigger DELAY unit#3 */ +		LRADC_DELAY_KICK | +		LRADC_DELAY_DELAY(lradc->settling_delay), +			LRADC_DELAY(2)); +} + +/* + * Pressure detection is special: + * We want to do both required measurements for the pressure detection in + * one turn. Use the hardware features to chain both conversions and let the + * hardware report one interrupt if both conversions are done + */ +static void mxs_lradc_setup_ts_pressure(struct mxs_lradc *lradc, unsigned ch1, +							unsigned ch2) +{ +	u32 reg; + +	/* +	 * prepare for oversampling conversion +	 * +	 * from the datasheet: +	 * "The ACCUMULATE bit in the appropriate channel register +	 * HW_LRADC_CHn must be set to 1 if NUM_SAMPLES is greater then 0; +	 * otherwise, the IRQs will not fire." +	 */ +	reg = LRADC_CH_ACCUMULATE | +		LRADC_CH_NUM_SAMPLES(lradc->over_sample_cnt - 1); +	mxs_lradc_reg_wrt(lradc, reg, LRADC_CH(ch1)); +	mxs_lradc_reg_wrt(lradc, reg, LRADC_CH(ch2)); + +	/* from the datasheet: +	 * "Software must clear this register in preparation for a +	 * multi-cycle accumulation. +	 */ +	mxs_lradc_reg_clear(lradc, LRADC_CH_VALUE_MASK, LRADC_CH(ch1)); +	mxs_lradc_reg_clear(lradc, LRADC_CH_VALUE_MASK, LRADC_CH(ch2)); + +	/* prepare the delay/loop unit according to the oversampling count */ +	mxs_lradc_reg_wrt(lradc, LRADC_DELAY_TRIGGER(1 << ch1) | +		LRADC_DELAY_TRIGGER(1 << ch2) | /* start both channels */ +		LRADC_DELAY_TRIGGER_DELAYS(0) | +		LRADC_DELAY_LOOP(lradc->over_sample_cnt - 1) | +		LRADC_DELAY_DELAY(lradc->over_sample_delay - 1), +					LRADC_DELAY(3)); + +	mxs_lradc_reg_clear(lradc, LRADC_CTRL1_LRADC_IRQ(2) | +			LRADC_CTRL1_LRADC_IRQ(3) | LRADC_CTRL1_LRADC_IRQ(4) | +			LRADC_CTRL1_LRADC_IRQ(5), LRADC_CTRL1); + +	/* wake us again, when the conversions are done */ +	mxs_lradc_reg_set(lradc, LRADC_CTRL1_LRADC_IRQ_EN(ch2), LRADC_CTRL1); +	/* +	 * after changing the touchscreen plates setting +	 * the signals need some initial time to settle. Start the +	 * SoC's delay unit and start the conversion later +	 * and automatically. +	 */ +	mxs_lradc_reg_wrt(lradc, LRADC_DELAY_TRIGGER(0) | /* don't trigger ADC */ +		LRADC_DELAY_TRIGGER_DELAYS(1 << 3) | /* trigger DELAY unit#3 */ +		LRADC_DELAY_KICK | +		LRADC_DELAY_DELAY(lradc->settling_delay), LRADC_DELAY(2)); +} + +static unsigned mxs_lradc_read_raw_channel(struct mxs_lradc *lradc, +							unsigned channel) +{ +	u32 reg; +	unsigned num_samples, val; + +	reg = readl(lradc->base + LRADC_CH(channel)); +	if (reg & LRADC_CH_ACCUMULATE) +		num_samples = lradc->over_sample_cnt; +	else +		num_samples = 1; + +	val = (reg & LRADC_CH_VALUE_MASK) >> LRADC_CH_VALUE_OFFSET; +	return val / num_samples; +} + +static unsigned mxs_lradc_read_ts_pressure(struct mxs_lradc *lradc, +						unsigned ch1, unsigned ch2) +{ +	u32 reg, mask; +	unsigned pressure, m1, m2; + +	mask = LRADC_CTRL1_LRADC_IRQ(ch1) | LRADC_CTRL1_LRADC_IRQ(ch2); +	reg = readl(lradc->base + LRADC_CTRL1) & mask; + +	while (reg != mask) { +		reg = readl(lradc->base + LRADC_CTRL1) & mask; +		dev_dbg(lradc->dev, "One channel is still busy: %X\n", reg); +	} + +	m1 = mxs_lradc_read_raw_channel(lradc, ch1); +	m2 = mxs_lradc_read_raw_channel(lradc, ch2); + +	if (m2 == 0) { +		dev_warn(lradc->dev, "Cannot calculate pressure\n"); +		return 1 << (LRADC_RESOLUTION - 1); +	} + +	/* simply scale the value from 0 ... max ADC resolution */ +	pressure = m1; +	pressure *= (1 << LRADC_RESOLUTION); +	pressure /= m2; + +	dev_dbg(lradc->dev, "Pressure = %u\n", pressure); +	return pressure; +} + +#define TS_CH_XP 2 +#define TS_CH_YP 3 +#define TS_CH_XM 4 +#define TS_CH_YM 5 + +static int mxs_lradc_read_ts_channel(struct mxs_lradc *lradc) +{ +	u32 reg; +	int val; + +	reg = readl(lradc->base + LRADC_CTRL1); + +	/* only channels 3 to 5 are of interest here */ +	if (reg & LRADC_CTRL1_LRADC_IRQ(TS_CH_YP)) { +		mxs_lradc_reg_clear(lradc, LRADC_CTRL1_LRADC_IRQ_EN(TS_CH_YP) | +			LRADC_CTRL1_LRADC_IRQ(TS_CH_YP), LRADC_CTRL1); +		val = mxs_lradc_read_raw_channel(lradc, TS_CH_YP); +	} else if (reg & LRADC_CTRL1_LRADC_IRQ(TS_CH_XM)) { +		mxs_lradc_reg_clear(lradc, LRADC_CTRL1_LRADC_IRQ_EN(TS_CH_XM) | +			LRADC_CTRL1_LRADC_IRQ(TS_CH_XM), LRADC_CTRL1); +		val = mxs_lradc_read_raw_channel(lradc, TS_CH_XM); +	} else if (reg & LRADC_CTRL1_LRADC_IRQ(TS_CH_YM)) { +		mxs_lradc_reg_clear(lradc, LRADC_CTRL1_LRADC_IRQ_EN(TS_CH_YM) | +			LRADC_CTRL1_LRADC_IRQ(TS_CH_YM), LRADC_CTRL1); +		val = mxs_lradc_read_raw_channel(lradc, TS_CH_YM); +	} else { +		return -EIO; +	} + +	mxs_lradc_reg_wrt(lradc, 0, LRADC_DELAY(2)); +	mxs_lradc_reg_wrt(lradc, 0, LRADC_DELAY(3)); + +	return val; +} + +/* + * YP(open)--+-------------+ + *           |             |--+ + *           |             |  | + *    YM(-)--+-------------+  | + *             +--------------+ + *             |              | + *         XP(weak+)        XM(open) + * + * "weak+" means 200k Ohm VDDIO + * (-) means GND + */ +static void mxs_lradc_setup_touch_detection(struct mxs_lradc *lradc) +{ +	/* +	 * In order to detect a touch event the 'touch detect enable' bit +	 * enables: +	 *  - a weak pullup to the X+ connector +	 *  - a strong ground at the Y- connector +	 */ +	mxs_lradc_reg_clear(lradc, mxs_lradc_plate_mask(lradc), LRADC_CTRL0); +	mxs_lradc_reg_set(lradc, mxs_lradc_touch_detect_bit(lradc), +				LRADC_CTRL0); +} + +/* + * YP(meas)--+-------------+ + *           |             |--+ + *           |             |  | + * YM(open)--+-------------+  | + *             +--------------+ + *             |              | + *           XP(+)          XM(-) + * + * (+) means here 1.85 V + * (-) means here GND + */ +static void mxs_lradc_prepare_x_pos(struct mxs_lradc *lradc) +{ +	mxs_lradc_reg_clear(lradc, mxs_lradc_plate_mask(lradc), LRADC_CTRL0); +	mxs_lradc_reg_set(lradc, mxs_lradc_drive_x_plate(lradc), LRADC_CTRL0); + +	lradc->cur_plate = LRADC_SAMPLE_X; +	mxs_lradc_setup_ts_channel(lradc, TS_CH_YP); +} + +/* + *   YP(+)--+-------------+ + *          |             |--+ + *          |             |  | + *   YM(-)--+-------------+  | + *            +--------------+ + *            |              | + *         XP(open)        XM(meas) + * + * (+) means here 1.85 V + * (-) means here GND + */ +static void mxs_lradc_prepare_y_pos(struct mxs_lradc *lradc) +{ +	mxs_lradc_reg_clear(lradc, mxs_lradc_plate_mask(lradc), LRADC_CTRL0); +	mxs_lradc_reg_set(lradc, mxs_lradc_drive_y_plate(lradc), LRADC_CTRL0); + +	lradc->cur_plate = LRADC_SAMPLE_Y; +	mxs_lradc_setup_ts_channel(lradc, TS_CH_XM); +} + +/* + *    YP(+)--+-------------+ + *           |             |--+ + *           |             |  | + * YM(meas)--+-------------+  | + *             +--------------+ + *             |              | + *          XP(meas)        XM(-) + * + * (+) means here 1.85 V + * (-) means here GND + */ +static void mxs_lradc_prepare_pressure(struct mxs_lradc *lradc) +{ +	mxs_lradc_reg_clear(lradc, mxs_lradc_plate_mask(lradc), LRADC_CTRL0); +	mxs_lradc_reg_set(lradc, mxs_lradc_drive_pressure(lradc), LRADC_CTRL0); + +	lradc->cur_plate = LRADC_SAMPLE_PRESSURE; +	mxs_lradc_setup_ts_pressure(lradc, TS_CH_XP, TS_CH_YM); +} + +static void mxs_lradc_enable_touch_detection(struct mxs_lradc *lradc) +{ +	mxs_lradc_setup_touch_detection(lradc); + +	lradc->cur_plate = LRADC_TOUCH; +	mxs_lradc_reg_clear(lradc, LRADC_CTRL1_TOUCH_DETECT_IRQ | +				LRADC_CTRL1_TOUCH_DETECT_IRQ_EN, LRADC_CTRL1); +	mxs_lradc_reg_set(lradc, LRADC_CTRL1_TOUCH_DETECT_IRQ_EN, LRADC_CTRL1); +} + +static void mxs_lradc_report_ts_event(struct mxs_lradc *lradc) +{ +	input_report_abs(lradc->ts_input, ABS_X, lradc->ts_x_pos); +	input_report_abs(lradc->ts_input, ABS_Y, lradc->ts_y_pos); +	input_report_abs(lradc->ts_input, ABS_PRESSURE, lradc->ts_pressure); +	input_report_key(lradc->ts_input, BTN_TOUCH, 1); +	input_sync(lradc->ts_input); +} + +static void mxs_lradc_complete_touch_event(struct mxs_lradc *lradc) +{ +	mxs_lradc_setup_touch_detection(lradc); +	lradc->cur_plate = LRADC_SAMPLE_VALID; +	/* +	 * start a dummy conversion to burn time to settle the signals +	 * note: we are not interested in the conversion's value +	 */ +	mxs_lradc_reg_wrt(lradc, 0, LRADC_CH(5)); +	mxs_lradc_reg_clear(lradc, LRADC_CTRL1_LRADC_IRQ(5), LRADC_CTRL1); +	mxs_lradc_reg_set(lradc, LRADC_CTRL1_LRADC_IRQ_EN(5), LRADC_CTRL1); +	mxs_lradc_reg_wrt(lradc, LRADC_DELAY_TRIGGER(1 << 5) | +		LRADC_DELAY_KICK | LRADC_DELAY_DELAY(10), /* waste 5 ms */ +			LRADC_DELAY(2)); +} + +/* + * in order to avoid false measurements, report only samples where + * the surface is still touched after the position measurement + */ +static void mxs_lradc_finish_touch_event(struct mxs_lradc *lradc, bool valid) +{ +	/* if it is still touched, report the sample */ +	if (valid && mxs_lradc_check_touch_event(lradc)) { +		lradc->ts_valid = true; +		mxs_lradc_report_ts_event(lradc); +	} + +	/* if it is even still touched, continue with the next measurement */ +	if (mxs_lradc_check_touch_event(lradc)) { +		mxs_lradc_prepare_y_pos(lradc); +		return; +	} + +	if (lradc->ts_valid) { +		/* signal the release */ +		lradc->ts_valid = false; +		input_report_key(lradc->ts_input, BTN_TOUCH, 0); +		input_sync(lradc->ts_input); +	} + +	/* if it is released, wait for the next touch via IRQ */ +	lradc->cur_plate = LRADC_TOUCH; +	mxs_lradc_reg_clear(lradc, LRADC_CTRL1_TOUCH_DETECT_IRQ, LRADC_CTRL1); +	mxs_lradc_reg_set(lradc, LRADC_CTRL1_TOUCH_DETECT_IRQ_EN, LRADC_CTRL1); +} + +/* touchscreen's state machine */ +static void mxs_lradc_handle_touch(struct mxs_lradc *lradc) +{ +	int val; + +	switch (lradc->cur_plate) { +	case LRADC_TOUCH: +		/* +		 * start with the Y-pos, because it uses nearly the same plate +		 * settings like the touch detection +		 */ +		if (mxs_lradc_check_touch_event(lradc)) { +			mxs_lradc_reg_clear(lradc, +					LRADC_CTRL1_TOUCH_DETECT_IRQ_EN, +					LRADC_CTRL1); +			mxs_lradc_prepare_y_pos(lradc); +		} +		mxs_lradc_reg_clear(lradc, LRADC_CTRL1_TOUCH_DETECT_IRQ, +					LRADC_CTRL1); +		return; + +	case LRADC_SAMPLE_Y: +		val = mxs_lradc_read_ts_channel(lradc); +		if (val < 0) { +			mxs_lradc_enable_touch_detection(lradc); /* re-start */ +			return; +		} +		lradc->ts_y_pos = val; +		mxs_lradc_prepare_x_pos(lradc); +		return; + +	case LRADC_SAMPLE_X: +		val = mxs_lradc_read_ts_channel(lradc); +		if (val < 0) { +			mxs_lradc_enable_touch_detection(lradc); /* re-start */ +			return; +		} +		lradc->ts_x_pos = val; +		mxs_lradc_prepare_pressure(lradc); +		return; + +	case LRADC_SAMPLE_PRESSURE: +		lradc->ts_pressure = +			mxs_lradc_read_ts_pressure(lradc, TS_CH_XP, TS_CH_YM); +		mxs_lradc_complete_touch_event(lradc); +		return; + +	case LRADC_SAMPLE_VALID: +		val = mxs_lradc_read_ts_channel(lradc); /* ignore the value */ +		mxs_lradc_finish_touch_event(lradc, 1); +		break; +	} +} + +/* + * Raw I/O operations + */ +static int mxs_lradc_read_single(struct iio_dev *iio_dev, int chan, int *val) +{ +	struct mxs_lradc *lradc = iio_priv(iio_dev); +	int ret; + +	/* +	 * See if there is no buffered operation in progess. If there is, simply +	 * bail out. This can be improved to support both buffered and raw IO at +	 * the same time, yet the code becomes horribly complicated. Therefore I +	 * applied KISS principle here. +	 */ +	ret = mutex_trylock(&lradc->lock); +	if (!ret) +		return -EBUSY; + +	reinit_completion(&lradc->completion); + +	/* +	 * No buffered operation in progress, map the channel and trigger it. +	 * Virtual channel 0 is always used here as the others are always not +	 * used if doing raw sampling. +	 */ +	if (lradc->soc == IMX28_LRADC) +		mxs_lradc_reg_clear(lradc, LRADC_CTRL1_MX28_LRADC_IRQ_EN_MASK, +			LRADC_CTRL1); +	mxs_lradc_reg_clear(lradc, 0xff, LRADC_CTRL0); + +	/* Enable / disable the divider per requirement */ +	if (test_bit(chan, &lradc->is_divided)) +		mxs_lradc_reg_set(lradc, 1 << LRADC_CTRL2_DIVIDE_BY_TWO_OFFSET, +			LRADC_CTRL2); +	else +		mxs_lradc_reg_clear(lradc, +			1 << LRADC_CTRL2_DIVIDE_BY_TWO_OFFSET, LRADC_CTRL2); + +	/* Clean the slot's previous content, then set new one. */ +	mxs_lradc_reg_clear(lradc, LRADC_CTRL4_LRADCSELECT_MASK(0), +			LRADC_CTRL4); +	mxs_lradc_reg_set(lradc, chan, LRADC_CTRL4); + +	mxs_lradc_reg_wrt(lradc, 0, LRADC_CH(0)); + +	/* Enable the IRQ and start sampling the channel. */ +	mxs_lradc_reg_set(lradc, LRADC_CTRL1_LRADC_IRQ_EN(0), LRADC_CTRL1); +	mxs_lradc_reg_set(lradc, 1 << 0, LRADC_CTRL0); + +	/* Wait for completion on the channel, 1 second max. */ +	ret = wait_for_completion_killable_timeout(&lradc->completion, HZ); +	if (!ret) +		ret = -ETIMEDOUT; +	if (ret < 0) +		goto err; + +	/* Read the data. */ +	*val = readl(lradc->base + LRADC_CH(0)) & LRADC_CH_VALUE_MASK; +	ret = IIO_VAL_INT; + +err: +	mxs_lradc_reg_clear(lradc, LRADC_CTRL1_LRADC_IRQ_EN(0), LRADC_CTRL1); + +	mutex_unlock(&lradc->lock); + +	return ret; +} + +static int mxs_lradc_read_temp(struct iio_dev *iio_dev, int *val) +{ +	int ret, min, max; + +	ret = mxs_lradc_read_single(iio_dev, 8, &min); +	if (ret != IIO_VAL_INT) +		return ret; + +	ret = mxs_lradc_read_single(iio_dev, 9, &max); +	if (ret != IIO_VAL_INT) +		return ret; + +	*val = max - min; + +	return IIO_VAL_INT; +} + +static int mxs_lradc_read_raw(struct iio_dev *iio_dev, +			const struct iio_chan_spec *chan, +			int *val, int *val2, long m) +{ +	struct mxs_lradc *lradc = iio_priv(iio_dev); + +	switch (m) { +	case IIO_CHAN_INFO_RAW: +		if (chan->type == IIO_TEMP) +			return mxs_lradc_read_temp(iio_dev, val); + +		return mxs_lradc_read_single(iio_dev, chan->channel, val); + +	case IIO_CHAN_INFO_SCALE: +		if (chan->type == IIO_TEMP) { +			/* From the datasheet, we have to multiply by 1.012 and +			 * divide by 4 +			 */ +			*val = 0; +			*val2 = 253000; +			return IIO_VAL_INT_PLUS_MICRO; +		} + +		*val = lradc->vref_mv[chan->channel]; +		*val2 = chan->scan_type.realbits - +			test_bit(chan->channel, &lradc->is_divided); +		return IIO_VAL_FRACTIONAL_LOG2; + +	case IIO_CHAN_INFO_OFFSET: +		if (chan->type == IIO_TEMP) { +			/* The calculated value from the ADC is in Kelvin, we +			 * want Celsius for hwmon so the offset is +			 * -272.15 * scale +			 */ +			*val = -1075; +			*val2 = 691699; + +			return IIO_VAL_INT_PLUS_MICRO; +		} + +		return -EINVAL; + +	default: +		break; +	} + +	return -EINVAL; +} + +static int mxs_lradc_write_raw(struct iio_dev *iio_dev, +			       const struct iio_chan_spec *chan, +			       int val, int val2, long m) +{ +	struct mxs_lradc *lradc = iio_priv(iio_dev); +	struct mxs_lradc_scale *scale_avail = +			lradc->scale_avail[chan->channel]; +	int ret; + +	ret = mutex_trylock(&lradc->lock); +	if (!ret) +		return -EBUSY; + +	switch (m) { +	case IIO_CHAN_INFO_SCALE: +		ret = -EINVAL; +		if (val == scale_avail[MXS_LRADC_DIV_DISABLED].integer && +		    val2 == scale_avail[MXS_LRADC_DIV_DISABLED].nano) { +			/* divider by two disabled */ +			clear_bit(chan->channel, &lradc->is_divided); +			ret = 0; +		} else if (val == scale_avail[MXS_LRADC_DIV_ENABLED].integer && +			   val2 == scale_avail[MXS_LRADC_DIV_ENABLED].nano) { +			/* divider by two enabled */ +			set_bit(chan->channel, &lradc->is_divided); +			ret = 0; +		} + +		break; +	default: +		ret = -EINVAL; +		break; +	} + +	mutex_unlock(&lradc->lock); + +	return ret; +} + +static int mxs_lradc_write_raw_get_fmt(struct iio_dev *iio_dev, +				       const struct iio_chan_spec *chan, +				       long m) +{ +	return IIO_VAL_INT_PLUS_NANO; +} + +static ssize_t mxs_lradc_show_scale_available_ch(struct device *dev, +		struct device_attribute *attr, +		char *buf, +		int ch) +{ +	struct iio_dev *iio = dev_to_iio_dev(dev); +	struct mxs_lradc *lradc = iio_priv(iio); +	int i, len = 0; + +	for (i = 0; i < ARRAY_SIZE(lradc->scale_avail[ch]); i++) +		len += sprintf(buf + len, "%d.%09u ", +			       lradc->scale_avail[ch][i].integer, +			       lradc->scale_avail[ch][i].nano); + +	len += sprintf(buf + len, "\n"); + +	return len; +} + +static ssize_t mxs_lradc_show_scale_available(struct device *dev, +		struct device_attribute *attr, +		char *buf) +{ +	struct iio_dev_attr *iio_attr = to_iio_dev_attr(attr); + +	return mxs_lradc_show_scale_available_ch(dev, attr, buf, +						 iio_attr->address); +} + +#define SHOW_SCALE_AVAILABLE_ATTR(ch)					\ +static IIO_DEVICE_ATTR(in_voltage##ch##_scale_available, S_IRUGO,	\ +		       mxs_lradc_show_scale_available, NULL, ch) + +SHOW_SCALE_AVAILABLE_ATTR(0); +SHOW_SCALE_AVAILABLE_ATTR(1); +SHOW_SCALE_AVAILABLE_ATTR(2); +SHOW_SCALE_AVAILABLE_ATTR(3); +SHOW_SCALE_AVAILABLE_ATTR(4); +SHOW_SCALE_AVAILABLE_ATTR(5); +SHOW_SCALE_AVAILABLE_ATTR(6); +SHOW_SCALE_AVAILABLE_ATTR(7); +SHOW_SCALE_AVAILABLE_ATTR(10); +SHOW_SCALE_AVAILABLE_ATTR(11); +SHOW_SCALE_AVAILABLE_ATTR(12); +SHOW_SCALE_AVAILABLE_ATTR(13); +SHOW_SCALE_AVAILABLE_ATTR(14); +SHOW_SCALE_AVAILABLE_ATTR(15); + +static struct attribute *mxs_lradc_attributes[] = { +	&iio_dev_attr_in_voltage0_scale_available.dev_attr.attr, +	&iio_dev_attr_in_voltage1_scale_available.dev_attr.attr, +	&iio_dev_attr_in_voltage2_scale_available.dev_attr.attr, +	&iio_dev_attr_in_voltage3_scale_available.dev_attr.attr, +	&iio_dev_attr_in_voltage4_scale_available.dev_attr.attr, +	&iio_dev_attr_in_voltage5_scale_available.dev_attr.attr, +	&iio_dev_attr_in_voltage6_scale_available.dev_attr.attr, +	&iio_dev_attr_in_voltage7_scale_available.dev_attr.attr, +	&iio_dev_attr_in_voltage10_scale_available.dev_attr.attr, +	&iio_dev_attr_in_voltage11_scale_available.dev_attr.attr, +	&iio_dev_attr_in_voltage12_scale_available.dev_attr.attr, +	&iio_dev_attr_in_voltage13_scale_available.dev_attr.attr, +	&iio_dev_attr_in_voltage14_scale_available.dev_attr.attr, +	&iio_dev_attr_in_voltage15_scale_available.dev_attr.attr, +	NULL +}; + +static const struct attribute_group mxs_lradc_attribute_group = { +	.attrs = mxs_lradc_attributes, +}; + +static const struct iio_info mxs_lradc_iio_info = { +	.driver_module		= THIS_MODULE, +	.read_raw		= mxs_lradc_read_raw, +	.write_raw		= mxs_lradc_write_raw, +	.write_raw_get_fmt	= mxs_lradc_write_raw_get_fmt, +	.attrs			= &mxs_lradc_attribute_group, +}; + +static int mxs_lradc_ts_open(struct input_dev *dev) +{ +	struct mxs_lradc *lradc = input_get_drvdata(dev); + +	/* Enable the touch-detect circuitry. */ +	mxs_lradc_enable_touch_detection(lradc); + +	return 0; +} + +static void mxs_lradc_disable_ts(struct mxs_lradc *lradc) +{ +	/* stop all interrupts from firing */ +	mxs_lradc_reg_clear(lradc, LRADC_CTRL1_TOUCH_DETECT_IRQ_EN | +		LRADC_CTRL1_LRADC_IRQ_EN(2) | LRADC_CTRL1_LRADC_IRQ_EN(3) | +		LRADC_CTRL1_LRADC_IRQ_EN(4) | LRADC_CTRL1_LRADC_IRQ_EN(5), +		LRADC_CTRL1); + +	/* Power-down touchscreen touch-detect circuitry. */ +	mxs_lradc_reg_clear(lradc, mxs_lradc_plate_mask(lradc), LRADC_CTRL0); +} + +static void mxs_lradc_ts_close(struct input_dev *dev) +{ +	struct mxs_lradc *lradc = input_get_drvdata(dev); + +	mxs_lradc_disable_ts(lradc); +} + +static int mxs_lradc_ts_register(struct mxs_lradc *lradc) +{ +	struct input_dev *input; +	struct device *dev = lradc->dev; +	int ret; + +	if (!lradc->use_touchscreen) +		return 0; + +	input = input_allocate_device(); +	if (!input) +		return -ENOMEM; + +	input->name = DRIVER_NAME; +	input->id.bustype = BUS_HOST; +	input->dev.parent = dev; +	input->open = mxs_lradc_ts_open; +	input->close = mxs_lradc_ts_close; + +	__set_bit(EV_ABS, input->evbit); +	__set_bit(EV_KEY, input->evbit); +	__set_bit(BTN_TOUCH, input->keybit); +	input_set_abs_params(input, ABS_X, 0, LRADC_SINGLE_SAMPLE_MASK, 0, 0); +	input_set_abs_params(input, ABS_Y, 0, LRADC_SINGLE_SAMPLE_MASK, 0, 0); +	input_set_abs_params(input, ABS_PRESSURE, 0, LRADC_SINGLE_SAMPLE_MASK, +			     0, 0); + +	lradc->ts_input = input; +	input_set_drvdata(input, lradc); +	ret = input_register_device(input); +	if (ret) +		input_free_device(lradc->ts_input); + +	return ret; +} + +static void mxs_lradc_ts_unregister(struct mxs_lradc *lradc) +{ +	if (!lradc->use_touchscreen) +		return; + +	mxs_lradc_disable_ts(lradc); +	input_unregister_device(lradc->ts_input); +} + +/* + * IRQ Handling + */ +static irqreturn_t mxs_lradc_handle_irq(int irq, void *data) +{ +	struct iio_dev *iio = data; +	struct mxs_lradc *lradc = iio_priv(iio); +	unsigned long reg = readl(lradc->base + LRADC_CTRL1); +	const uint32_t ts_irq_mask = +		LRADC_CTRL1_TOUCH_DETECT_IRQ | +		LRADC_CTRL1_LRADC_IRQ(2) | +		LRADC_CTRL1_LRADC_IRQ(3) | +		LRADC_CTRL1_LRADC_IRQ(4) | +		LRADC_CTRL1_LRADC_IRQ(5); + +	if (!(reg & mxs_lradc_irq_mask(lradc))) +		return IRQ_NONE; + +	if (lradc->use_touchscreen && (reg & ts_irq_mask)) +		mxs_lradc_handle_touch(lradc); + +	if (iio_buffer_enabled(iio)) +		iio_trigger_poll(iio->trig, iio_get_time_ns()); +	else if (reg & LRADC_CTRL1_LRADC_IRQ(0)) +		complete(&lradc->completion); + +	mxs_lradc_reg_clear(lradc, reg & mxs_lradc_irq_mask(lradc), +			LRADC_CTRL1); + +	return IRQ_HANDLED; +} + +/* + * Trigger handling + */ +static irqreturn_t mxs_lradc_trigger_handler(int irq, void *p) +{ +	struct iio_poll_func *pf = p; +	struct iio_dev *iio = pf->indio_dev; +	struct mxs_lradc *lradc = iio_priv(iio); +	const uint32_t chan_value = LRADC_CH_ACCUMULATE | +		((LRADC_DELAY_TIMER_LOOP - 1) << LRADC_CH_NUM_SAMPLES_OFFSET); +	unsigned int i, j = 0; + +	for_each_set_bit(i, iio->active_scan_mask, LRADC_MAX_TOTAL_CHANS) { +		lradc->buffer[j] = readl(lradc->base + LRADC_CH(j)); +		mxs_lradc_reg_wrt(lradc, chan_value, LRADC_CH(j)); +		lradc->buffer[j] &= LRADC_CH_VALUE_MASK; +		lradc->buffer[j] /= LRADC_DELAY_TIMER_LOOP; +		j++; +	} + +	iio_push_to_buffers_with_timestamp(iio, lradc->buffer, pf->timestamp); + +	iio_trigger_notify_done(iio->trig); + +	return IRQ_HANDLED; +} + +static int mxs_lradc_configure_trigger(struct iio_trigger *trig, bool state) +{ +	struct iio_dev *iio = iio_trigger_get_drvdata(trig); +	struct mxs_lradc *lradc = iio_priv(iio); +	const uint32_t st = state ? STMP_OFFSET_REG_SET : STMP_OFFSET_REG_CLR; + +	mxs_lradc_reg_wrt(lradc, LRADC_DELAY_KICK, LRADC_DELAY(0) + st); + +	return 0; +} + +static const struct iio_trigger_ops mxs_lradc_trigger_ops = { +	.owner = THIS_MODULE, +	.set_trigger_state = &mxs_lradc_configure_trigger, +}; + +static int mxs_lradc_trigger_init(struct iio_dev *iio) +{ +	int ret; +	struct iio_trigger *trig; +	struct mxs_lradc *lradc = iio_priv(iio); + +	trig = iio_trigger_alloc("%s-dev%i", iio->name, iio->id); +	if (trig == NULL) +		return -ENOMEM; + +	trig->dev.parent = lradc->dev; +	iio_trigger_set_drvdata(trig, iio); +	trig->ops = &mxs_lradc_trigger_ops; + +	ret = iio_trigger_register(trig); +	if (ret) { +		iio_trigger_free(trig); +		return ret; +	} + +	lradc->trig = trig; + +	return 0; +} + +static void mxs_lradc_trigger_remove(struct iio_dev *iio) +{ +	struct mxs_lradc *lradc = iio_priv(iio); + +	iio_trigger_unregister(lradc->trig); +	iio_trigger_free(lradc->trig); +} + +static int mxs_lradc_buffer_preenable(struct iio_dev *iio) +{ +	struct mxs_lradc *lradc = iio_priv(iio); +	int ret = 0, chan, ofs = 0; +	unsigned long enable = 0; +	uint32_t ctrl4_set = 0; +	uint32_t ctrl4_clr = 0; +	uint32_t ctrl1_irq = 0; +	const uint32_t chan_value = LRADC_CH_ACCUMULATE | +		((LRADC_DELAY_TIMER_LOOP - 1) << LRADC_CH_NUM_SAMPLES_OFFSET); +	const int len = bitmap_weight(iio->active_scan_mask, +			LRADC_MAX_TOTAL_CHANS); + +	if (!len) +		return -EINVAL; + +	/* +	 * Lock the driver so raw access can not be done during buffered +	 * operation. This simplifies the code a lot. +	 */ +	ret = mutex_trylock(&lradc->lock); +	if (!ret) +		return -EBUSY; + +	lradc->buffer = kmalloc(len * sizeof(*lradc->buffer), GFP_KERNEL); +	if (!lradc->buffer) { +		ret = -ENOMEM; +		goto err_mem; +	} + +	if (lradc->soc == IMX28_LRADC) +		mxs_lradc_reg_clear(lradc, LRADC_CTRL1_MX28_LRADC_IRQ_EN_MASK, +							LRADC_CTRL1); +	mxs_lradc_reg_clear(lradc, 0xff, LRADC_CTRL0); + +	for_each_set_bit(chan, iio->active_scan_mask, LRADC_MAX_TOTAL_CHANS) { +		ctrl4_set |= chan << LRADC_CTRL4_LRADCSELECT_OFFSET(ofs); +		ctrl4_clr |= LRADC_CTRL4_LRADCSELECT_MASK(ofs); +		ctrl1_irq |= LRADC_CTRL1_LRADC_IRQ_EN(ofs); +		mxs_lradc_reg_wrt(lradc, chan_value, LRADC_CH(ofs)); +		bitmap_set(&enable, ofs, 1); +		ofs++; +	} + +	mxs_lradc_reg_clear(lradc, LRADC_DELAY_TRIGGER_LRADCS_MASK | +					LRADC_DELAY_KICK, LRADC_DELAY(0)); +	mxs_lradc_reg_clear(lradc, ctrl4_clr, LRADC_CTRL4); +	mxs_lradc_reg_set(lradc, ctrl4_set, LRADC_CTRL4); +	mxs_lradc_reg_set(lradc, ctrl1_irq, LRADC_CTRL1); +	mxs_lradc_reg_set(lradc, enable << LRADC_DELAY_TRIGGER_LRADCS_OFFSET, +					LRADC_DELAY(0)); + +	return 0; + +err_mem: +	mutex_unlock(&lradc->lock); +	return ret; +} + +static int mxs_lradc_buffer_postdisable(struct iio_dev *iio) +{ +	struct mxs_lradc *lradc = iio_priv(iio); + +	mxs_lradc_reg_clear(lradc, LRADC_DELAY_TRIGGER_LRADCS_MASK | +					LRADC_DELAY_KICK, LRADC_DELAY(0)); + +	mxs_lradc_reg_clear(lradc, 0xff, LRADC_CTRL0); +	if (lradc->soc == IMX28_LRADC) +		mxs_lradc_reg_clear(lradc, LRADC_CTRL1_MX28_LRADC_IRQ_EN_MASK, +					LRADC_CTRL1); + +	kfree(lradc->buffer); +	mutex_unlock(&lradc->lock); + +	return 0; +} + +static bool mxs_lradc_validate_scan_mask(struct iio_dev *iio, +					const unsigned long *mask) +{ +	struct mxs_lradc *lradc = iio_priv(iio); +	const int map_chans = bitmap_weight(mask, LRADC_MAX_TOTAL_CHANS); +	int rsvd_chans = 0; +	unsigned long rsvd_mask = 0; + +	if (lradc->use_touchbutton) +		rsvd_mask |= CHAN_MASK_TOUCHBUTTON; +	if (lradc->use_touchscreen == MXS_LRADC_TOUCHSCREEN_4WIRE) +		rsvd_mask |= CHAN_MASK_TOUCHSCREEN_4WIRE; +	if (lradc->use_touchscreen == MXS_LRADC_TOUCHSCREEN_5WIRE) +		rsvd_mask |= CHAN_MASK_TOUCHSCREEN_5WIRE; + +	if (lradc->use_touchbutton) +		rsvd_chans++; +	if (lradc->use_touchscreen) +		rsvd_chans++; + +	/* Test for attempts to map channels with special mode of operation. */ +	if (bitmap_intersects(mask, &rsvd_mask, LRADC_MAX_TOTAL_CHANS)) +		return false; + +	/* Test for attempts to map more channels then available slots. */ +	if (map_chans + rsvd_chans > LRADC_MAX_MAPPED_CHANS) +		return false; + +	return true; +} + +static const struct iio_buffer_setup_ops mxs_lradc_buffer_ops = { +	.preenable = &mxs_lradc_buffer_preenable, +	.postenable = &iio_triggered_buffer_postenable, +	.predisable = &iio_triggered_buffer_predisable, +	.postdisable = &mxs_lradc_buffer_postdisable, +	.validate_scan_mask = &mxs_lradc_validate_scan_mask, +}; + +/* + * Driver initialization + */ + +#define MXS_ADC_CHAN(idx, chan_type) {				\ +	.type = (chan_type),					\ +	.indexed = 1,						\ +	.scan_index = (idx),					\ +	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |		\ +			      BIT(IIO_CHAN_INFO_SCALE),		\ +	.channel = (idx),					\ +	.address = (idx),					\ +	.scan_type = {						\ +		.sign = 'u',					\ +		.realbits = LRADC_RESOLUTION,			\ +		.storagebits = 32,				\ +	},							\ +} + +static const struct iio_chan_spec mxs_lradc_chan_spec[] = { +	MXS_ADC_CHAN(0, IIO_VOLTAGE), +	MXS_ADC_CHAN(1, IIO_VOLTAGE), +	MXS_ADC_CHAN(2, IIO_VOLTAGE), +	MXS_ADC_CHAN(3, IIO_VOLTAGE), +	MXS_ADC_CHAN(4, IIO_VOLTAGE), +	MXS_ADC_CHAN(5, IIO_VOLTAGE), +	MXS_ADC_CHAN(6, IIO_VOLTAGE), +	MXS_ADC_CHAN(7, IIO_VOLTAGE),	/* VBATT */ +	/* Combined Temperature sensors */ +	{ +		.type = IIO_TEMP, +		.indexed = 1, +		.scan_index = 8, +		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | +				      BIT(IIO_CHAN_INFO_OFFSET) | +				      BIT(IIO_CHAN_INFO_SCALE), +		.channel = 8, +		.scan_type = {.sign = 'u', .realbits = 18, .storagebits = 32,}, +	}, +	MXS_ADC_CHAN(10, IIO_VOLTAGE),	/* VDDIO */ +	MXS_ADC_CHAN(11, IIO_VOLTAGE),	/* VTH */ +	MXS_ADC_CHAN(12, IIO_VOLTAGE),	/* VDDA */ +	MXS_ADC_CHAN(13, IIO_VOLTAGE),	/* VDDD */ +	MXS_ADC_CHAN(14, IIO_VOLTAGE),	/* VBG */ +	MXS_ADC_CHAN(15, IIO_VOLTAGE),	/* VDD5V */ +}; + +static int mxs_lradc_hw_init(struct mxs_lradc *lradc) +{ +	/* The ADC always uses DELAY CHANNEL 0. */ +	const uint32_t adc_cfg = +		(1 << (LRADC_DELAY_TRIGGER_DELAYS_OFFSET + 0)) | +		(LRADC_DELAY_TIMER_PER << LRADC_DELAY_DELAY_OFFSET); + +	int ret = stmp_reset_block(lradc->base); +	if (ret) +		return ret; + +	/* Configure DELAY CHANNEL 0 for generic ADC sampling. */ +	mxs_lradc_reg_wrt(lradc, adc_cfg, LRADC_DELAY(0)); + +	/* Disable remaining DELAY CHANNELs */ +	mxs_lradc_reg_wrt(lradc, 0, LRADC_DELAY(1)); +	mxs_lradc_reg_wrt(lradc, 0, LRADC_DELAY(2)); +	mxs_lradc_reg_wrt(lradc, 0, LRADC_DELAY(3)); + +	/* Configure the touchscreen type */ +	if (lradc->soc == IMX28_LRADC) { +		mxs_lradc_reg_clear(lradc, LRADC_CTRL0_MX28_TOUCH_SCREEN_TYPE, +							LRADC_CTRL0); + +	if (lradc->use_touchscreen == MXS_LRADC_TOUCHSCREEN_5WIRE) +		mxs_lradc_reg_set(lradc, LRADC_CTRL0_MX28_TOUCH_SCREEN_TYPE, +				LRADC_CTRL0); +	} + +	/* Start internal temperature sensing. */ +	mxs_lradc_reg_wrt(lradc, 0, LRADC_CTRL2); + +	return 0; +} + +static void mxs_lradc_hw_stop(struct mxs_lradc *lradc) +{ +	int i; + +	mxs_lradc_reg_clear(lradc, mxs_lradc_irq_en_mask(lradc), LRADC_CTRL1); + +	for (i = 0; i < LRADC_MAX_DELAY_CHANS; i++) +		mxs_lradc_reg_wrt(lradc, 0, LRADC_DELAY(i)); +} + +static const struct of_device_id mxs_lradc_dt_ids[] = { +	{ .compatible = "fsl,imx23-lradc", .data = (void *)IMX23_LRADC, }, +	{ .compatible = "fsl,imx28-lradc", .data = (void *)IMX28_LRADC, }, +	{ /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, mxs_lradc_dt_ids); + +static int mxs_lradc_probe_touchscreen(struct mxs_lradc *lradc, +						struct device_node *lradc_node) +{ +	int ret; +	u32 ts_wires = 0, adapt; + +	ret = of_property_read_u32(lradc_node, "fsl,lradc-touchscreen-wires", +				&ts_wires); +	if (ret) +		return -ENODEV; /* touchscreen feature disabled */ + +	switch (ts_wires) { +	case 4: +		lradc->use_touchscreen = MXS_LRADC_TOUCHSCREEN_4WIRE; +		break; +	case 5: +		if (lradc->soc == IMX28_LRADC) { +			lradc->use_touchscreen = MXS_LRADC_TOUCHSCREEN_5WIRE; +			break; +		} +		/* fall through an error message for i.MX23 */ +	default: +		dev_err(lradc->dev, +			"Unsupported number of touchscreen wires (%d)\n", +			ts_wires); +		return -EINVAL; +	} + +	lradc->over_sample_cnt = 4; +	ret = of_property_read_u32(lradc_node, "fsl,ave-ctrl", &adapt); +	if (ret == 0) +		lradc->over_sample_cnt = adapt; + +	lradc->over_sample_delay = 2; +	ret = of_property_read_u32(lradc_node, "fsl,ave-delay", &adapt); +	if (ret == 0) +		lradc->over_sample_delay = adapt; + +	lradc->settling_delay = 10; +	ret = of_property_read_u32(lradc_node, "fsl,settling", &adapt); +	if (ret == 0) +		lradc->settling_delay = adapt; + +	return 0; +} + +static int mxs_lradc_probe(struct platform_device *pdev) +{ +	const struct of_device_id *of_id = +		of_match_device(mxs_lradc_dt_ids, &pdev->dev); +	const struct mxs_lradc_of_config *of_cfg = +		&mxs_lradc_of_config[(enum mxs_lradc_id)of_id->data]; +	struct device *dev = &pdev->dev; +	struct device_node *node = dev->of_node; +	struct mxs_lradc *lradc; +	struct iio_dev *iio; +	struct resource *iores; +	int ret = 0, touch_ret; +	int i, s; +	uint64_t scale_uv; + +	/* Allocate the IIO device. */ +	iio = devm_iio_device_alloc(dev, sizeof(*lradc)); +	if (!iio) { +		dev_err(dev, "Failed to allocate IIO device\n"); +		return -ENOMEM; +	} + +	lradc = iio_priv(iio); +	lradc->soc = (enum mxs_lradc_id)of_id->data; + +	/* Grab the memory area */ +	iores = platform_get_resource(pdev, IORESOURCE_MEM, 0); +	lradc->dev = &pdev->dev; +	lradc->base = devm_ioremap_resource(dev, iores); +	if (IS_ERR(lradc->base)) +		return PTR_ERR(lradc->base); + +	lradc->clk = devm_clk_get(&pdev->dev, NULL); +	if (IS_ERR(lradc->clk)) { +		dev_err(dev, "Failed to get the delay unit clock\n"); +		return PTR_ERR(lradc->clk); +	} +	ret = clk_prepare_enable(lradc->clk); +	if (ret != 0) { +		dev_err(dev, "Failed to enable the delay unit clock\n"); +		return ret; +	} + +	touch_ret = mxs_lradc_probe_touchscreen(lradc, node); + +	/* Grab all IRQ sources */ +	for (i = 0; i < of_cfg->irq_count; i++) { +		lradc->irq[i] = platform_get_irq(pdev, i); +		if (lradc->irq[i] < 0) +			return lradc->irq[i]; + +		ret = devm_request_irq(dev, lradc->irq[i], +					mxs_lradc_handle_irq, 0, +					of_cfg->irq_name[i], iio); +		if (ret) +			return ret; +	} + +	lradc->vref_mv = of_cfg->vref_mv; + +	platform_set_drvdata(pdev, iio); + +	init_completion(&lradc->completion); +	mutex_init(&lradc->lock); + +	iio->name = pdev->name; +	iio->dev.parent = &pdev->dev; +	iio->info = &mxs_lradc_iio_info; +	iio->modes = INDIO_DIRECT_MODE; +	iio->channels = mxs_lradc_chan_spec; +	iio->num_channels = ARRAY_SIZE(mxs_lradc_chan_spec); +	iio->masklength = LRADC_MAX_TOTAL_CHANS; + +	ret = iio_triggered_buffer_setup(iio, &iio_pollfunc_store_time, +				&mxs_lradc_trigger_handler, +				&mxs_lradc_buffer_ops); +	if (ret) +		return ret; + +	ret = mxs_lradc_trigger_init(iio); +	if (ret) +		goto err_trig; + +	/* Populate available ADC input ranges */ +	for (i = 0; i < LRADC_MAX_TOTAL_CHANS; i++) { +		for (s = 0; s < ARRAY_SIZE(lradc->scale_avail[i]); s++) { +			/* +			 * [s=0] = optional divider by two disabled (default) +			 * [s=1] = optional divider by two enabled +			 * +			 * The scale is calculated by doing: +			 *   Vref >> (realbits - s) +			 * which multiplies by two on the second component +			 * of the array. +			 */ +			scale_uv = ((u64)lradc->vref_mv[i] * 100000000) >> +				   (LRADC_RESOLUTION - s); +			lradc->scale_avail[i][s].nano = +					do_div(scale_uv, 100000000) * 10; +			lradc->scale_avail[i][s].integer = scale_uv; +		} +	} + +	/* Configure the hardware. */ +	ret = mxs_lradc_hw_init(lradc); +	if (ret) +		goto err_dev; + +	/* Register the touchscreen input device. */ +	if (touch_ret == 0) { +		ret = mxs_lradc_ts_register(lradc); +		if (ret) +			goto err_ts_register; +	} + +	/* Register IIO device. */ +	ret = iio_device_register(iio); +	if (ret) { +		dev_err(dev, "Failed to register IIO device\n"); +		goto err_ts; +	} + +	return 0; + +err_ts: +	mxs_lradc_ts_unregister(lradc); +err_ts_register: +	mxs_lradc_hw_stop(lradc); +err_dev: +	mxs_lradc_trigger_remove(iio); +err_trig: +	iio_triggered_buffer_cleanup(iio); +	return ret; +} + +static int mxs_lradc_remove(struct platform_device *pdev) +{ +	struct iio_dev *iio = platform_get_drvdata(pdev); +	struct mxs_lradc *lradc = iio_priv(iio); + +	iio_device_unregister(iio); +	mxs_lradc_ts_unregister(lradc); +	mxs_lradc_hw_stop(lradc); +	mxs_lradc_trigger_remove(iio); +	iio_triggered_buffer_cleanup(iio); + +	clk_disable_unprepare(lradc->clk); +	return 0; +} + +static struct platform_driver mxs_lradc_driver = { +	.driver	= { +		.name	= DRIVER_NAME, +		.owner	= THIS_MODULE, +		.of_match_table = mxs_lradc_dt_ids, +	}, +	.probe	= mxs_lradc_probe, +	.remove	= mxs_lradc_remove, +}; + +module_platform_driver(mxs_lradc_driver); + +MODULE_AUTHOR("Marek Vasut <marex@denx.de>"); +MODULE_DESCRIPTION("Freescale i.MX28 LRADC driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:" DRIVER_NAME); diff --git a/drivers/staging/iio/adc/spear_adc.c b/drivers/staging/iio/adc/spear_adc.c new file mode 100644 index 00000000000..c5492ba5075 --- /dev/null +++ b/drivers/staging/iio/adc/spear_adc.c @@ -0,0 +1,401 @@ +/* + * ST SPEAr ADC driver + * + * Copyright 2012 Stefan Roese <sr@denx.de> + * + * Licensed under the GPL-2. + */ + +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/interrupt.h> +#include <linux/device.h> +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/io.h> +#include <linux/clk.h> +#include <linux/err.h> +#include <linux/completion.h> +#include <linux/of.h> +#include <linux/of_address.h> + +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> + +/* SPEAR registers definitions */ +#define SPEAR600_ADC_SCAN_RATE_LO(x)	((x) & 0xFFFF) +#define SPEAR600_ADC_SCAN_RATE_HI(x)	(((x) >> 0x10) & 0xFFFF) +#define SPEAR_ADC_CLK_LOW(x)		(((x) & 0xf) << 0) +#define SPEAR_ADC_CLK_HIGH(x)		(((x) & 0xf) << 4) + +/* Bit definitions for SPEAR_ADC_STATUS */ +#define SPEAR_ADC_STATUS_START_CONVERSION	(1 << 0) +#define SPEAR_ADC_STATUS_CHANNEL_NUM(x)		((x) << 1) +#define SPEAR_ADC_STATUS_ADC_ENABLE		(1 << 4) +#define SPEAR_ADC_STATUS_AVG_SAMPLE(x)		((x) << 5) +#define SPEAR_ADC_STATUS_VREF_INTERNAL		(1 << 9) + +#define SPEAR_ADC_DATA_MASK		0x03ff +#define SPEAR_ADC_DATA_BITS		10 + +#define SPEAR_ADC_MOD_NAME "spear-adc" + +#define SPEAR_ADC_CHANNEL_NUM		8 + +#define SPEAR_ADC_CLK_MIN			2500000 +#define SPEAR_ADC_CLK_MAX			20000000 + +struct adc_regs_spear3xx { +	u32 status; +	u32 average; +	u32 scan_rate; +	u32 clk;	/* Not avail for 1340 & 1310 */ +	u32 ch_ctrl[SPEAR_ADC_CHANNEL_NUM]; +	u32 ch_data[SPEAR_ADC_CHANNEL_NUM]; +}; + +struct chan_data { +	u32 lsb; +	u32 msb; +}; + +struct adc_regs_spear6xx { +	u32 status; +	u32 pad[2]; +	u32 clk; +	u32 ch_ctrl[SPEAR_ADC_CHANNEL_NUM]; +	struct chan_data ch_data[SPEAR_ADC_CHANNEL_NUM]; +	u32 scan_rate_lo; +	u32 scan_rate_hi; +	struct chan_data average; +}; + +struct spear_adc_state { +	struct device_node *np; +	struct adc_regs_spear3xx __iomem *adc_base_spear3xx; +	struct adc_regs_spear6xx __iomem *adc_base_spear6xx; +	struct clk *clk; +	struct completion completion; +	u32 current_clk; +	u32 sampling_freq; +	u32 avg_samples; +	u32 vref_external; +	u32 value; +}; + +/* + * Functions to access some SPEAr ADC register. Abstracted into + * static inline functions, because of different register offsets + * on different SoC variants (SPEAr300 vs SPEAr600 etc). + */ +static void spear_adc_set_status(struct spear_adc_state *st, u32 val) +{ +	__raw_writel(val, &st->adc_base_spear6xx->status); +} + +static void spear_adc_set_clk(struct spear_adc_state *st, u32 val) +{ +	u32 clk_high, clk_low, count; +	u32 apb_clk = clk_get_rate(st->clk); + +	count = (apb_clk + val - 1) / val; +	clk_low = count / 2; +	clk_high = count - clk_low; +	st->current_clk = apb_clk / count; + +	__raw_writel(SPEAR_ADC_CLK_LOW(clk_low) | SPEAR_ADC_CLK_HIGH(clk_high), +		     &st->adc_base_spear6xx->clk); +} + +static void spear_adc_set_ctrl(struct spear_adc_state *st, int n, +			       u32 val) +{ +	__raw_writel(val, &st->adc_base_spear6xx->ch_ctrl[n]); +} + +static u32 spear_adc_get_average(struct spear_adc_state *st) +{ +	if (of_device_is_compatible(st->np, "st,spear600-adc")) { +		return __raw_readl(&st->adc_base_spear6xx->average.msb) & +			SPEAR_ADC_DATA_MASK; +	} else { +		return __raw_readl(&st->adc_base_spear3xx->average) & +			SPEAR_ADC_DATA_MASK; +	} +} + +static void spear_adc_set_scanrate(struct spear_adc_state *st, u32 rate) +{ +	if (of_device_is_compatible(st->np, "st,spear600-adc")) { +		__raw_writel(SPEAR600_ADC_SCAN_RATE_LO(rate), +			     &st->adc_base_spear6xx->scan_rate_lo); +		__raw_writel(SPEAR600_ADC_SCAN_RATE_HI(rate), +			     &st->adc_base_spear6xx->scan_rate_hi); +	} else { +		__raw_writel(rate, &st->adc_base_spear3xx->scan_rate); +	} +} + +static int spear_adc_read_raw(struct iio_dev *indio_dev, +			      struct iio_chan_spec const *chan, +			      int *val, +			      int *val2, +			      long mask) +{ +	struct spear_adc_state *st = iio_priv(indio_dev); +	u32 status; + +	switch (mask) { +	case IIO_CHAN_INFO_RAW: +		mutex_lock(&indio_dev->mlock); + +		status = SPEAR_ADC_STATUS_CHANNEL_NUM(chan->channel) | +			SPEAR_ADC_STATUS_AVG_SAMPLE(st->avg_samples) | +			SPEAR_ADC_STATUS_START_CONVERSION | +			SPEAR_ADC_STATUS_ADC_ENABLE; +		if (st->vref_external == 0) +			status |= SPEAR_ADC_STATUS_VREF_INTERNAL; + +		spear_adc_set_status(st, status); +		wait_for_completion(&st->completion); /* set by ISR */ +		*val = st->value; + +		mutex_unlock(&indio_dev->mlock); + +		return IIO_VAL_INT; + +	case IIO_CHAN_INFO_SCALE: +		*val = st->vref_external; +		*val2 = SPEAR_ADC_DATA_BITS; +		return IIO_VAL_FRACTIONAL_LOG2; +	case IIO_CHAN_INFO_SAMP_FREQ: +		*val = st->current_clk; +		return IIO_VAL_INT; +	} + +	return -EINVAL; +} + +static int spear_adc_write_raw(struct iio_dev *indio_dev, +			       struct iio_chan_spec const *chan, +			       int val, +			       int val2, +			       long mask) +{ +	struct spear_adc_state *st = iio_priv(indio_dev); +	int ret = 0; + +	if (mask != IIO_CHAN_INFO_SAMP_FREQ) +		return -EINVAL; + +	mutex_lock(&indio_dev->mlock); + +	if ((val < SPEAR_ADC_CLK_MIN) || +		(val > SPEAR_ADC_CLK_MAX) || +		(val2 != 0)) { +		ret = -EINVAL; +		goto out; +	} + +	spear_adc_set_clk(st, val); + +out: +	mutex_unlock(&indio_dev->mlock); +	return ret; +} + +#define SPEAR_ADC_CHAN(idx) {				\ +	.type = IIO_VOLTAGE,				\ +	.indexed = 1,					\ +	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),	\ +	.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),	\ +	.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),\ +	.channel = idx,					\ +} + +static const struct iio_chan_spec spear_adc_iio_channels[] = { +	SPEAR_ADC_CHAN(0), +	SPEAR_ADC_CHAN(1), +	SPEAR_ADC_CHAN(2), +	SPEAR_ADC_CHAN(3), +	SPEAR_ADC_CHAN(4), +	SPEAR_ADC_CHAN(5), +	SPEAR_ADC_CHAN(6), +	SPEAR_ADC_CHAN(7), +}; + +static irqreturn_t spear_adc_isr(int irq, void *dev_id) +{ +	struct spear_adc_state *st = (struct spear_adc_state *)dev_id; + +	/* Read value to clear IRQ */ +	st->value = spear_adc_get_average(st); +	complete(&st->completion); + +	return IRQ_HANDLED; +} + +static int spear_adc_configure(struct spear_adc_state *st) +{ +	int i; + +	/* Reset ADC core */ +	spear_adc_set_status(st, 0); +	__raw_writel(0, &st->adc_base_spear6xx->clk); +	for (i = 0; i < 8; i++) +		spear_adc_set_ctrl(st, i, 0); +	spear_adc_set_scanrate(st, 0); + +	spear_adc_set_clk(st, st->sampling_freq); + +	return 0; +} + +static const struct iio_info spear_adc_info = { +	.read_raw = &spear_adc_read_raw, +	.write_raw = &spear_adc_write_raw, +	.driver_module = THIS_MODULE, +}; + +static int spear_adc_probe(struct platform_device *pdev) +{ +	struct device_node *np = pdev->dev.of_node; +	struct device *dev = &pdev->dev; +	struct spear_adc_state *st; +	struct iio_dev *indio_dev = NULL; +	int ret = -ENODEV; +	int irq; + +	indio_dev = devm_iio_device_alloc(dev, sizeof(struct spear_adc_state)); +	if (!indio_dev) { +		dev_err(dev, "failed allocating iio device\n"); +		return -ENOMEM; +	} + +	st = iio_priv(indio_dev); +	st->np = np; + +	/* +	 * SPEAr600 has a different register layout than other SPEAr SoC's +	 * (e.g. SPEAr3xx). Let's provide two register base addresses +	 * to support multi-arch kernels. +	 */ +	st->adc_base_spear6xx = of_iomap(np, 0); +	if (!st->adc_base_spear6xx) { +		dev_err(dev, "failed mapping memory\n"); +		return -ENOMEM; +	} +	st->adc_base_spear3xx = +		(struct adc_regs_spear3xx __iomem *)st->adc_base_spear6xx; + +	st->clk = clk_get(dev, NULL); +	if (IS_ERR(st->clk)) { +		dev_err(dev, "failed getting clock\n"); +		goto errout1; +	} + +	ret = clk_prepare_enable(st->clk); +	if (ret) { +		dev_err(dev, "failed enabling clock\n"); +		goto errout2; +	} + +	irq = platform_get_irq(pdev, 0); +	if (irq <= 0) { +		dev_err(dev, "failed getting interrupt resource\n"); +		ret = -EINVAL; +		goto errout3; +	} + +	ret = devm_request_irq(dev, irq, spear_adc_isr, 0, SPEAR_ADC_MOD_NAME, +			       st); +	if (ret < 0) { +		dev_err(dev, "failed requesting interrupt\n"); +		goto errout3; +	} + +	if (of_property_read_u32(np, "sampling-frequency", +				 &st->sampling_freq)) { +		dev_err(dev, "sampling-frequency missing in DT\n"); +		ret = -EINVAL; +		goto errout3; +	} + +	/* +	 * Optional avg_samples defaults to 0, resulting in single data +	 * conversion +	 */ +	of_property_read_u32(np, "average-samples", &st->avg_samples); + +	/* +	 * Optional vref_external defaults to 0, resulting in internal vref +	 * selection +	 */ +	of_property_read_u32(np, "vref-external", &st->vref_external); + +	spear_adc_configure(st); + +	platform_set_drvdata(pdev, indio_dev); + +	init_completion(&st->completion); + +	indio_dev->name = SPEAR_ADC_MOD_NAME; +	indio_dev->dev.parent = dev; +	indio_dev->info = &spear_adc_info; +	indio_dev->modes = INDIO_DIRECT_MODE; +	indio_dev->channels = spear_adc_iio_channels; +	indio_dev->num_channels = ARRAY_SIZE(spear_adc_iio_channels); + +	ret = iio_device_register(indio_dev); +	if (ret) +		goto errout3; + +	dev_info(dev, "SPEAR ADC driver loaded, IRQ %d\n", irq); + +	return 0; + +errout3: +	clk_disable_unprepare(st->clk); +errout2: +	clk_put(st->clk); +errout1: +	iounmap(st->adc_base_spear6xx); +	return ret; +} + +static int spear_adc_remove(struct platform_device *pdev) +{ +	struct iio_dev *indio_dev = platform_get_drvdata(pdev); +	struct spear_adc_state *st = iio_priv(indio_dev); + +	iio_device_unregister(indio_dev); +	clk_disable_unprepare(st->clk); +	clk_put(st->clk); +	iounmap(st->adc_base_spear6xx); + +	return 0; +} + +#ifdef CONFIG_OF +static const struct of_device_id spear_adc_dt_ids[] = { +	{ .compatible = "st,spear600-adc", }, +	{ /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, spear_adc_dt_ids); +#endif + +static struct platform_driver spear_adc_driver = { +	.probe		= spear_adc_probe, +	.remove		= spear_adc_remove, +	.driver		= { +		.name	= SPEAR_ADC_MOD_NAME, +		.owner	= THIS_MODULE, +		.of_match_table = of_match_ptr(spear_adc_dt_ids), +	}, +}; + +module_platform_driver(spear_adc_driver); + +MODULE_AUTHOR("Stefan Roese <sr@denx.de>"); +MODULE_DESCRIPTION("SPEAr ADC driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/staging/iio/addac/Kconfig b/drivers/staging/iio/addac/Kconfig new file mode 100644 index 00000000000..e6795e0bed1 --- /dev/null +++ b/drivers/staging/iio/addac/Kconfig @@ -0,0 +1,28 @@ +# +# ADDAC drivers +# +menu "Analog digital bi-direction converters" + +config ADT7316 +	tristate "Analog Devices ADT7316/7/8 ADT7516/7/9 temperature sensor, ADC and DAC driver" +	depends on GPIOLIB +	help +	  Say yes here to build support for Analog Devices ADT7316, ADT7317, ADT7318 +	  and ADT7516, ADT7517, ADT7519 temperature sensors, ADC and DAC. + +config ADT7316_SPI +	tristate "support SPI bus connection" +	depends on SPI && ADT7316 +	default y +	help +	  Say yes here to build SPI bus support for Analog Devices ADT7316/7/8 +	  and ADT7516/7/9. + +config ADT7316_I2C +	tristate "support I2C bus connection" +	depends on I2C && ADT7316 +	help +	  Say yes here to build I2C bus support for Analog Devices ADT7316/7/8 +	  and ADT7516/7/9. + +endmenu diff --git a/drivers/staging/iio/addac/Makefile b/drivers/staging/iio/addac/Makefile new file mode 100644 index 00000000000..4c768613369 --- /dev/null +++ b/drivers/staging/iio/addac/Makefile @@ -0,0 +1,7 @@ +# +# Makefile for industrial I/O ADDAC drivers +# + +obj-$(CONFIG_ADT7316) += adt7316.o +obj-$(CONFIG_ADT7316_SPI) += adt7316-spi.o +obj-$(CONFIG_ADT7316_I2C) += adt7316-i2c.o diff --git a/drivers/staging/iio/addac/adt7316-i2c.c b/drivers/staging/iio/addac/adt7316-i2c.c new file mode 100644 index 00000000000..75ddd4f801a --- /dev/null +++ b/drivers/staging/iio/addac/adt7316-i2c.c @@ -0,0 +1,136 @@ +/* + * I2C bus driver for ADT7316/7/8 ADT7516/7/9 digital temperature + * sensor, ADC and DAC + * + * Copyright 2010 Analog Devices Inc. + * + * Licensed under the GPL-2 or later. + */ + +#include <linux/device.h> +#include <linux/kernel.h> +#include <linux/i2c.h> +#include <linux/interrupt.h> +#include <linux/module.h> + +#include "adt7316.h" + +/* + * adt7316 register access by I2C + */ +static int adt7316_i2c_read(void *client, u8 reg, u8 *data) +{ +	struct i2c_client *cl = client; +	int ret = 0; + +	ret = i2c_smbus_write_byte(cl, reg); +	if (ret < 0) { +		dev_err(&cl->dev, "I2C fail to select reg\n"); +		return ret; +	} + +	ret = i2c_smbus_read_byte(client); +	if (ret < 0) { +		dev_err(&cl->dev, "I2C read error\n"); +		return ret; +	} + +	return 0; +} + +static int adt7316_i2c_write(void *client, u8 reg, u8 data) +{ +	struct i2c_client *cl = client; +	int ret = 0; + +	ret = i2c_smbus_write_byte_data(cl, reg, data); +	if (ret < 0) +		dev_err(&cl->dev, "I2C write error\n"); + +	return ret; +} + +static int adt7316_i2c_multi_read(void *client, u8 reg, u8 count, u8 *data) +{ +	struct i2c_client *cl = client; +	int i, ret = 0; + +	if (count > ADT7316_REG_MAX_ADDR) +		count = ADT7316_REG_MAX_ADDR; + +	for (i = 0; i < count; i++) { +		ret = adt7316_i2c_read(cl, reg, &data[i]); +		if (ret < 0) { +			dev_err(&cl->dev, "I2C multi read error\n"); +			return ret; +		} +	} + +	return 0; +} + +static int adt7316_i2c_multi_write(void *client, u8 reg, u8 count, u8 *data) +{ +	struct i2c_client *cl = client; +	int i, ret = 0; + +	if (count > ADT7316_REG_MAX_ADDR) +		count = ADT7316_REG_MAX_ADDR; + +	for (i = 0; i < count; i++) { +		ret = adt7316_i2c_write(cl, reg, data[i]); +		if (ret < 0) { +			dev_err(&cl->dev, "I2C multi write error\n"); +			return ret; +		} +	} + +	return 0; +} + +/* + * device probe and remove + */ + +static int adt7316_i2c_probe(struct i2c_client *client, +		const struct i2c_device_id *id) +{ +	struct adt7316_bus bus = { +		.client = client, +		.irq = client->irq, +		.irq_flags = IRQF_TRIGGER_LOW, +		.read = adt7316_i2c_read, +		.write = adt7316_i2c_write, +		.multi_read = adt7316_i2c_multi_read, +		.multi_write = adt7316_i2c_multi_write, +	}; + +	return adt7316_probe(&client->dev, &bus, id->name); +} + +static const struct i2c_device_id adt7316_i2c_id[] = { +	{ "adt7316", 0 }, +	{ "adt7317", 0 }, +	{ "adt7318", 0 }, +	{ "adt7516", 0 }, +	{ "adt7517", 0 }, +	{ "adt7519", 0 }, +	{ } +}; + +MODULE_DEVICE_TABLE(i2c, adt7316_i2c_id); + +static struct i2c_driver adt7316_driver = { +	.driver = { +		.name = "adt7316", +		.pm = ADT7316_PM_OPS, +		.owner  = THIS_MODULE, +	}, +	.probe = adt7316_i2c_probe, +	.id_table = adt7316_i2c_id, +}; +module_i2c_driver(adt7316_driver); + +MODULE_AUTHOR("Sonic Zhang <sonic.zhang@analog.com>"); +MODULE_DESCRIPTION("I2C bus driver for Analog Devices ADT7316/7/9 and ADT7516/7/8 digital temperature sensor, ADC and DAC"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/iio/addac/adt7316-spi.c b/drivers/staging/iio/addac/adt7316-spi.c new file mode 100644 index 00000000000..e480abb72e4 --- /dev/null +++ b/drivers/staging/iio/addac/adt7316-spi.c @@ -0,0 +1,144 @@ +/* + * API bus driver for ADT7316/7/8 ADT7516/7/9 digital temperature + * sensor, ADC and DAC + * + * Copyright 2010 Analog Devices Inc. + * + * Licensed under the GPL-2 or later. + */ + +#include <linux/device.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/interrupt.h> +#include <linux/spi/spi.h> + +#include "adt7316.h" + +#define ADT7316_SPI_MAX_FREQ_HZ		5000000 +#define ADT7316_SPI_CMD_READ		0x91 +#define ADT7316_SPI_CMD_WRITE		0x90 + +/* + * adt7316 register access by SPI + */ + +static int adt7316_spi_multi_read(void *client, u8 reg, u8 count, u8 *data) +{ +	struct spi_device *spi_dev = client; +	u8 cmd[2]; +	int ret = 0; + +	if (count > ADT7316_REG_MAX_ADDR) +		count = ADT7316_REG_MAX_ADDR; + +	cmd[0] = ADT7316_SPI_CMD_WRITE; +	cmd[1] = reg; + +	ret = spi_write(spi_dev, cmd, 2); +	if (ret < 0) { +		dev_err(&spi_dev->dev, "SPI fail to select reg\n"); +		return ret; +	} + +	cmd[0] = ADT7316_SPI_CMD_READ; + +	ret = spi_write_then_read(spi_dev, cmd, 1, data, count); +	if (ret < 0) { +		dev_err(&spi_dev->dev, "SPI read data error\n"); +		return ret; +	} + +	return 0; +} + +static int adt7316_spi_multi_write(void *client, u8 reg, u8 count, u8 *data) +{ +	struct spi_device *spi_dev = client; +	u8 buf[ADT7316_REG_MAX_ADDR + 2]; +	int i, ret = 0; + +	if (count > ADT7316_REG_MAX_ADDR) +		count = ADT7316_REG_MAX_ADDR; + +	buf[0] = ADT7316_SPI_CMD_WRITE; +	buf[1] = reg; +	for (i = 0; i < count; i++) +		buf[i + 2] = data[i]; + +	ret = spi_write(spi_dev, buf, count + 2); +	if (ret < 0) { +		dev_err(&spi_dev->dev, "SPI write error\n"); +		return ret; +	} + +	return ret; +} + +static int adt7316_spi_read(void *client, u8 reg, u8 *data) +{ +	return adt7316_spi_multi_read(client, reg, 1, data); +} + +static int adt7316_spi_write(void *client, u8 reg, u8 val) +{ +	return adt7316_spi_multi_write(client, reg, 1, &val); +} + +/* + * device probe and remove + */ + +static int adt7316_spi_probe(struct spi_device *spi_dev) +{ +	struct adt7316_bus bus = { +		.client = spi_dev, +		.irq = spi_dev->irq, +		.irq_flags = IRQF_TRIGGER_LOW, +		.read = adt7316_spi_read, +		.write = adt7316_spi_write, +		.multi_read = adt7316_spi_multi_read, +		.multi_write = adt7316_spi_multi_write, +	}; + +	/* don't exceed max specified SPI CLK frequency */ +	if (spi_dev->max_speed_hz > ADT7316_SPI_MAX_FREQ_HZ) { +		dev_err(&spi_dev->dev, "SPI CLK %d Hz?\n", +			spi_dev->max_speed_hz); +		return -EINVAL; +	} + +	/* switch from default I2C protocol to SPI protocol */ +	adt7316_spi_write(spi_dev, 0, 0); +	adt7316_spi_write(spi_dev, 0, 0); +	adt7316_spi_write(spi_dev, 0, 0); + +	return adt7316_probe(&spi_dev->dev, &bus, spi_dev->modalias); +} + +static const struct spi_device_id adt7316_spi_id[] = { +	{ "adt7316", 0 }, +	{ "adt7317", 0 }, +	{ "adt7318", 0 }, +	{ "adt7516", 0 }, +	{ "adt7517", 0 }, +	{ "adt7519", 0 }, +	{ } +}; + +MODULE_DEVICE_TABLE(spi, adt7316_spi_id); + +static struct spi_driver adt7316_driver = { +	.driver = { +		.name = "adt7316", +		.pm = ADT7316_PM_OPS, +		.owner = THIS_MODULE, +	}, +	.probe = adt7316_spi_probe, +	.id_table = adt7316_spi_id, +}; +module_spi_driver(adt7316_driver); + +MODULE_AUTHOR("Sonic Zhang <sonic.zhang@analog.com>"); +MODULE_DESCRIPTION("SPI bus driver for Analog Devices ADT7316/7/8 and ADT7516/7/9 digital temperature sensor, ADC and DAC"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/iio/addac/adt7316.c b/drivers/staging/iio/addac/adt7316.c new file mode 100644 index 00000000000..5f1770e6f6c --- /dev/null +++ b/drivers/staging/iio/addac/adt7316.c @@ -0,0 +1,2190 @@ +/* + * ADT7316 digital temperature sensor driver supporting ADT7316/7/8 ADT7516/7/9 + * + * + * Copyright 2010 Analog Devices Inc. + * + * Licensed under the GPL-2 or later. + */ + +#include <linux/interrupt.h> +#include <linux/gpio.h> +#include <linux/workqueue.h> +#include <linux/device.h> +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/sysfs.h> +#include <linux/list.h> +#include <linux/i2c.h> +#include <linux/rtc.h> +#include <linux/module.h> + +#include <linux/iio/iio.h> +#include <linux/iio/events.h> +#include <linux/iio/sysfs.h> +#include "adt7316.h" + +/* + * ADT7316 registers definition + */ +#define ADT7316_INT_STAT1		0x0 +#define ADT7316_INT_STAT2		0x1 +#define ADT7316_LSB_IN_TEMP_VDD		0x3 +#define ADT7316_LSB_IN_TEMP_MASK	0x3 +#define ADT7316_LSB_VDD_MASK		0xC +#define ADT7316_LSB_VDD_OFFSET		2 +#define ADT7316_LSB_EX_TEMP_AIN		0x4 +#define ADT7316_LSB_EX_TEMP_MASK	0x3 +#define ADT7516_LSB_AIN_SHIFT		2 +#define ADT7316_AD_MSB_DATA_BASE        0x6 +#define ADT7316_AD_MSB_DATA_REGS        3 +#define ADT7516_AD_MSB_DATA_REGS        6 +#define ADT7316_MSB_VDD			0x6 +#define ADT7316_MSB_IN_TEMP		0x7 +#define ADT7316_MSB_EX_TEMP		0x8 +#define ADT7516_MSB_AIN1		0x8 +#define ADT7516_MSB_AIN2		0x9 +#define ADT7516_MSB_AIN3		0xA +#define ADT7516_MSB_AIN4		0xB +#define ADT7316_DA_DATA_BASE		0x10 +#define ADT7316_DA_MSB_DATA_REGS	4 +#define ADT7316_LSB_DAC_A		0x10 +#define ADT7316_MSB_DAC_A		0x11 +#define ADT7316_LSB_DAC_B		0x12 +#define ADT7316_MSB_DAC_B		0x13 +#define ADT7316_LSB_DAC_C		0x14 +#define ADT7316_MSB_DAC_C		0x15 +#define ADT7316_LSB_DAC_D		0x16 +#define ADT7316_MSB_DAC_D		0x17 +#define ADT7316_CONFIG1			0x18 +#define ADT7316_CONFIG2			0x19 +#define ADT7316_CONFIG3			0x1A +#define ADT7316_LDAC_CONFIG		0x1B +#define ADT7316_DAC_CONFIG		0x1C +#define ADT7316_INT_MASK1		0x1D +#define ADT7316_INT_MASK2		0x1E +#define ADT7316_IN_TEMP_OFFSET		0x1F +#define ADT7316_EX_TEMP_OFFSET		0x20 +#define ADT7316_IN_ANALOG_TEMP_OFFSET	0x21 +#define ADT7316_EX_ANALOG_TEMP_OFFSET	0x22 +#define ADT7316_VDD_HIGH		0x23 +#define ADT7316_VDD_LOW			0x24 +#define ADT7316_IN_TEMP_HIGH		0x25 +#define ADT7316_IN_TEMP_LOW		0x26 +#define ADT7316_EX_TEMP_HIGH		0x27 +#define ADT7316_EX_TEMP_LOW		0x28 +#define ADT7516_AIN2_HIGH		0x2B +#define ADT7516_AIN2_LOW		0x2C +#define ADT7516_AIN3_HIGH		0x2D +#define ADT7516_AIN3_LOW		0x2E +#define ADT7516_AIN4_HIGH		0x2F +#define ADT7516_AIN4_LOW		0x30 +#define ADT7316_DEVICE_ID		0x4D +#define ADT7316_MANUFACTURE_ID		0x4E +#define ADT7316_DEVICE_REV		0x4F +#define ADT7316_SPI_LOCK_STAT		0x7F + +/* + * ADT7316 config1 + */ +#define ADT7316_EN			0x1 +#define ADT7516_SEL_EX_TEMP		0x4 +#define ADT7516_SEL_AIN1_2_EX_TEMP_MASK	0x6 +#define ADT7516_SEL_AIN3		0x8 +#define ADT7316_INT_EN			0x20 +#define ADT7316_INT_POLARITY		0x40 +#define ADT7316_PD			0x80 + +/* + * ADT7316 config2 + */ +#define ADT7316_AD_SINGLE_CH_MASK	0x3 +#define ADT7516_AD_SINGLE_CH_MASK	0x7 +#define ADT7316_AD_SINGLE_CH_VDD	0 +#define ADT7316_AD_SINGLE_CH_IN		1 +#define ADT7316_AD_SINGLE_CH_EX		2 +#define ADT7516_AD_SINGLE_CH_AIN1	2 +#define ADT7516_AD_SINGLE_CH_AIN2	3 +#define ADT7516_AD_SINGLE_CH_AIN3	4 +#define ADT7516_AD_SINGLE_CH_AIN4	5 +#define ADT7316_AD_SINGLE_CH_MODE	0x10 +#define ADT7316_DISABLE_AVERAGING	0x20 +#define ADT7316_EN_SMBUS_TIMEOUT	0x40 +#define ADT7316_RESET			0x80 + +/* + * ADT7316 config3 + */ +#define ADT7316_ADCLK_22_5		0x1 +#define ADT7316_DA_HIGH_RESOLUTION	0x2 +#define ADT7316_DA_EN_VIA_DAC_LDCA	0x4 +#define ADT7516_AIN_IN_VREF		0x10 +#define ADT7316_EN_IN_TEMP_PROP_DACA	0x20 +#define ADT7316_EN_EX_TEMP_PROP_DACB	0x40 + +/* + * ADT7316 DAC config + */ +#define ADT7316_DA_2VREF_CH_MASK	0xF +#define ADT7316_DA_EN_MODE_MASK		0x30 +#define ADT7316_DA_EN_MODE_SINGLE	0x00 +#define ADT7316_DA_EN_MODE_AB_CD	0x10 +#define ADT7316_DA_EN_MODE_ABCD		0x20 +#define ADT7316_DA_EN_MODE_LDAC		0x30 +#define ADT7316_VREF_BYPASS_DAC_AB	0x40 +#define ADT7316_VREF_BYPASS_DAC_CD	0x80 + +/* + * ADT7316 LDAC config + */ +#define ADT7316_LDAC_EN_DA_MASK		0xF +#define ADT7316_DAC_IN_VREF		0x10 +#define ADT7516_DAC_AB_IN_VREF		0x10 +#define ADT7516_DAC_CD_IN_VREF		0x20 +#define ADT7516_DAC_IN_VREF_OFFSET	4 +#define ADT7516_DAC_IN_VREF_MASK	0x30 + +/* + * ADT7316 INT_MASK2 + */ +#define ADT7316_INT_MASK2_VDD		0x10 + +/* + * ADT7316 value masks + */ +#define ADT7316_VALUE_MASK		0xfff +#define ADT7316_T_VALUE_SIGN		0x400 +#define ADT7316_T_VALUE_FLOAT_OFFSET	2 +#define ADT7316_T_VALUE_FLOAT_MASK	0x2 + +/* + * Chip ID + */ +#define ID_ADT7316		0x1 +#define ID_ADT7317		0x2 +#define ID_ADT7318		0x3 +#define ID_ADT7516		0x11 +#define ID_ADT7517		0x12 +#define ID_ADT7519		0x14 + +#define ID_FAMILY_MASK		0xF0 +#define ID_ADT73XX		0x0 +#define ID_ADT75XX		0x10 + +/* + * struct adt7316_chip_info - chip specific information + */ + +struct adt7316_chip_info { +	struct adt7316_bus	bus; +	u16			ldac_pin; +	u16			int_mask;	/* 0x2f */ +	u8			config1; +	u8			config2; +	u8			config3; +	u8			dac_config;	/* DAC config */ +	u8			ldac_config;	/* LDAC config */ +	u8			dac_bits;	/* 8, 10, 12 */ +	u8			id;		/* chip id */ +}; + +/* + * Logic interrupt mask for user application to enable + * interrupts. + */ +#define ADT7316_IN_TEMP_HIGH_INT_MASK	0x1 +#define ADT7316_IN_TEMP_LOW_INT_MASK	0x2 +#define ADT7316_EX_TEMP_HIGH_INT_MASK	0x4 +#define ADT7316_EX_TEMP_LOW_INT_MASK	0x8 +#define ADT7316_EX_TEMP_FAULT_INT_MASK	0x10 +#define ADT7516_AIN1_INT_MASK		0x4 +#define ADT7516_AIN2_INT_MASK		0x20 +#define ADT7516_AIN3_INT_MASK		0x40 +#define ADT7516_AIN4_INT_MASK		0x80 +#define ADT7316_VDD_INT_MASK		0x100 +#define ADT7316_TEMP_INT_MASK		0x1F +#define ADT7516_AIN_INT_MASK		0xE0 +#define ADT7316_TEMP_AIN_INT_MASK	\ +	(ADT7316_TEMP_INT_MASK) + +/* + * struct adt7316_chip_info - chip specific information + */ + +struct adt7316_limit_regs { +	u16	data_high; +	u16	data_low; +}; + +static ssize_t adt7316_show_enabled(struct device *dev, +		struct device_attribute *attr, +		char *buf) +{ +	struct iio_dev *dev_info = dev_to_iio_dev(dev); +	struct adt7316_chip_info *chip = iio_priv(dev_info); + +	return sprintf(buf, "%d\n", !!(chip->config1 & ADT7316_EN)); +} + +static ssize_t _adt7316_store_enabled(struct adt7316_chip_info *chip, +		int enable) +{ +	u8 config1; +	int ret; + +	if (enable) +		config1 = chip->config1 | ADT7316_EN; +	else +		config1 = chip->config1 & ~ADT7316_EN; + +	ret = chip->bus.write(chip->bus.client, ADT7316_CONFIG1, config1); +	if (ret) +		return -EIO; + +	chip->config1 = config1; + +	return ret; + +} + +static ssize_t adt7316_store_enabled(struct device *dev, +		struct device_attribute *attr, +		const char *buf, +		size_t len) +{ +	struct iio_dev *dev_info = dev_to_iio_dev(dev); +	struct adt7316_chip_info *chip = iio_priv(dev_info); +	int enable; + +	if (buf[0] == '1') +		enable = 1; +	else +		enable = 0; + +	if (_adt7316_store_enabled(chip, enable) < 0) +		return -EIO; +	else +		return len; +} + +static IIO_DEVICE_ATTR(enabled, S_IRUGO | S_IWUSR, +		adt7316_show_enabled, +		adt7316_store_enabled, +		0); + +static ssize_t adt7316_show_select_ex_temp(struct device *dev, +		struct device_attribute *attr, +		char *buf) +{ +	struct iio_dev *dev_info = dev_to_iio_dev(dev); +	struct adt7316_chip_info *chip = iio_priv(dev_info); + +	if ((chip->id & ID_FAMILY_MASK) != ID_ADT75XX) +		return -EPERM; + +	return sprintf(buf, "%d\n", !!(chip->config1 & ADT7516_SEL_EX_TEMP)); +} + +static ssize_t adt7316_store_select_ex_temp(struct device *dev, +		struct device_attribute *attr, +		const char *buf, +		size_t len) +{ +	struct iio_dev *dev_info = dev_to_iio_dev(dev); +	struct adt7316_chip_info *chip = iio_priv(dev_info); +	u8 config1; +	int ret; + +	if ((chip->id & ID_FAMILY_MASK) != ID_ADT75XX) +		return -EPERM; + +	config1 = chip->config1 & (~ADT7516_SEL_EX_TEMP); +	if (buf[0] == '1') +		config1 |= ADT7516_SEL_EX_TEMP; + +	ret = chip->bus.write(chip->bus.client, ADT7316_CONFIG1, config1); +	if (ret) +		return -EIO; + +	chip->config1 = config1; + +	return len; +} + +static IIO_DEVICE_ATTR(select_ex_temp, S_IRUGO | S_IWUSR, +		adt7316_show_select_ex_temp, +		adt7316_store_select_ex_temp, +		0); + +static ssize_t adt7316_show_mode(struct device *dev, +		struct device_attribute *attr, +		char *buf) +{ +	struct iio_dev *dev_info = dev_to_iio_dev(dev); +	struct adt7316_chip_info *chip = iio_priv(dev_info); + +	if (chip->config2 & ADT7316_AD_SINGLE_CH_MODE) +		return sprintf(buf, "single_channel\n"); +	else +		return sprintf(buf, "round_robin\n"); +} + +static ssize_t adt7316_store_mode(struct device *dev, +		struct device_attribute *attr, +		const char *buf, +		size_t len) +{ +	struct iio_dev *dev_info = dev_to_iio_dev(dev); +	struct adt7316_chip_info *chip = iio_priv(dev_info); +	u8 config2; +	int ret; + +	config2 = chip->config2 & (~ADT7316_AD_SINGLE_CH_MODE); +	if (!memcmp(buf, "single_channel", 14)) +		config2 |= ADT7316_AD_SINGLE_CH_MODE; + +	ret = chip->bus.write(chip->bus.client, ADT7316_CONFIG2, config2); +	if (ret) +		return -EIO; + +	chip->config2 = config2; + +	return len; +} + +static IIO_DEVICE_ATTR(mode, S_IRUGO | S_IWUSR, +		adt7316_show_mode, +		adt7316_store_mode, +		0); + +static ssize_t adt7316_show_all_modes(struct device *dev, +		struct device_attribute *attr, +		char *buf) +{ +	return sprintf(buf, "single_channel\nround_robin\n"); +} + +static IIO_DEVICE_ATTR(all_modes, S_IRUGO, adt7316_show_all_modes, NULL, 0); + +static ssize_t adt7316_show_ad_channel(struct device *dev, +		struct device_attribute *attr, +		char *buf) +{ +	struct iio_dev *dev_info = dev_to_iio_dev(dev); +	struct adt7316_chip_info *chip = iio_priv(dev_info); + +	if (!(chip->config2 & ADT7316_AD_SINGLE_CH_MODE)) +		return -EPERM; + +	switch (chip->config2 & ADT7516_AD_SINGLE_CH_MASK) { +	case ADT7316_AD_SINGLE_CH_VDD: +		return sprintf(buf, "0 - VDD\n"); +	case ADT7316_AD_SINGLE_CH_IN: +		return sprintf(buf, "1 - Internal Temperature\n"); +	case ADT7316_AD_SINGLE_CH_EX: +		if (((chip->id & ID_FAMILY_MASK) == ID_ADT75XX) && +			(chip->config1 & ADT7516_SEL_AIN1_2_EX_TEMP_MASK) == 0) +			return sprintf(buf, "2 - AIN1\n"); +		else +			return sprintf(buf, "2 - External Temperature\n"); +	case ADT7516_AD_SINGLE_CH_AIN2: +		if ((chip->config1 & ADT7516_SEL_AIN1_2_EX_TEMP_MASK) == 0) +			return sprintf(buf, "3 - AIN2\n"); +		else +			return sprintf(buf, "N/A\n"); +	case ADT7516_AD_SINGLE_CH_AIN3: +		if (chip->config1 & ADT7516_SEL_AIN3) +			return sprintf(buf, "4 - AIN3\n"); +		else +			return sprintf(buf, "N/A\n"); +	case ADT7516_AD_SINGLE_CH_AIN4: +		return sprintf(buf, "5 - AIN4\n"); +	default: +		return sprintf(buf, "N/A\n"); +	} +} + +static ssize_t adt7316_store_ad_channel(struct device *dev, +		struct device_attribute *attr, +		const char *buf, +		size_t len) +{ +	struct iio_dev *dev_info = dev_to_iio_dev(dev); +	struct adt7316_chip_info *chip = iio_priv(dev_info); +	u8 config2; +	u8 data; +	int ret; + +	if (!(chip->config2 & ADT7316_AD_SINGLE_CH_MODE)) +		return -EPERM; + +	ret = kstrtou8(buf, 10, &data); +	if (ret) +		return -EINVAL; + +	if ((chip->id & ID_FAMILY_MASK) == ID_ADT75XX) { +		if (data > 5) +			return -EINVAL; + +		config2 = chip->config2 & (~ADT7516_AD_SINGLE_CH_MASK); +	} else { +		if (data > 2) +			return -EINVAL; + +		config2 = chip->config2 & (~ADT7316_AD_SINGLE_CH_MASK); +	} + + +	config2 |= data; + +	ret = chip->bus.write(chip->bus.client, ADT7316_CONFIG2, config2); +	if (ret) +		return -EIO; + +	chip->config2 = config2; + +	return len; +} + +static IIO_DEVICE_ATTR(ad_channel, S_IRUGO | S_IWUSR, +		adt7316_show_ad_channel, +		adt7316_store_ad_channel, +		0); + +static ssize_t adt7316_show_all_ad_channels(struct device *dev, +		struct device_attribute *attr, +		char *buf) +{ +	struct iio_dev *dev_info = dev_to_iio_dev(dev); +	struct adt7316_chip_info *chip = iio_priv(dev_info); + +	if (!(chip->config2 & ADT7316_AD_SINGLE_CH_MODE)) +		return -EPERM; + +	if ((chip->id & ID_FAMILY_MASK) == ID_ADT75XX) +		return sprintf(buf, "0 - VDD\n1 - Internal Temperature\n" +				"2 - External Temperature or AIN1\n" +				"3 - AIN2\n4 - AIN3\n5 - AIN4\n"); +	else +		return sprintf(buf, "0 - VDD\n1 - Internal Temperature\n" +				"2 - External Temperature\n"); +} + +static IIO_DEVICE_ATTR(all_ad_channels, S_IRUGO, +		adt7316_show_all_ad_channels, NULL, 0); + +static ssize_t adt7316_show_disable_averaging(struct device *dev, +		struct device_attribute *attr, +		char *buf) +{ +	struct iio_dev *dev_info = dev_to_iio_dev(dev); +	struct adt7316_chip_info *chip = iio_priv(dev_info); + +	return sprintf(buf, "%d\n", +		!!(chip->config2 & ADT7316_DISABLE_AVERAGING)); +} + +static ssize_t adt7316_store_disable_averaging(struct device *dev, +		struct device_attribute *attr, +		const char *buf, +		size_t len) +{ +	struct iio_dev *dev_info = dev_to_iio_dev(dev); +	struct adt7316_chip_info *chip = iio_priv(dev_info); +	u8 config2; +	int ret; + +	config2 = chip->config2 & (~ADT7316_DISABLE_AVERAGING); +	if (buf[0] == '1') +		config2 |= ADT7316_DISABLE_AVERAGING; + +	ret = chip->bus.write(chip->bus.client, ADT7316_CONFIG2, config2); +	if (ret) +		return -EIO; + +	chip->config2 = config2; + +	return len; +} + +static IIO_DEVICE_ATTR(disable_averaging, S_IRUGO | S_IWUSR, +		adt7316_show_disable_averaging, +		adt7316_store_disable_averaging, +		0); + +static ssize_t adt7316_show_enable_smbus_timeout(struct device *dev, +		struct device_attribute *attr, +		char *buf) +{ +	struct iio_dev *dev_info = dev_to_iio_dev(dev); +	struct adt7316_chip_info *chip = iio_priv(dev_info); + +	return sprintf(buf, "%d\n", +		!!(chip->config2 & ADT7316_EN_SMBUS_TIMEOUT)); +} + +static ssize_t adt7316_store_enable_smbus_timeout(struct device *dev, +		struct device_attribute *attr, +		const char *buf, +		size_t len) +{ +	struct iio_dev *dev_info = dev_to_iio_dev(dev); +	struct adt7316_chip_info *chip = iio_priv(dev_info); +	u8 config2; +	int ret; + +	config2 = chip->config2 & (~ADT7316_EN_SMBUS_TIMEOUT); +	if (buf[0] == '1') +		config2 |= ADT7316_EN_SMBUS_TIMEOUT; + +	ret = chip->bus.write(chip->bus.client, ADT7316_CONFIG2, config2); +	if (ret) +		return -EIO; + +	chip->config2 = config2; + +	return len; +} + +static IIO_DEVICE_ATTR(enable_smbus_timeout, S_IRUGO | S_IWUSR, +		adt7316_show_enable_smbus_timeout, +		adt7316_store_enable_smbus_timeout, +		0); + +static ssize_t adt7316_show_powerdown(struct device *dev, +		struct device_attribute *attr, +		char *buf) +{ +	struct iio_dev *dev_info = dev_to_iio_dev(dev); +	struct adt7316_chip_info *chip = iio_priv(dev_info); + +	return sprintf(buf, "%d\n", !!(chip->config1 & ADT7316_PD)); +} + +static ssize_t adt7316_store_powerdown(struct device *dev, +		struct device_attribute *attr, +		const char *buf, +		size_t len) +{ +	struct iio_dev *dev_info = dev_to_iio_dev(dev); +	struct adt7316_chip_info *chip = iio_priv(dev_info); +	u8 config1; +	int ret; + +	config1 = chip->config1 & (~ADT7316_PD); +	if (buf[0] == '1') +		config1 |= ADT7316_PD; + +	ret = chip->bus.write(chip->bus.client, ADT7316_CONFIG1, config1); +	if (ret) +		return -EIO; + +	chip->config1 = config1; + +	return len; +} + +static IIO_DEVICE_ATTR(powerdown, S_IRUGO | S_IWUSR, +		adt7316_show_powerdown, +		adt7316_store_powerdown, +		0); + +static ssize_t adt7316_show_fast_ad_clock(struct device *dev, +		struct device_attribute *attr, +		char *buf) +{ +	struct iio_dev *dev_info = dev_to_iio_dev(dev); +	struct adt7316_chip_info *chip = iio_priv(dev_info); + +	return sprintf(buf, "%d\n", !!(chip->config3 & ADT7316_ADCLK_22_5)); +} + +static ssize_t adt7316_store_fast_ad_clock(struct device *dev, +		struct device_attribute *attr, +		const char *buf, +		size_t len) +{ +	struct iio_dev *dev_info = dev_to_iio_dev(dev); +	struct adt7316_chip_info *chip = iio_priv(dev_info); +	u8 config3; +	int ret; + +	config3 = chip->config3 & (~ADT7316_ADCLK_22_5); +	if (buf[0] == '1') +		config3 |= ADT7316_ADCLK_22_5; + +	ret = chip->bus.write(chip->bus.client, ADT7316_CONFIG3, config3); +	if (ret) +		return -EIO; + +	chip->config3 = config3; + +	return len; +} + +static IIO_DEVICE_ATTR(fast_ad_clock, S_IRUGO | S_IWUSR, +		adt7316_show_fast_ad_clock, +		adt7316_store_fast_ad_clock, +		0); + +static ssize_t adt7316_show_da_high_resolution(struct device *dev, +		struct device_attribute *attr, +		char *buf) +{ +	struct iio_dev *dev_info = dev_to_iio_dev(dev); +	struct adt7316_chip_info *chip = iio_priv(dev_info); + +	if (chip->config3 & ADT7316_DA_HIGH_RESOLUTION) { +		if (chip->id == ID_ADT7316 || chip->id == ID_ADT7516) +			return sprintf(buf, "1 (12 bits)\n"); +		else if (chip->id == ID_ADT7317 || chip->id == ID_ADT7517) +			return sprintf(buf, "1 (10 bits)\n"); +	} + +	return sprintf(buf, "0 (8 bits)\n"); +} + +static ssize_t adt7316_store_da_high_resolution(struct device *dev, +		struct device_attribute *attr, +		const char *buf, +		size_t len) +{ +	struct iio_dev *dev_info = dev_to_iio_dev(dev); +	struct adt7316_chip_info *chip = iio_priv(dev_info); +	u8 config3; +	int ret; + +	chip->dac_bits = 8; + +	if (buf[0] == '1') { +		config3 = chip->config3 | ADT7316_DA_HIGH_RESOLUTION; +		if (chip->id == ID_ADT7316 || chip->id == ID_ADT7516) +			chip->dac_bits = 12; +		else if (chip->id == ID_ADT7317 || chip->id == ID_ADT7517) +			chip->dac_bits = 10; +	} else +		config3 = chip->config3 & (~ADT7316_DA_HIGH_RESOLUTION); + +	ret = chip->bus.write(chip->bus.client, ADT7316_CONFIG3, config3); +	if (ret) +		return -EIO; + +	chip->config3 = config3; + +	return len; +} + +static IIO_DEVICE_ATTR(da_high_resolution, S_IRUGO | S_IWUSR, +		adt7316_show_da_high_resolution, +		adt7316_store_da_high_resolution, +		0); + +static ssize_t adt7316_show_AIN_internal_Vref(struct device *dev, +		struct device_attribute *attr, +		char *buf) +{ +	struct iio_dev *dev_info = dev_to_iio_dev(dev); +	struct adt7316_chip_info *chip = iio_priv(dev_info); + +	if ((chip->id & ID_FAMILY_MASK) != ID_ADT75XX) +		return -EPERM; + +	return sprintf(buf, "%d\n", +		!!(chip->config3 & ADT7516_AIN_IN_VREF)); +} + +static ssize_t adt7316_store_AIN_internal_Vref(struct device *dev, +		struct device_attribute *attr, +		const char *buf, +		size_t len) +{ +	struct iio_dev *dev_info = dev_to_iio_dev(dev); +	struct adt7316_chip_info *chip = iio_priv(dev_info); +	u8 config3; +	int ret; + +	if ((chip->id & ID_FAMILY_MASK) != ID_ADT75XX) +		return -EPERM; + +	if (buf[0] != '1') +		config3 = chip->config3 & (~ADT7516_AIN_IN_VREF); +	else +		config3 = chip->config3 | ADT7516_AIN_IN_VREF; + +	ret = chip->bus.write(chip->bus.client, ADT7316_CONFIG3, config3); +	if (ret) +		return -EIO; + +	chip->config3 = config3; + +	return len; +} + +static IIO_DEVICE_ATTR(AIN_internal_Vref, S_IRUGO | S_IWUSR, +		adt7316_show_AIN_internal_Vref, +		adt7316_store_AIN_internal_Vref, +		0); + + +static ssize_t adt7316_show_enable_prop_DACA(struct device *dev, +		struct device_attribute *attr, +		char *buf) +{ +	struct iio_dev *dev_info = dev_to_iio_dev(dev); +	struct adt7316_chip_info *chip = iio_priv(dev_info); + +	return sprintf(buf, "%d\n", +		!!(chip->config3 & ADT7316_EN_IN_TEMP_PROP_DACA)); +} + +static ssize_t adt7316_store_enable_prop_DACA(struct device *dev, +		struct device_attribute *attr, +		const char *buf, +		size_t len) +{ +	struct iio_dev *dev_info = dev_to_iio_dev(dev); +	struct adt7316_chip_info *chip = iio_priv(dev_info); +	u8 config3; +	int ret; + +	config3 = chip->config3 & (~ADT7316_EN_IN_TEMP_PROP_DACA); +	if (buf[0] == '1') +		config3 |= ADT7316_EN_IN_TEMP_PROP_DACA; + +	ret = chip->bus.write(chip->bus.client, ADT7316_CONFIG3, config3); +	if (ret) +		return -EIO; + +	chip->config3 = config3; + +	return len; +} + +static IIO_DEVICE_ATTR(enable_proportion_DACA, S_IRUGO | S_IWUSR, +		adt7316_show_enable_prop_DACA, +		adt7316_store_enable_prop_DACA, +		0); + +static ssize_t adt7316_show_enable_prop_DACB(struct device *dev, +		struct device_attribute *attr, +		char *buf) +{ +	struct iio_dev *dev_info = dev_to_iio_dev(dev); +	struct adt7316_chip_info *chip = iio_priv(dev_info); + +	return sprintf(buf, "%d\n", +		!!(chip->config3 & ADT7316_EN_EX_TEMP_PROP_DACB)); +} + +static ssize_t adt7316_store_enable_prop_DACB(struct device *dev, +		struct device_attribute *attr, +		const char *buf, +		size_t len) +{ +	struct iio_dev *dev_info = dev_to_iio_dev(dev); +	struct adt7316_chip_info *chip = iio_priv(dev_info); +	u8 config3; +	int ret; + +	config3 = chip->config3 & (~ADT7316_EN_EX_TEMP_PROP_DACB); +	if (buf[0] == '1') +		config3 |= ADT7316_EN_EX_TEMP_PROP_DACB; + +	ret = chip->bus.write(chip->bus.client, ADT7316_CONFIG3, config3); +	if (ret) +		return -EIO; + +	chip->config3 = config3; + +	return len; +} + +static IIO_DEVICE_ATTR(enable_proportion_DACB, S_IRUGO | S_IWUSR, +		adt7316_show_enable_prop_DACB, +		adt7316_store_enable_prop_DACB, +		0); + +static ssize_t adt7316_show_DAC_2Vref_ch_mask(struct device *dev, +		struct device_attribute *attr, +		char *buf) +{ +	struct iio_dev *dev_info = dev_to_iio_dev(dev); +	struct adt7316_chip_info *chip = iio_priv(dev_info); + +	return sprintf(buf, "0x%x\n", +		chip->dac_config & ADT7316_DA_2VREF_CH_MASK); +} + +static ssize_t adt7316_store_DAC_2Vref_ch_mask(struct device *dev, +		struct device_attribute *attr, +		const char *buf, +		size_t len) +{ +	struct iio_dev *dev_info = dev_to_iio_dev(dev); +	struct adt7316_chip_info *chip = iio_priv(dev_info); +	u8 dac_config; +	u8 data; +	int ret; + +	ret = kstrtou8(buf, 16, &data); +	if (ret || data > ADT7316_DA_2VREF_CH_MASK) +		return -EINVAL; + +	dac_config = chip->dac_config & (~ADT7316_DA_2VREF_CH_MASK); +	dac_config |= data; + +	ret = chip->bus.write(chip->bus.client, ADT7316_DAC_CONFIG, dac_config); +	if (ret) +		return -EIO; + +	chip->dac_config = dac_config; + +	return len; +} + +static IIO_DEVICE_ATTR(DAC_2Vref_channels_mask, S_IRUGO | S_IWUSR, +		adt7316_show_DAC_2Vref_ch_mask, +		adt7316_store_DAC_2Vref_ch_mask, +		0); + +static ssize_t adt7316_show_DAC_update_mode(struct device *dev, +		struct device_attribute *attr, +		char *buf) +{ +	struct iio_dev *dev_info = dev_to_iio_dev(dev); +	struct adt7316_chip_info *chip = iio_priv(dev_info); + +	if (!(chip->config3 & ADT7316_DA_EN_VIA_DAC_LDCA)) +		return sprintf(buf, "manual\n"); +	else { +		switch (chip->dac_config & ADT7316_DA_EN_MODE_MASK) { +		case ADT7316_DA_EN_MODE_SINGLE: +			return sprintf(buf, +				"0 - auto at any MSB DAC writing\n"); +		case ADT7316_DA_EN_MODE_AB_CD: +			return sprintf(buf, +				"1 - auto at MSB DAC AB and CD writing\n"); +		case ADT7316_DA_EN_MODE_ABCD: +			return sprintf(buf, +				"2 - auto at MSB DAC ABCD writing\n"); +		default: /* ADT7316_DA_EN_MODE_LDAC */ +			return sprintf(buf, "3 - manual\n"); +		} +	} +} + +static ssize_t adt7316_store_DAC_update_mode(struct device *dev, +		struct device_attribute *attr, +		const char *buf, +		size_t len) +{ +	struct iio_dev *dev_info = dev_to_iio_dev(dev); +	struct adt7316_chip_info *chip = iio_priv(dev_info); +	u8 dac_config; +	u8 data; +	int ret; + +	if (!(chip->config3 & ADT7316_DA_EN_VIA_DAC_LDCA)) +		return -EPERM; + +	ret = kstrtou8(buf, 10, &data); +	if (ret || data > ADT7316_DA_EN_MODE_MASK) +		return -EINVAL; + +	dac_config = chip->dac_config & (~ADT7316_DA_EN_MODE_MASK); +	dac_config |= data; + +	ret = chip->bus.write(chip->bus.client, ADT7316_DAC_CONFIG, dac_config); +	if (ret) +		return -EIO; + +	chip->dac_config = dac_config; + +	return len; +} + +static IIO_DEVICE_ATTR(DAC_update_mode, S_IRUGO | S_IWUSR, +		adt7316_show_DAC_update_mode, +		adt7316_store_DAC_update_mode, +		0); + +static ssize_t adt7316_show_all_DAC_update_modes(struct device *dev, +		struct device_attribute *attr, +		char *buf) +{ +	struct iio_dev *dev_info = dev_to_iio_dev(dev); +	struct adt7316_chip_info *chip = iio_priv(dev_info); + +	if (chip->config3 & ADT7316_DA_EN_VIA_DAC_LDCA) +		return sprintf(buf, "0 - auto at any MSB DAC writing\n" +				"1 - auto at MSB DAC AB and CD writing\n" +				"2 - auto at MSB DAC ABCD writing\n" +				"3 - manual\n"); +	else +		return sprintf(buf, "manual\n"); +} + +static IIO_DEVICE_ATTR(all_DAC_update_modes, S_IRUGO, +		adt7316_show_all_DAC_update_modes, NULL, 0); + + +static ssize_t adt7316_store_update_DAC(struct device *dev, +		struct device_attribute *attr, +		const char *buf, +		size_t len) +{ +	struct iio_dev *dev_info = dev_to_iio_dev(dev); +	struct adt7316_chip_info *chip = iio_priv(dev_info); +	u8 ldac_config; +	u8 data; +	int ret; + +	if (chip->config3 & ADT7316_DA_EN_VIA_DAC_LDCA) { +		if ((chip->dac_config & ADT7316_DA_EN_MODE_MASK) != +			ADT7316_DA_EN_MODE_LDAC) +			return -EPERM; + +		ret = kstrtou8(buf, 16, &data); +		if (ret || data > ADT7316_LDAC_EN_DA_MASK) +			return -EINVAL; + +		ldac_config = chip->ldac_config & (~ADT7316_LDAC_EN_DA_MASK); +		ldac_config |= data; + +		ret = chip->bus.write(chip->bus.client, ADT7316_LDAC_CONFIG, +			ldac_config); +		if (ret) +			return -EIO; +	} else { +		gpio_set_value(chip->ldac_pin, 0); +		gpio_set_value(chip->ldac_pin, 1); +	} + +	return len; +} + +static IIO_DEVICE_ATTR(update_DAC, S_IRUGO | S_IWUSR, +		NULL, +		adt7316_store_update_DAC, +		0); + +static ssize_t adt7316_show_DA_AB_Vref_bypass(struct device *dev, +		struct device_attribute *attr, +		char *buf) +{ +	struct iio_dev *dev_info = dev_to_iio_dev(dev); +	struct adt7316_chip_info *chip = iio_priv(dev_info); + +	if ((chip->id & ID_FAMILY_MASK) == ID_ADT75XX) +		return -EPERM; + +	return sprintf(buf, "%d\n", +		!!(chip->dac_config & ADT7316_VREF_BYPASS_DAC_AB)); +} + +static ssize_t adt7316_store_DA_AB_Vref_bypass(struct device *dev, +		struct device_attribute *attr, +		const char *buf, +		size_t len) +{ +	struct iio_dev *dev_info = dev_to_iio_dev(dev); +	struct adt7316_chip_info *chip = iio_priv(dev_info); +	u8 dac_config; +	int ret; + +	if ((chip->id & ID_FAMILY_MASK) == ID_ADT75XX) +		return -EPERM; + +	dac_config = chip->dac_config & (~ADT7316_VREF_BYPASS_DAC_AB); +	if (buf[0] == '1') +		dac_config |= ADT7316_VREF_BYPASS_DAC_AB; + +	ret = chip->bus.write(chip->bus.client, ADT7316_DAC_CONFIG, dac_config); +	if (ret) +		return -EIO; + +	chip->dac_config = dac_config; + +	return len; +} + +static IIO_DEVICE_ATTR(DA_AB_Vref_bypass, S_IRUGO | S_IWUSR, +		adt7316_show_DA_AB_Vref_bypass, +		adt7316_store_DA_AB_Vref_bypass, +		0); + +static ssize_t adt7316_show_DA_CD_Vref_bypass(struct device *dev, +		struct device_attribute *attr, +		char *buf) +{ +	struct iio_dev *dev_info = dev_to_iio_dev(dev); +	struct adt7316_chip_info *chip = iio_priv(dev_info); + +	if ((chip->id & ID_FAMILY_MASK) == ID_ADT75XX) +		return -EPERM; + +	return sprintf(buf, "%d\n", +		!!(chip->dac_config & ADT7316_VREF_BYPASS_DAC_CD)); +} + +static ssize_t adt7316_store_DA_CD_Vref_bypass(struct device *dev, +		struct device_attribute *attr, +		const char *buf, +		size_t len) +{ +	struct iio_dev *dev_info = dev_to_iio_dev(dev); +	struct adt7316_chip_info *chip = iio_priv(dev_info); +	u8 dac_config; +	int ret; + +	if ((chip->id & ID_FAMILY_MASK) == ID_ADT75XX) +		return -EPERM; + +	dac_config = chip->dac_config & (~ADT7316_VREF_BYPASS_DAC_CD); +	if (buf[0] == '1') +		dac_config |= ADT7316_VREF_BYPASS_DAC_CD; + +	ret = chip->bus.write(chip->bus.client, ADT7316_DAC_CONFIG, dac_config); +	if (ret) +		return -EIO; + +	chip->dac_config = dac_config; + +	return len; +} + +static IIO_DEVICE_ATTR(DA_CD_Vref_bypass, S_IRUGO | S_IWUSR, +		adt7316_show_DA_CD_Vref_bypass, +		adt7316_store_DA_CD_Vref_bypass, +		0); + +static ssize_t adt7316_show_DAC_internal_Vref(struct device *dev, +		struct device_attribute *attr, +		char *buf) +{ +	struct iio_dev *dev_info = dev_to_iio_dev(dev); +	struct adt7316_chip_info *chip = iio_priv(dev_info); + +	if ((chip->id & ID_FAMILY_MASK) == ID_ADT75XX) +		return sprintf(buf, "0x%x\n", +			(chip->dac_config & ADT7516_DAC_IN_VREF_MASK) >> +			ADT7516_DAC_IN_VREF_OFFSET); +	else +		return sprintf(buf, "%d\n", +			!!(chip->dac_config & ADT7316_DAC_IN_VREF)); +} + +static ssize_t adt7316_store_DAC_internal_Vref(struct device *dev, +		struct device_attribute *attr, +		const char *buf, +		size_t len) +{ +	struct iio_dev *dev_info = dev_to_iio_dev(dev); +	struct adt7316_chip_info *chip = iio_priv(dev_info); +	u8 ldac_config; +	u8 data; +	int ret; + +	if ((chip->id & ID_FAMILY_MASK) == ID_ADT75XX) { +		ret = kstrtou8(buf, 16, &data); +		if (ret || data > 3) +			return -EINVAL; + +		ldac_config = chip->ldac_config & (~ADT7516_DAC_IN_VREF_MASK); +		if (data & 0x1) +			ldac_config |= ADT7516_DAC_AB_IN_VREF; +		else if (data & 0x2) +			ldac_config |= ADT7516_DAC_CD_IN_VREF; +	} else { +		ret = kstrtou8(buf, 16, &data); +		if (ret) +			return -EINVAL; + +		ldac_config = chip->ldac_config & (~ADT7316_DAC_IN_VREF); +		if (data) +			ldac_config = chip->ldac_config | ADT7316_DAC_IN_VREF; +	} + +	ret = chip->bus.write(chip->bus.client, ADT7316_LDAC_CONFIG, +			ldac_config); +	if (ret) +		return -EIO; + +	chip->ldac_config = ldac_config; + +	return len; +} + +static IIO_DEVICE_ATTR(DAC_internal_Vref, S_IRUGO | S_IWUSR, +		adt7316_show_DAC_internal_Vref, +		adt7316_store_DAC_internal_Vref, +		0); + +static ssize_t adt7316_show_ad(struct adt7316_chip_info *chip, +		int channel, char *buf) +{ +	u16 data; +	u8 msb, lsb; +	char sign = ' '; +	int ret; + +	if ((chip->config2 & ADT7316_AD_SINGLE_CH_MODE) && +		channel != (chip->config2 & ADT7516_AD_SINGLE_CH_MASK)) +		return -EPERM; + +	switch (channel) { +	case ADT7316_AD_SINGLE_CH_IN: +		ret = chip->bus.read(chip->bus.client, +			ADT7316_LSB_IN_TEMP_VDD, &lsb); +		if (ret) +			return -EIO; + +		ret = chip->bus.read(chip->bus.client, +			ADT7316_AD_MSB_DATA_BASE + channel, &msb); +		if (ret) +			return -EIO; + +		data = msb << ADT7316_T_VALUE_FLOAT_OFFSET; +		data |= lsb & ADT7316_LSB_IN_TEMP_MASK; +		break; +	case ADT7316_AD_SINGLE_CH_VDD: +		ret = chip->bus.read(chip->bus.client, +			ADT7316_LSB_IN_TEMP_VDD, &lsb); +		if (ret) +			return -EIO; + +		ret = chip->bus.read(chip->bus.client, + +			ADT7316_AD_MSB_DATA_BASE + channel, &msb); +		if (ret) +			return -EIO; + +		data = msb << ADT7316_T_VALUE_FLOAT_OFFSET; +		data |= (lsb & ADT7316_LSB_VDD_MASK) >> ADT7316_LSB_VDD_OFFSET; +		return sprintf(buf, "%d\n", data); +	default: /* ex_temp and ain */ +		ret = chip->bus.read(chip->bus.client, +			ADT7316_LSB_EX_TEMP_AIN, &lsb); +		if (ret) +			return -EIO; + +		ret = chip->bus.read(chip->bus.client, +			ADT7316_AD_MSB_DATA_BASE + channel, &msb); +		if (ret) +			return -EIO; + +		data = msb << ADT7316_T_VALUE_FLOAT_OFFSET; +		data |= lsb & (ADT7316_LSB_EX_TEMP_MASK << +			(ADT7516_LSB_AIN_SHIFT * (channel - +			(ADT7316_MSB_EX_TEMP - ADT7316_AD_MSB_DATA_BASE)))); + +		if ((chip->id & ID_FAMILY_MASK) == ID_ADT75XX) +			return sprintf(buf, "%d\n", data); +		else +			break; +	} + +	if (data & ADT7316_T_VALUE_SIGN) { +		/* convert supplement to positive value */ +		data = (ADT7316_T_VALUE_SIGN << 1) - data; +		sign = '-'; +	} + +	return sprintf(buf, "%c%d.%.2d\n", sign, +		(data >> ADT7316_T_VALUE_FLOAT_OFFSET), +		(data & ADT7316_T_VALUE_FLOAT_MASK) * 25); +} + +static ssize_t adt7316_show_VDD(struct device *dev, +		struct device_attribute *attr, +		char *buf) +{ +	struct iio_dev *dev_info = dev_to_iio_dev(dev); +	struct adt7316_chip_info *chip = iio_priv(dev_info); + +	return adt7316_show_ad(chip, ADT7316_AD_SINGLE_CH_VDD, buf); +} +static IIO_DEVICE_ATTR(VDD, S_IRUGO, adt7316_show_VDD, NULL, 0); + +static ssize_t adt7316_show_in_temp(struct device *dev, +		struct device_attribute *attr, +		char *buf) +{ +	struct iio_dev *dev_info = dev_to_iio_dev(dev); +	struct adt7316_chip_info *chip = iio_priv(dev_info); + +	return adt7316_show_ad(chip, ADT7316_AD_SINGLE_CH_IN, buf); +} + +static IIO_DEVICE_ATTR(in_temp, S_IRUGO, adt7316_show_in_temp, NULL, 0); + +static ssize_t adt7316_show_ex_temp_AIN1(struct device *dev, +		struct device_attribute *attr, +		char *buf) +{ +	struct iio_dev *dev_info = dev_to_iio_dev(dev); +	struct adt7316_chip_info *chip = iio_priv(dev_info); + +	return adt7316_show_ad(chip, ADT7316_AD_SINGLE_CH_EX, buf); +} + +static IIO_DEVICE_ATTR(ex_temp_AIN1, S_IRUGO, adt7316_show_ex_temp_AIN1, +		NULL, 0); +static IIO_DEVICE_ATTR(ex_temp, S_IRUGO, adt7316_show_ex_temp_AIN1, NULL, 0); + +static ssize_t adt7316_show_AIN2(struct device *dev, +		struct device_attribute *attr, +		char *buf) +{ +	struct iio_dev *dev_info = dev_to_iio_dev(dev); +	struct adt7316_chip_info *chip = iio_priv(dev_info); + +	return adt7316_show_ad(chip, ADT7516_AD_SINGLE_CH_AIN2, buf); +} +static IIO_DEVICE_ATTR(AIN2, S_IRUGO, adt7316_show_AIN2, NULL, 0); + +static ssize_t adt7316_show_AIN3(struct device *dev, +		struct device_attribute *attr, +		char *buf) +{ +	struct iio_dev *dev_info = dev_to_iio_dev(dev); +	struct adt7316_chip_info *chip = iio_priv(dev_info); + +	return adt7316_show_ad(chip, ADT7516_AD_SINGLE_CH_AIN3, buf); +} +static IIO_DEVICE_ATTR(AIN3, S_IRUGO, adt7316_show_AIN3, NULL, 0); + +static ssize_t adt7316_show_AIN4(struct device *dev, +		struct device_attribute *attr, +		char *buf) +{ +	struct iio_dev *dev_info = dev_to_iio_dev(dev); +	struct adt7316_chip_info *chip = iio_priv(dev_info); + +	return adt7316_show_ad(chip, ADT7516_AD_SINGLE_CH_AIN4, buf); +} +static IIO_DEVICE_ATTR(AIN4, S_IRUGO, adt7316_show_AIN4, NULL, 0); + +static ssize_t adt7316_show_temp_offset(struct adt7316_chip_info *chip, +		int offset_addr, char *buf) +{ +	int data; +	u8 val; +	int ret; + +	ret = chip->bus.read(chip->bus.client, offset_addr, &val); +	if (ret) +		return -EIO; + +	data = (int)val; +	if (val & 0x80) +		data -= 256; + +	return sprintf(buf, "%d\n", data); +} + +static ssize_t adt7316_store_temp_offset(struct adt7316_chip_info *chip, +		int offset_addr, const char *buf, size_t len) +{ +	int data; +	u8 val; +	int ret; + +	ret = kstrtoint(buf, 10, &data); +	if (ret || data > 127 || data < -128) +		return -EINVAL; + +	if (data < 0) +		data += 256; + +	val = (u8)data; + +	ret = chip->bus.write(chip->bus.client, offset_addr, val); +	if (ret) +		return -EIO; + +	return len; +} + +static ssize_t adt7316_show_in_temp_offset(struct device *dev, +		struct device_attribute *attr, +		char *buf) +{ +	struct iio_dev *dev_info = dev_to_iio_dev(dev); +	struct adt7316_chip_info *chip = iio_priv(dev_info); + +	return adt7316_show_temp_offset(chip, ADT7316_IN_TEMP_OFFSET, buf); +} + +static ssize_t adt7316_store_in_temp_offset(struct device *dev, +		struct device_attribute *attr, +		const char *buf, +		size_t len) +{ +	struct iio_dev *dev_info = dev_to_iio_dev(dev); +	struct adt7316_chip_info *chip = iio_priv(dev_info); + +	return adt7316_store_temp_offset(chip, ADT7316_IN_TEMP_OFFSET, buf, +			len); +} + +static IIO_DEVICE_ATTR(in_temp_offset, S_IRUGO | S_IWUSR, +		adt7316_show_in_temp_offset, +		adt7316_store_in_temp_offset, 0); + +static ssize_t adt7316_show_ex_temp_offset(struct device *dev, +		struct device_attribute *attr, +		char *buf) +{ +	struct iio_dev *dev_info = dev_to_iio_dev(dev); +	struct adt7316_chip_info *chip = iio_priv(dev_info); + +	return adt7316_show_temp_offset(chip, ADT7316_EX_TEMP_OFFSET, buf); +} + +static ssize_t adt7316_store_ex_temp_offset(struct device *dev, +		struct device_attribute *attr, +		const char *buf, +		size_t len) +{ +	struct iio_dev *dev_info = dev_to_iio_dev(dev); +	struct adt7316_chip_info *chip = iio_priv(dev_info); + +	return adt7316_store_temp_offset(chip, ADT7316_EX_TEMP_OFFSET, buf, +			len); +} + +static IIO_DEVICE_ATTR(ex_temp_offset, S_IRUGO | S_IWUSR, +		adt7316_show_ex_temp_offset, +		adt7316_store_ex_temp_offset, 0); + +static ssize_t adt7316_show_in_analog_temp_offset(struct device *dev, +		struct device_attribute *attr, +		char *buf) +{ +	struct iio_dev *dev_info = dev_to_iio_dev(dev); +	struct adt7316_chip_info *chip = iio_priv(dev_info); + +	return adt7316_show_temp_offset(chip, +			ADT7316_IN_ANALOG_TEMP_OFFSET, buf); +} + +static ssize_t adt7316_store_in_analog_temp_offset(struct device *dev, +		struct device_attribute *attr, +		const char *buf, +		size_t len) +{ +	struct iio_dev *dev_info = dev_to_iio_dev(dev); +	struct adt7316_chip_info *chip = iio_priv(dev_info); + +	return adt7316_store_temp_offset(chip, +			ADT7316_IN_ANALOG_TEMP_OFFSET, buf, len); +} + +static IIO_DEVICE_ATTR(in_analog_temp_offset, S_IRUGO | S_IWUSR, +		adt7316_show_in_analog_temp_offset, +		adt7316_store_in_analog_temp_offset, 0); + +static ssize_t adt7316_show_ex_analog_temp_offset(struct device *dev, +		struct device_attribute *attr, +		char *buf) +{ +	struct iio_dev *dev_info = dev_to_iio_dev(dev); +	struct adt7316_chip_info *chip = iio_priv(dev_info); + +	return adt7316_show_temp_offset(chip, +			ADT7316_EX_ANALOG_TEMP_OFFSET, buf); +} + +static ssize_t adt7316_store_ex_analog_temp_offset(struct device *dev, +		struct device_attribute *attr, +		const char *buf, +		size_t len) +{ +	struct iio_dev *dev_info = dev_to_iio_dev(dev); +	struct adt7316_chip_info *chip = iio_priv(dev_info); + +	return adt7316_store_temp_offset(chip, +			ADT7316_EX_ANALOG_TEMP_OFFSET, buf, len); +} + +static IIO_DEVICE_ATTR(ex_analog_temp_offset, S_IRUGO | S_IWUSR, +		adt7316_show_ex_analog_temp_offset, +		adt7316_store_ex_analog_temp_offset, 0); + +static ssize_t adt7316_show_DAC(struct adt7316_chip_info *chip, +		int channel, char *buf) +{ +	u16 data; +	u8 msb, lsb, offset; +	int ret; + +	if (channel >= ADT7316_DA_MSB_DATA_REGS || +		(channel == 0 && +		(chip->config3 & ADT7316_EN_IN_TEMP_PROP_DACA)) || +		(channel == 1 && +		(chip->config3 & ADT7316_EN_EX_TEMP_PROP_DACB))) +		return -EPERM; + +	offset = chip->dac_bits - 8; + +	if (chip->dac_bits > 8) { +		ret = chip->bus.read(chip->bus.client, +			ADT7316_DA_DATA_BASE + channel * 2, &lsb); +		if (ret) +			return -EIO; +	} + +	ret = chip->bus.read(chip->bus.client, +		ADT7316_DA_DATA_BASE + 1 + channel * 2, &msb); +	if (ret) +		return -EIO; + +	data = (msb << offset) + (lsb & ((1 << offset) - 1)); + +	return sprintf(buf, "%d\n", data); +} + +static ssize_t adt7316_store_DAC(struct adt7316_chip_info *chip, +		int channel, const char *buf, size_t len) +{ +	u8 msb, lsb, offset; +	u16 data; +	int ret; + +	if (channel >= ADT7316_DA_MSB_DATA_REGS || +		(channel == 0 && +		(chip->config3 & ADT7316_EN_IN_TEMP_PROP_DACA)) || +		(channel == 1 && +		(chip->config3 & ADT7316_EN_EX_TEMP_PROP_DACB))) +		return -EPERM; + +	offset = chip->dac_bits - 8; + +	ret = kstrtou16(buf, 10, &data); +	if (ret || data >= (1 << chip->dac_bits)) +		return -EINVAL; + +	if (chip->dac_bits > 8) { +		lsb = data & (1 << offset); +		ret = chip->bus.write(chip->bus.client, +			ADT7316_DA_DATA_BASE + channel * 2, lsb); +		if (ret) +			return -EIO; +	} + +	msb = data >> offset; +	ret = chip->bus.write(chip->bus.client, +		ADT7316_DA_DATA_BASE + 1 + channel * 2, msb); +	if (ret) +		return -EIO; + +	return len; +} + +static ssize_t adt7316_show_DAC_A(struct device *dev, +		struct device_attribute *attr, +		char *buf) +{ +	struct iio_dev *dev_info = dev_to_iio_dev(dev); +	struct adt7316_chip_info *chip = iio_priv(dev_info); + +	return adt7316_show_DAC(chip, 0, buf); +} + +static ssize_t adt7316_store_DAC_A(struct device *dev, +		struct device_attribute *attr, +		const char *buf, +		size_t len) +{ +	struct iio_dev *dev_info = dev_to_iio_dev(dev); +	struct adt7316_chip_info *chip = iio_priv(dev_info); + +	return adt7316_store_DAC(chip, 0, buf, len); +} + +static IIO_DEVICE_ATTR(DAC_A, S_IRUGO | S_IWUSR, adt7316_show_DAC_A, +		adt7316_store_DAC_A, 0); + +static ssize_t adt7316_show_DAC_B(struct device *dev, +		struct device_attribute *attr, +		char *buf) +{ +	struct iio_dev *dev_info = dev_to_iio_dev(dev); +	struct adt7316_chip_info *chip = iio_priv(dev_info); + +	return adt7316_show_DAC(chip, 1, buf); +} + +static ssize_t adt7316_store_DAC_B(struct device *dev, +		struct device_attribute *attr, +		const char *buf, +		size_t len) +{ +	struct iio_dev *dev_info = dev_to_iio_dev(dev); +	struct adt7316_chip_info *chip = iio_priv(dev_info); + +	return adt7316_store_DAC(chip, 1, buf, len); +} + +static IIO_DEVICE_ATTR(DAC_B, S_IRUGO | S_IWUSR, adt7316_show_DAC_B, +		adt7316_store_DAC_B, 0); + +static ssize_t adt7316_show_DAC_C(struct device *dev, +		struct device_attribute *attr, +		char *buf) +{ +	struct iio_dev *dev_info = dev_to_iio_dev(dev); +	struct adt7316_chip_info *chip = iio_priv(dev_info); + +	return adt7316_show_DAC(chip, 2, buf); +} + +static ssize_t adt7316_store_DAC_C(struct device *dev, +		struct device_attribute *attr, +		const char *buf, +		size_t len) +{ +	struct iio_dev *dev_info = dev_to_iio_dev(dev); +	struct adt7316_chip_info *chip = iio_priv(dev_info); + +	return adt7316_store_DAC(chip, 2, buf, len); +} + +static IIO_DEVICE_ATTR(DAC_C, S_IRUGO | S_IWUSR, adt7316_show_DAC_C, +		adt7316_store_DAC_C, 0); + +static ssize_t adt7316_show_DAC_D(struct device *dev, +		struct device_attribute *attr, +		char *buf) +{ +	struct iio_dev *dev_info = dev_to_iio_dev(dev); +	struct adt7316_chip_info *chip = iio_priv(dev_info); + +	return adt7316_show_DAC(chip, 3, buf); +} + +static ssize_t adt7316_store_DAC_D(struct device *dev, +		struct device_attribute *attr, +		const char *buf, +		size_t len) +{ +	struct iio_dev *dev_info = dev_to_iio_dev(dev); +	struct adt7316_chip_info *chip = iio_priv(dev_info); + +	return adt7316_store_DAC(chip, 3, buf, len); +} + +static IIO_DEVICE_ATTR(DAC_D, S_IRUGO | S_IWUSR, adt7316_show_DAC_D, +		adt7316_store_DAC_D, 0); + +static ssize_t adt7316_show_device_id(struct device *dev, +		struct device_attribute *attr, +		char *buf) +{ +	struct iio_dev *dev_info = dev_to_iio_dev(dev); +	struct adt7316_chip_info *chip = iio_priv(dev_info); +	u8 id; +	int ret; + +	ret = chip->bus.read(chip->bus.client, ADT7316_DEVICE_ID, &id); +	if (ret) +		return -EIO; + +	return sprintf(buf, "%d\n", id); +} + +static IIO_DEVICE_ATTR(device_id, S_IRUGO, adt7316_show_device_id, NULL, 0); + +static ssize_t adt7316_show_manufactorer_id(struct device *dev, +		struct device_attribute *attr, +		char *buf) +{ +	struct iio_dev *dev_info = dev_to_iio_dev(dev); +	struct adt7316_chip_info *chip = iio_priv(dev_info); +	u8 id; +	int ret; + +	ret = chip->bus.read(chip->bus.client, ADT7316_MANUFACTURE_ID, &id); +	if (ret) +		return -EIO; + +	return sprintf(buf, "%d\n", id); +} + +static IIO_DEVICE_ATTR(manufactorer_id, S_IRUGO, +		adt7316_show_manufactorer_id, NULL, 0); + +static ssize_t adt7316_show_device_rev(struct device *dev, +		struct device_attribute *attr, +		char *buf) +{ +	struct iio_dev *dev_info = dev_to_iio_dev(dev); +	struct adt7316_chip_info *chip = iio_priv(dev_info); +	u8 rev; +	int ret; + +	ret = chip->bus.read(chip->bus.client, ADT7316_DEVICE_REV, &rev); +	if (ret) +		return -EIO; + +	return sprintf(buf, "%d\n", rev); +} + +static IIO_DEVICE_ATTR(device_rev, S_IRUGO, adt7316_show_device_rev, NULL, 0); + +static ssize_t adt7316_show_bus_type(struct device *dev, +		struct device_attribute *attr, +		char *buf) +{ +	struct iio_dev *dev_info = dev_to_iio_dev(dev); +	struct adt7316_chip_info *chip = iio_priv(dev_info); +	u8 stat; +	int ret; + +	ret = chip->bus.read(chip->bus.client, ADT7316_SPI_LOCK_STAT, &stat); +	if (ret) +		return -EIO; + +	if (stat) +		return sprintf(buf, "spi\n"); +	else +		return sprintf(buf, "i2c\n"); +} + +static IIO_DEVICE_ATTR(bus_type, S_IRUGO, adt7316_show_bus_type, NULL, 0); + +static struct attribute *adt7316_attributes[] = { +	&iio_dev_attr_all_modes.dev_attr.attr, +	&iio_dev_attr_mode.dev_attr.attr, +	&iio_dev_attr_enabled.dev_attr.attr, +	&iio_dev_attr_ad_channel.dev_attr.attr, +	&iio_dev_attr_all_ad_channels.dev_attr.attr, +	&iio_dev_attr_disable_averaging.dev_attr.attr, +	&iio_dev_attr_enable_smbus_timeout.dev_attr.attr, +	&iio_dev_attr_powerdown.dev_attr.attr, +	&iio_dev_attr_fast_ad_clock.dev_attr.attr, +	&iio_dev_attr_da_high_resolution.dev_attr.attr, +	&iio_dev_attr_enable_proportion_DACA.dev_attr.attr, +	&iio_dev_attr_enable_proportion_DACB.dev_attr.attr, +	&iio_dev_attr_DAC_2Vref_channels_mask.dev_attr.attr, +	&iio_dev_attr_DAC_update_mode.dev_attr.attr, +	&iio_dev_attr_all_DAC_update_modes.dev_attr.attr, +	&iio_dev_attr_update_DAC.dev_attr.attr, +	&iio_dev_attr_DA_AB_Vref_bypass.dev_attr.attr, +	&iio_dev_attr_DA_CD_Vref_bypass.dev_attr.attr, +	&iio_dev_attr_DAC_internal_Vref.dev_attr.attr, +	&iio_dev_attr_VDD.dev_attr.attr, +	&iio_dev_attr_in_temp.dev_attr.attr, +	&iio_dev_attr_ex_temp.dev_attr.attr, +	&iio_dev_attr_in_temp_offset.dev_attr.attr, +	&iio_dev_attr_ex_temp_offset.dev_attr.attr, +	&iio_dev_attr_in_analog_temp_offset.dev_attr.attr, +	&iio_dev_attr_ex_analog_temp_offset.dev_attr.attr, +	&iio_dev_attr_DAC_A.dev_attr.attr, +	&iio_dev_attr_DAC_B.dev_attr.attr, +	&iio_dev_attr_DAC_C.dev_attr.attr, +	&iio_dev_attr_DAC_D.dev_attr.attr, +	&iio_dev_attr_device_id.dev_attr.attr, +	&iio_dev_attr_manufactorer_id.dev_attr.attr, +	&iio_dev_attr_device_rev.dev_attr.attr, +	&iio_dev_attr_bus_type.dev_attr.attr, +	NULL, +}; + +static const struct attribute_group adt7316_attribute_group = { +	.attrs = adt7316_attributes, +}; + +static struct attribute *adt7516_attributes[] = { +	&iio_dev_attr_all_modes.dev_attr.attr, +	&iio_dev_attr_mode.dev_attr.attr, +	&iio_dev_attr_select_ex_temp.dev_attr.attr, +	&iio_dev_attr_enabled.dev_attr.attr, +	&iio_dev_attr_ad_channel.dev_attr.attr, +	&iio_dev_attr_all_ad_channels.dev_attr.attr, +	&iio_dev_attr_disable_averaging.dev_attr.attr, +	&iio_dev_attr_enable_smbus_timeout.dev_attr.attr, +	&iio_dev_attr_powerdown.dev_attr.attr, +	&iio_dev_attr_fast_ad_clock.dev_attr.attr, +	&iio_dev_attr_AIN_internal_Vref.dev_attr.attr, +	&iio_dev_attr_da_high_resolution.dev_attr.attr, +	&iio_dev_attr_enable_proportion_DACA.dev_attr.attr, +	&iio_dev_attr_enable_proportion_DACB.dev_attr.attr, +	&iio_dev_attr_DAC_2Vref_channels_mask.dev_attr.attr, +	&iio_dev_attr_DAC_update_mode.dev_attr.attr, +	&iio_dev_attr_all_DAC_update_modes.dev_attr.attr, +	&iio_dev_attr_update_DAC.dev_attr.attr, +	&iio_dev_attr_DA_AB_Vref_bypass.dev_attr.attr, +	&iio_dev_attr_DA_CD_Vref_bypass.dev_attr.attr, +	&iio_dev_attr_DAC_internal_Vref.dev_attr.attr, +	&iio_dev_attr_VDD.dev_attr.attr, +	&iio_dev_attr_in_temp.dev_attr.attr, +	&iio_dev_attr_ex_temp_AIN1.dev_attr.attr, +	&iio_dev_attr_AIN2.dev_attr.attr, +	&iio_dev_attr_AIN3.dev_attr.attr, +	&iio_dev_attr_AIN4.dev_attr.attr, +	&iio_dev_attr_in_temp_offset.dev_attr.attr, +	&iio_dev_attr_ex_temp_offset.dev_attr.attr, +	&iio_dev_attr_in_analog_temp_offset.dev_attr.attr, +	&iio_dev_attr_ex_analog_temp_offset.dev_attr.attr, +	&iio_dev_attr_DAC_A.dev_attr.attr, +	&iio_dev_attr_DAC_B.dev_attr.attr, +	&iio_dev_attr_DAC_C.dev_attr.attr, +	&iio_dev_attr_DAC_D.dev_attr.attr, +	&iio_dev_attr_device_id.dev_attr.attr, +	&iio_dev_attr_manufactorer_id.dev_attr.attr, +	&iio_dev_attr_device_rev.dev_attr.attr, +	&iio_dev_attr_bus_type.dev_attr.attr, +	NULL, +}; + +static const struct attribute_group adt7516_attribute_group = { +	.attrs = adt7516_attributes, +}; + +static irqreturn_t adt7316_event_handler(int irq, void *private) +{ +	struct iio_dev *indio_dev = private; +	struct adt7316_chip_info *chip = iio_priv(indio_dev); +	u8 stat1, stat2; +	int ret; +	s64 time; + +	ret = chip->bus.read(chip->bus.client, ADT7316_INT_STAT1, &stat1); +	if (!ret) { +		if ((chip->id & ID_FAMILY_MASK) != ID_ADT75XX) +			stat1 &= 0x1F; + +		time = iio_get_time_ns(); +		if (stat1 & (1 << 0)) +			iio_push_event(indio_dev, +				       IIO_UNMOD_EVENT_CODE(IIO_TEMP, 0, +							    IIO_EV_TYPE_THRESH, +							    IIO_EV_DIR_RISING), +				       time); +		if (stat1 & (1 << 1)) +			iio_push_event(indio_dev, +				       IIO_UNMOD_EVENT_CODE(IIO_TEMP, 0, +							    IIO_EV_TYPE_THRESH, +							    IIO_EV_DIR_FALLING), +				       time); +		if (stat1 & (1 << 2)) +			iio_push_event(indio_dev, +				       IIO_UNMOD_EVENT_CODE(IIO_TEMP, 1, +							    IIO_EV_TYPE_THRESH, +							    IIO_EV_DIR_RISING), +				       time); +		if (stat1 & (1 << 3)) +			iio_push_event(indio_dev, +				       IIO_UNMOD_EVENT_CODE(IIO_TEMP, 1, +							    IIO_EV_TYPE_THRESH, +							    IIO_EV_DIR_FALLING), +				       time); +		if (stat1 & (1 << 5)) +			iio_push_event(indio_dev, +				       IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, 1, +							    IIO_EV_TYPE_THRESH, +							    IIO_EV_DIR_EITHER), +				       time); +		if (stat1 & (1 << 6)) +			iio_push_event(indio_dev, +				       IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, 2, +							    IIO_EV_TYPE_THRESH, +							    IIO_EV_DIR_EITHER), +				       time); +		if (stat1 & (1 << 7)) +			iio_push_event(indio_dev, +				       IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, 3, +							    IIO_EV_TYPE_THRESH, +							    IIO_EV_DIR_EITHER), +				       time); +		} +	ret = chip->bus.read(chip->bus.client, ADT7316_INT_STAT2, &stat2); +	if (!ret) { +		if (stat2 & ADT7316_INT_MASK2_VDD) +			iio_push_event(indio_dev, +				       IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, +							    0, +							    IIO_EV_TYPE_THRESH, +							    IIO_EV_DIR_RISING), +				       iio_get_time_ns()); +	} + +	return IRQ_HANDLED; +} + +/* + * Show mask of enabled interrupts in Hex. + */ +static ssize_t adt7316_show_int_mask(struct device *dev, +		struct device_attribute *attr, +		char *buf) +{ +	struct iio_dev *dev_info = dev_to_iio_dev(dev); +	struct adt7316_chip_info *chip = iio_priv(dev_info); + +	return sprintf(buf, "0x%x\n", chip->int_mask); +} + +/* + * Set 1 to the mask in Hex to enabled interrupts. + */ +static ssize_t adt7316_set_int_mask(struct device *dev, +		struct device_attribute *attr, +		const char *buf, +		size_t len) +{ +	struct iio_dev *dev_info = dev_to_iio_dev(dev); +	struct adt7316_chip_info *chip = iio_priv(dev_info); +	u16 data; +	int ret; +	u8 mask; + +	ret = kstrtou16(buf, 16, &data); +	if (ret || data >= ADT7316_VDD_INT_MASK + 1) +		return -EINVAL; + +	if (data & ADT7316_VDD_INT_MASK) +		mask = 0;			/* enable vdd int */ +	else +		mask = ADT7316_INT_MASK2_VDD;	/* disable vdd int */ + +	ret = chip->bus.write(chip->bus.client, ADT7316_INT_MASK2, mask); +	if (!ret) { +		chip->int_mask &= ~ADT7316_VDD_INT_MASK; +		chip->int_mask |= data & ADT7316_VDD_INT_MASK; +	} + +	if (data & ADT7316_TEMP_AIN_INT_MASK) { +		if ((chip->id & ID_FAMILY_MASK) == ID_ADT73XX) +			/* mask in reg is opposite, set 1 to disable */ +			mask = (~data) & ADT7316_TEMP_INT_MASK; +		else +			/* mask in reg is opposite, set 1 to disable */ +			mask = (~data) & ADT7316_TEMP_AIN_INT_MASK; +	} +	ret = chip->bus.write(chip->bus.client, ADT7316_INT_MASK1, mask); + +	chip->int_mask = mask; + +	return len; +} +static inline ssize_t adt7316_show_ad_bound(struct device *dev, +		struct device_attribute *attr, +		char *buf) +{ +	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); +	struct iio_dev *dev_info = dev_to_iio_dev(dev); +	struct adt7316_chip_info *chip = iio_priv(dev_info); +	u8 val; +	int data; +	int ret; + +	if ((chip->id & ID_FAMILY_MASK) == ID_ADT73XX && +		this_attr->address > ADT7316_EX_TEMP_LOW) +		return -EPERM; + +	ret = chip->bus.read(chip->bus.client, this_attr->address, &val); +	if (ret) +		return -EIO; + +	data = (int)val; + +	if (!((chip->id & ID_FAMILY_MASK) == ID_ADT75XX && +		(chip->config1 & ADT7516_SEL_AIN1_2_EX_TEMP_MASK) == 0)) { +		if (data & 0x80) +			data -= 256; +	} + +	return sprintf(buf, "%d\n", data); +} + +static inline ssize_t adt7316_set_ad_bound(struct device *dev, +		struct device_attribute *attr, +		const char *buf, +		size_t len) +{ +	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); +	struct iio_dev *dev_info = dev_to_iio_dev(dev); +	struct adt7316_chip_info *chip = iio_priv(dev_info); +	int data; +	u8 val; +	int ret; + +	if ((chip->id & ID_FAMILY_MASK) == ID_ADT73XX && +		this_attr->address > ADT7316_EX_TEMP_LOW) +		return -EPERM; + +	ret = kstrtoint(buf, 10, &data); +	if (ret) +		return -EINVAL; + +	if ((chip->id & ID_FAMILY_MASK) == ID_ADT75XX && +		(chip->config1 & ADT7516_SEL_AIN1_2_EX_TEMP_MASK) == 0) { +		if (data > 255 || data < 0) +			return -EINVAL; +	} else { +		if (data > 127 || data < -128) +			return -EINVAL; + +		if (data < 0) +			data += 256; +	} + +	val = (u8)data; + +	ret = chip->bus.write(chip->bus.client, this_attr->address, val); +	if (ret) +		return -EIO; + +	return len; +} + +static ssize_t adt7316_show_int_enabled(struct device *dev, +		struct device_attribute *attr, +		char *buf) +{ +	struct iio_dev *dev_info = dev_to_iio_dev(dev); +	struct adt7316_chip_info *chip = iio_priv(dev_info); + +	return sprintf(buf, "%d\n", !!(chip->config1 & ADT7316_INT_EN)); +} + +static ssize_t adt7316_set_int_enabled(struct device *dev, +		struct device_attribute *attr, +		const char *buf, +		size_t len) +{ +	struct iio_dev *dev_info = dev_to_iio_dev(dev); +	struct adt7316_chip_info *chip = iio_priv(dev_info); +	u8 config1; +	int ret; + +	config1 = chip->config1 & (~ADT7316_INT_EN); +	if (buf[0] == '1') +		config1 |= ADT7316_INT_EN; + +	ret = chip->bus.write(chip->bus.client, ADT7316_CONFIG1, config1); +	if (ret) +		return -EIO; + +	chip->config1 = config1; + +	return len; +} + +static IIO_DEVICE_ATTR(int_mask, +		       S_IRUGO | S_IWUSR, +		       adt7316_show_int_mask, adt7316_set_int_mask, +		       0); +static IIO_DEVICE_ATTR(in_temp_high_value, +		       S_IRUGO | S_IWUSR, +		       adt7316_show_ad_bound, adt7316_set_ad_bound, +		       ADT7316_IN_TEMP_HIGH); +static IIO_DEVICE_ATTR(in_temp_low_value, +		       S_IRUGO | S_IWUSR, +		       adt7316_show_ad_bound, adt7316_set_ad_bound, +		       ADT7316_IN_TEMP_LOW); +static IIO_DEVICE_ATTR(ex_temp_high_value, +		       S_IRUGO | S_IWUSR, +		       adt7316_show_ad_bound, adt7316_set_ad_bound, +		       ADT7316_EX_TEMP_HIGH); +static IIO_DEVICE_ATTR(ex_temp_low_value, +		       S_IRUGO | S_IWUSR, +		       adt7316_show_ad_bound, adt7316_set_ad_bound, +		       ADT7316_EX_TEMP_LOW); + +/* NASTY duplication to be fixed */ +static IIO_DEVICE_ATTR(ex_temp_ain1_high_value, +		       S_IRUGO | S_IWUSR, +		       adt7316_show_ad_bound, adt7316_set_ad_bound, +		       ADT7316_EX_TEMP_HIGH); +static IIO_DEVICE_ATTR(ex_temp_ain1_low_value, +		       S_IRUGO | S_IWUSR, +		       adt7316_show_ad_bound, adt7316_set_ad_bound, +		       ADT7316_EX_TEMP_LOW); +static IIO_DEVICE_ATTR(ain2_high_value, +		       S_IRUGO | S_IWUSR, +		       adt7316_show_ad_bound, adt7316_set_ad_bound, +		       ADT7516_AIN2_HIGH); +static IIO_DEVICE_ATTR(ain2_low_value, +		       S_IRUGO | S_IWUSR, +		       adt7316_show_ad_bound, adt7316_set_ad_bound, +		       ADT7516_AIN2_LOW); +static IIO_DEVICE_ATTR(ain3_high_value, +		       S_IRUGO | S_IWUSR, +		       adt7316_show_ad_bound, adt7316_set_ad_bound, +		       ADT7516_AIN3_HIGH); +static IIO_DEVICE_ATTR(ain3_low_value, +		       S_IRUGO | S_IWUSR, +		       adt7316_show_ad_bound, adt7316_set_ad_bound, +		       ADT7516_AIN3_LOW); +static IIO_DEVICE_ATTR(ain4_high_value, +		       S_IRUGO | S_IWUSR, +		       adt7316_show_ad_bound, adt7316_set_ad_bound, +		       ADT7516_AIN4_HIGH); +static IIO_DEVICE_ATTR(ain4_low_value, +		       S_IRUGO | S_IWUSR, +		       adt7316_show_ad_bound, adt7316_set_ad_bound, +		       ADT7516_AIN4_LOW); +static IIO_DEVICE_ATTR(int_enabled, +		       S_IRUGO | S_IWUSR, +		       adt7316_show_int_enabled, +		       adt7316_set_int_enabled, 0); + +static struct attribute *adt7316_event_attributes[] = { +	&iio_dev_attr_int_mask.dev_attr.attr, +	&iio_dev_attr_in_temp_high_value.dev_attr.attr, +	&iio_dev_attr_in_temp_low_value.dev_attr.attr, +	&iio_dev_attr_ex_temp_high_value.dev_attr.attr, +	&iio_dev_attr_ex_temp_low_value.dev_attr.attr, +	&iio_dev_attr_int_enabled.dev_attr.attr, +	NULL, +}; + +static struct attribute_group adt7316_event_attribute_group = { +	.attrs = adt7316_event_attributes, +	.name = "events", +}; + +static struct attribute *adt7516_event_attributes[] = { +	&iio_dev_attr_int_mask.dev_attr.attr, +	&iio_dev_attr_in_temp_high_value.dev_attr.attr, +	&iio_dev_attr_in_temp_low_value.dev_attr.attr, +	&iio_dev_attr_ex_temp_ain1_high_value.dev_attr.attr, +	&iio_dev_attr_ex_temp_ain1_low_value.dev_attr.attr, +	&iio_dev_attr_ain2_high_value.dev_attr.attr, +	&iio_dev_attr_ain2_low_value.dev_attr.attr, +	&iio_dev_attr_ain3_high_value.dev_attr.attr, +	&iio_dev_attr_ain3_low_value.dev_attr.attr, +	&iio_dev_attr_ain4_high_value.dev_attr.attr, +	&iio_dev_attr_ain4_low_value.dev_attr.attr, +	&iio_dev_attr_int_enabled.dev_attr.attr, +	NULL, +}; + +static struct attribute_group adt7516_event_attribute_group = { +	.attrs = adt7516_event_attributes, +	.name = "events", +}; + +#ifdef CONFIG_PM_SLEEP +static int adt7316_disable(struct device *dev) +{ +	struct iio_dev *dev_info = dev_get_drvdata(dev); +	struct adt7316_chip_info *chip = iio_priv(dev_info); + +	return _adt7316_store_enabled(chip, 0); +} + +static int adt7316_enable(struct device *dev) +{ +	struct iio_dev *dev_info = dev_get_drvdata(dev); +	struct adt7316_chip_info *chip = iio_priv(dev_info); + +	return _adt7316_store_enabled(chip, 1); +} + +SIMPLE_DEV_PM_OPS(adt7316_pm_ops, adt7316_disable, adt7316_enable); +EXPORT_SYMBOL_GPL(adt7316_pm_ops); +#endif + +static const struct iio_info adt7316_info = { +	.attrs = &adt7316_attribute_group, +	.event_attrs = &adt7316_event_attribute_group, +	.driver_module = THIS_MODULE, +}; + +static const struct iio_info adt7516_info = { +	.attrs = &adt7516_attribute_group, +	.event_attrs = &adt7516_event_attribute_group, +	.driver_module = THIS_MODULE, +}; + +/* + * device probe and remove + */ +int adt7316_probe(struct device *dev, struct adt7316_bus *bus, +		const char *name) +{ +	struct adt7316_chip_info *chip; +	struct iio_dev *indio_dev; +	unsigned short *adt7316_platform_data = dev->platform_data; +	int ret = 0; + +	indio_dev = devm_iio_device_alloc(dev, sizeof(*chip)); +	if (!indio_dev) +		return -ENOMEM; +	chip = iio_priv(indio_dev); +	/* this is only used for device removal purposes */ +	dev_set_drvdata(dev, indio_dev); + +	chip->bus = *bus; + +	if (name[4] == '3') +		chip->id = ID_ADT7316 + (name[6] - '6'); +	else if (name[4] == '5') +		chip->id = ID_ADT7516 + (name[6] - '6'); +	else +		return -ENODEV; + +	chip->ldac_pin = adt7316_platform_data[1]; +	if (chip->ldac_pin) { +		chip->config3 |= ADT7316_DA_EN_VIA_DAC_LDCA; +		if ((chip->id & ID_FAMILY_MASK) == ID_ADT75XX) +			chip->config1 |= ADT7516_SEL_AIN3; +	} +	chip->int_mask = ADT7316_TEMP_INT_MASK | ADT7316_VDD_INT_MASK; +	if ((chip->id & ID_FAMILY_MASK) == ID_ADT75XX) +		chip->int_mask |= ADT7516_AIN_INT_MASK; + +	indio_dev->dev.parent = dev; +	if ((chip->id & ID_FAMILY_MASK) == ID_ADT75XX) +		indio_dev->info = &adt7516_info; +	else +		indio_dev->info = &adt7316_info; +	indio_dev->name = name; +	indio_dev->modes = INDIO_DIRECT_MODE; + +	if (chip->bus.irq > 0) { +		if (adt7316_platform_data[0]) +			chip->bus.irq_flags = adt7316_platform_data[0]; + +		ret = devm_request_threaded_irq(dev, chip->bus.irq, +						NULL, +						&adt7316_event_handler, +						chip->bus.irq_flags | +						IRQF_ONESHOT, +						indio_dev->name, +						indio_dev); +		if (ret) +			return ret; + +		if (chip->bus.irq_flags & IRQF_TRIGGER_HIGH) +			chip->config1 |= ADT7316_INT_POLARITY; +	} + +	ret = chip->bus.write(chip->bus.client, ADT7316_CONFIG1, chip->config1); +	if (ret) +		return -EIO; + +	ret = chip->bus.write(chip->bus.client, ADT7316_CONFIG3, chip->config3); +	if (ret) +		return -EIO; + +	ret = devm_iio_device_register(dev, indio_dev); +	if (ret) +		return ret; + +	dev_info(dev, "%s temperature sensor, ADC and DAC registered.\n", +			indio_dev->name); + +	return 0; +} +EXPORT_SYMBOL(adt7316_probe); + +MODULE_AUTHOR("Sonic Zhang <sonic.zhang@analog.com>"); +MODULE_DESCRIPTION("Analog Devices ADT7316/7/8 and ADT7516/7/9 digital" +			" temperature sensor, ADC and DAC driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/iio/addac/adt7316.h b/drivers/staging/iio/addac/adt7316.h new file mode 100644 index 00000000000..ec50bf34628 --- /dev/null +++ b/drivers/staging/iio/addac/adt7316.h @@ -0,0 +1,35 @@ +/* + * ADT7316 digital temperature sensor driver supporting ADT7316/7/8 ADT7516/7/9 + * + * Copyright 2010 Analog Devices Inc. + * + * Licensed under the GPL-2 or later. + */ + +#ifndef _ADT7316_H_ +#define _ADT7316_H_ + +#include <linux/types.h> +#include <linux/pm.h> + +#define ADT7316_REG_MAX_ADDR		0x3F + +struct adt7316_bus { +	void *client; +	int irq; +	int irq_flags; +	int (*read)(void *client, u8 reg, u8 *data); +	int (*write)(void *client, u8 reg, u8 val); +	int (*multi_read)(void *client, u8 first_reg, u8 count, u8 *data); +	int (*multi_write)(void *client, u8 first_reg, u8 count, u8 *data); +}; + +#ifdef CONFIG_PM_SLEEP +extern const struct dev_pm_ops adt7316_pm_ops; +#define ADT7316_PM_OPS (&adt7316_pm_ops) +#else +#define ADT7316_PM_OPS NULL +#endif +int adt7316_probe(struct device *dev, struct adt7316_bus *bus, const char *name); + +#endif diff --git a/drivers/staging/iio/cdc/Kconfig b/drivers/staging/iio/cdc/Kconfig new file mode 100644 index 00000000000..80211df8c57 --- /dev/null +++ b/drivers/staging/iio/cdc/Kconfig @@ -0,0 +1,36 @@ +# +# CDC drivers +# +menu "Capacitance to digital converters" + +config AD7150 +	tristate "Analog Devices ad7150/1/6 capacitive sensor driver" +	depends on I2C +	help +	  Say yes here to build support for Analog Devices capacitive sensors. +	  (ad7150, ad7151, ad7156) Provides direct access via sysfs. + +	  To compile this driver as a module, choose M here: the +	  module will be called ad7150. + +config AD7152 +	tristate "Analog Devices ad7152/3 capacitive sensor driver" +	depends on I2C +	help +	  Say yes here to build support for Analog Devices capacitive sensors. +	  (ad7152, ad7153) Provides direct access via sysfs. + +	  To compile this driver as a module, choose M here: the +	  module will be called ad7152. + +config AD7746 +	tristate "Analog Devices AD7745, AD7746 AD7747 capacitive sensor driver" +	depends on I2C +	help +	  Say yes here to build support for Analog Devices capacitive sensors. +	  (AD7745, AD7746, AD7747) Provides direct access via sysfs. + +	  To compile this driver as a module, choose M here: the +	  module will be called ad7746. + +endmenu diff --git a/drivers/staging/iio/cdc/Makefile b/drivers/staging/iio/cdc/Makefile new file mode 100644 index 00000000000..a5fbabf5c8b --- /dev/null +++ b/drivers/staging/iio/cdc/Makefile @@ -0,0 +1,7 @@ +# +# Makefile for industrial I/O DAC drivers +# + +obj-$(CONFIG_AD7150) += ad7150.o +obj-$(CONFIG_AD7152) += ad7152.o +obj-$(CONFIG_AD7746) += ad7746.o diff --git a/drivers/staging/iio/cdc/ad7150.c b/drivers/staging/iio/cdc/ad7150.c new file mode 100644 index 00000000000..047af237630 --- /dev/null +++ b/drivers/staging/iio/cdc/ad7150.c @@ -0,0 +1,683 @@ +/* + * AD7150 capacitive sensor driver supporting AD7150/1/6 + * + * Copyright 2010-2011 Analog Devices Inc. + * + * Licensed under the GPL-2 or later. + */ + +#include <linux/interrupt.h> +#include <linux/device.h> +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/i2c.h> +#include <linux/module.h> + +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> +#include <linux/iio/events.h> +/* + * AD7150 registers definition + */ + +#define AD7150_STATUS              0 +#define AD7150_STATUS_OUT1         (1 << 3) +#define AD7150_STATUS_OUT2         (1 << 5) +#define AD7150_CH1_DATA_HIGH       1 +#define AD7150_CH2_DATA_HIGH       3 +#define AD7150_CH1_AVG_HIGH        5 +#define AD7150_CH2_AVG_HIGH        7 +#define AD7150_CH1_SENSITIVITY     9 +#define AD7150_CH1_THR_HOLD_H      9 +#define AD7150_CH1_TIMEOUT         10 +#define AD7150_CH1_SETUP           11 +#define AD7150_CH2_SENSITIVITY     12 +#define AD7150_CH2_THR_HOLD_H      12 +#define AD7150_CH2_TIMEOUT         13 +#define AD7150_CH2_SETUP           14 +#define AD7150_CFG                 15 +#define AD7150_CFG_FIX             (1 << 7) +#define AD7150_PD_TIMER            16 +#define AD7150_CH1_CAPDAC          17 +#define AD7150_CH2_CAPDAC          18 +#define AD7150_SN3                 19 +#define AD7150_SN2                 20 +#define AD7150_SN1                 21 +#define AD7150_SN0                 22 +#define AD7150_ID                  23 + +/** + * struct ad7150_chip_info - instance specific chip data + * @client: i2c client for this device + * @current_event: device always has one type of event enabled. + *	This element stores the event code of the current one. + * @threshold: thresholds for simple capacitance value events + * @thresh_sensitivity: threshold for simple capacitance offset + *	from 'average' value. + * @mag_sensitity: threshold for magnitude of capacitance offset from + *	from 'average' value. + * @thresh_timeout: a timeout, in samples from the moment an + *	adaptive threshold event occurs to when the average + *	value jumps to current value. + * @mag_timeout: a timeout, in sample from the moment an + *	adaptive magnitude event occurs to when the average + *	value jumps to the current value. + * @old_state: store state from previous event, allowing confirmation + *	of new condition. + * @conversion_mode: the current conversion mode. + * @state_lock: ensure consistent state of this structure wrt the + *	hardware. + */ +struct ad7150_chip_info { +	struct i2c_client *client; +	u64 current_event; +	u16 threshold[2][2]; +	u8 thresh_sensitivity[2][2]; +	u8 mag_sensitivity[2][2]; +	u8 thresh_timeout[2][2]; +	u8 mag_timeout[2][2]; +	int old_state; +	char *conversion_mode; +	struct mutex state_lock; +}; + +/* + * sysfs nodes + */ + +static const u8 ad7150_addresses[][6] = { +	{ AD7150_CH1_DATA_HIGH, AD7150_CH1_AVG_HIGH, +	  AD7150_CH1_SETUP, AD7150_CH1_THR_HOLD_H, +	  AD7150_CH1_SENSITIVITY, AD7150_CH1_TIMEOUT }, +	{ AD7150_CH2_DATA_HIGH, AD7150_CH2_AVG_HIGH, +	  AD7150_CH2_SETUP, AD7150_CH2_THR_HOLD_H, +	  AD7150_CH2_SENSITIVITY, AD7150_CH2_TIMEOUT }, +}; + +static int ad7150_read_raw(struct iio_dev *indio_dev, +			   struct iio_chan_spec const *chan, +			   int *val, +			   int *val2, +			   long mask) +{ +	int ret; +	struct ad7150_chip_info *chip = iio_priv(indio_dev); + +	switch (mask) { +	case IIO_CHAN_INFO_RAW: +		ret = i2c_smbus_read_word_data(chip->client, +					ad7150_addresses[chan->channel][0]); +		if (ret < 0) +			return ret; +		*val = swab16(ret); +		return IIO_VAL_INT; +	case IIO_CHAN_INFO_AVERAGE_RAW: +		ret = i2c_smbus_read_word_data(chip->client, +					ad7150_addresses[chan->channel][1]); +		if (ret < 0) +			return ret; +		*val = swab16(ret); +		return IIO_VAL_INT; +	default: +		return -EINVAL; +	} +} + +static int ad7150_read_event_config(struct iio_dev *indio_dev, +	const struct iio_chan_spec *chan, enum iio_event_type type, +	enum iio_event_direction dir) +{ +	int ret; +	u8 threshtype; +	bool adaptive; +	struct ad7150_chip_info *chip = iio_priv(indio_dev); + +	ret = i2c_smbus_read_byte_data(chip->client, AD7150_CFG); +	if (ret < 0) +		return ret; + +	threshtype = (ret >> 5) & 0x03; +	adaptive = !!(ret & 0x80); + +	switch (type) { +	case IIO_EV_TYPE_MAG_ADAPTIVE: +		if (dir == IIO_EV_DIR_RISING) +			return adaptive && (threshtype == 0x1); +		else +			return adaptive && (threshtype == 0x0); +	case IIO_EV_TYPE_THRESH_ADAPTIVE: +		if (dir == IIO_EV_DIR_RISING) +			return adaptive && (threshtype == 0x3); +		else +			return adaptive && (threshtype == 0x2); + +	case IIO_EV_TYPE_THRESH: +		if (dir == IIO_EV_DIR_RISING) +			return !adaptive && (threshtype == 0x1); +		else +			return !adaptive && (threshtype == 0x0); +	default: +		break; +	} +	return -EINVAL; +} + +/* lock should be held */ +static int ad7150_write_event_params(struct iio_dev *indio_dev, +	 unsigned int chan, enum iio_event_type type, +	 enum iio_event_direction dir) +{ +	int ret; +	u16 value; +	u8 sens, timeout; +	struct ad7150_chip_info *chip = iio_priv(indio_dev); +	int rising = (dir == IIO_EV_DIR_RISING); +	u64 event_code; + +	event_code = IIO_UNMOD_EVENT_CODE(IIO_CAPACITANCE, chan, type, dir); + +	if (event_code != chip->current_event) +		return 0; + +	switch (type) { +		/* Note completely different from the adaptive versions */ +	case IIO_EV_TYPE_THRESH: +		value = chip->threshold[rising][chan]; +		ret = i2c_smbus_write_word_data(chip->client, +						ad7150_addresses[chan][3], +						swab16(value)); +		if (ret < 0) +			return ret; +		return 0; +	case IIO_EV_TYPE_MAG_ADAPTIVE: +		sens = chip->mag_sensitivity[rising][chan]; +		timeout = chip->mag_timeout[rising][chan]; +		break; +	case IIO_EV_TYPE_THRESH_ADAPTIVE: +		sens = chip->thresh_sensitivity[rising][chan]; +		timeout = chip->thresh_timeout[rising][chan]; +		break; +	default: +		return -EINVAL; +	} +	ret = i2c_smbus_write_byte_data(chip->client, +					ad7150_addresses[chan][4], +					sens); +	if (ret < 0) +		return ret; + +	ret = i2c_smbus_write_byte_data(chip->client, +					ad7150_addresses[chan][5], +					timeout); +	if (ret < 0) +		return ret; + +	return 0; +} + +static int ad7150_write_event_config(struct iio_dev *indio_dev, +	const struct iio_chan_spec *chan, enum iio_event_type type, +	enum iio_event_direction dir, int state) +{ +	u8 thresh_type, cfg, adaptive; +	int ret; +	struct ad7150_chip_info *chip = iio_priv(indio_dev); +	int rising = (dir == IIO_EV_DIR_RISING); +	u64 event_code; + +	/* Something must always be turned on */ +	if (state == 0) +		return -EINVAL; + +	event_code = IIO_UNMOD_EVENT_CODE(chan->type, chan->channel, type, dir); +	if (event_code == chip->current_event) +		return 0; +	mutex_lock(&chip->state_lock); +	ret = i2c_smbus_read_byte_data(chip->client, AD7150_CFG); +	if (ret < 0) +		goto error_ret; + +	cfg = ret & ~((0x03 << 5) | (0x1 << 7)); + +	switch (type) { +	case IIO_EV_TYPE_MAG_ADAPTIVE: +		adaptive = 1; +		if (rising) +			thresh_type = 0x1; +		else +			thresh_type = 0x0; +		break; +	case IIO_EV_TYPE_THRESH_ADAPTIVE: +		adaptive = 1; +		if (rising) +			thresh_type = 0x3; +		else +			thresh_type = 0x2; +		break; +	case IIO_EV_TYPE_THRESH: +		adaptive = 0; +		if (rising) +			thresh_type = 0x1; +		else +			thresh_type = 0x0; +		break; +	default: +		ret = -EINVAL; +		goto error_ret; +	} + +	cfg |= (!adaptive << 7) | (thresh_type << 5); + +	ret = i2c_smbus_write_byte_data(chip->client, AD7150_CFG, cfg); +	if (ret < 0) +		goto error_ret; + +	chip->current_event = event_code; + +	/* update control attributes */ +	ret = ad7150_write_event_params(indio_dev, chan->channel, type, dir); +error_ret: +	mutex_unlock(&chip->state_lock); + +	return 0; +} + +static int ad7150_read_event_value(struct iio_dev *indio_dev, +				   const struct iio_chan_spec *chan, +				   enum iio_event_type type, +				   enum iio_event_direction dir, +				   enum iio_event_info info, +				   int *val, int *val2) +{ +	struct ad7150_chip_info *chip = iio_priv(indio_dev); +	int rising = (dir == IIO_EV_DIR_RISING); + +	/* Complex register sharing going on here */ +	switch (type) { +	case IIO_EV_TYPE_MAG_ADAPTIVE: +		*val = chip->mag_sensitivity[rising][chan->channel]; +		return IIO_VAL_INT; +	case IIO_EV_TYPE_THRESH_ADAPTIVE: +		*val = chip->thresh_sensitivity[rising][chan->channel]; +		return IIO_VAL_INT; +	case IIO_EV_TYPE_THRESH: +		*val = chip->threshold[rising][chan->channel]; +		return IIO_VAL_INT; +	default: +		return -EINVAL; +	} +} + +static int ad7150_write_event_value(struct iio_dev *indio_dev, +				   const struct iio_chan_spec *chan, +				   enum iio_event_type type, +				   enum iio_event_direction dir, +				   enum iio_event_info info, +				   int val, int val2) +{ +	int ret; +	struct ad7150_chip_info *chip = iio_priv(indio_dev); +	int rising = (dir == IIO_EV_DIR_RISING); + +	mutex_lock(&chip->state_lock); +	switch (type) { +	case IIO_EV_TYPE_MAG_ADAPTIVE: +		chip->mag_sensitivity[rising][chan->channel] = val; +		break; +	case IIO_EV_TYPE_THRESH_ADAPTIVE: +		chip->thresh_sensitivity[rising][chan->channel] = val; +		break; +	case IIO_EV_TYPE_THRESH: +		chip->threshold[rising][chan->channel] = val; +		break; +	default: +		ret = -EINVAL; +		goto error_ret; +	} + +	/* write back if active */ +	ret = ad7150_write_event_params(indio_dev, chan->channel, type, dir); + +error_ret: +	mutex_unlock(&chip->state_lock); +	return ret; +} + +static ssize_t ad7150_show_timeout(struct device *dev, +				   struct device_attribute *attr, +				   char *buf) +{ +	struct iio_dev *indio_dev = dev_to_iio_dev(dev); +	struct ad7150_chip_info *chip = iio_priv(indio_dev); +	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); +	u8 value; + +	/* use the event code for consistency reasons */ +	int chan = IIO_EVENT_CODE_EXTRACT_CHAN(this_attr->address); +	int rising = !!(IIO_EVENT_CODE_EXTRACT_DIR(this_attr->address) +			== IIO_EV_DIR_RISING); + +	switch (IIO_EVENT_CODE_EXTRACT_TYPE(this_attr->address)) { +	case IIO_EV_TYPE_MAG_ADAPTIVE: +		value = chip->mag_timeout[rising][chan]; +		break; +	case IIO_EV_TYPE_THRESH_ADAPTIVE: +		value = chip->thresh_timeout[rising][chan]; +		break; +	default: +		return -EINVAL; +	} + +	return sprintf(buf, "%d\n", value); +} + +static ssize_t ad7150_store_timeout(struct device *dev, +		struct device_attribute *attr, +		const char *buf, +		size_t len) +{ +	struct iio_dev *indio_dev = dev_to_iio_dev(dev); +	struct ad7150_chip_info *chip = iio_priv(indio_dev); +	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); +	int chan = IIO_EVENT_CODE_EXTRACT_CHAN(this_attr->address); +	enum iio_event_direction dir; +	enum iio_event_type type; +	int rising; +	u8 data; +	int ret; + +	type = IIO_EVENT_CODE_EXTRACT_TYPE(this_attr->address); +	dir = IIO_EVENT_CODE_EXTRACT_DIR(this_attr->address); +	rising = (dir == IIO_EV_DIR_RISING); + +	ret = kstrtou8(buf, 10, &data); +	if (ret < 0) +		return ret; + +	mutex_lock(&chip->state_lock); +	switch (type) { +	case IIO_EV_TYPE_MAG_ADAPTIVE: +		chip->mag_timeout[rising][chan] = data; +		break; +	case IIO_EV_TYPE_THRESH_ADAPTIVE: +		chip->thresh_timeout[rising][chan] = data; +		break; +	default: +		ret = -EINVAL; +		goto error_ret; +	} + +	ret = ad7150_write_event_params(indio_dev, chan, type, dir); +error_ret: +	mutex_unlock(&chip->state_lock); + +	if (ret < 0) +		return ret; + +	return len; +} + +#define AD7150_TIMEOUT(chan, type, dir, ev_type, ev_dir)		\ +	IIO_DEVICE_ATTR(in_capacitance##chan##_##type##_##dir##_timeout, \ +		S_IRUGO | S_IWUSR,					\ +		&ad7150_show_timeout,					\ +		&ad7150_store_timeout,					\ +		IIO_UNMOD_EVENT_CODE(IIO_CAPACITANCE,			\ +				     chan,				\ +				     IIO_EV_TYPE_##ev_type,		\ +				     IIO_EV_DIR_##ev_dir)) +static AD7150_TIMEOUT(0, mag_adaptive, rising, MAG_ADAPTIVE, RISING); +static AD7150_TIMEOUT(0, mag_adaptive, falling, MAG_ADAPTIVE, FALLING); +static AD7150_TIMEOUT(1, mag_adaptive, rising, MAG_ADAPTIVE, RISING); +static AD7150_TIMEOUT(1, mag_adaptive, falling, MAG_ADAPTIVE, FALLING); +static AD7150_TIMEOUT(0, thresh_adaptive, rising, THRESH_ADAPTIVE, RISING); +static AD7150_TIMEOUT(0, thresh_adaptive, falling, THRESH_ADAPTIVE, FALLING); +static AD7150_TIMEOUT(1, thresh_adaptive, rising, THRESH_ADAPTIVE, RISING); +static AD7150_TIMEOUT(1, thresh_adaptive, falling, THRESH_ADAPTIVE, FALLING); + +static const struct iio_event_spec ad7150_events[] = { +	{ +		.type = IIO_EV_TYPE_THRESH, +		.dir = IIO_EV_DIR_RISING, +		.mask_separate = BIT(IIO_EV_INFO_VALUE) | +			BIT(IIO_EV_INFO_ENABLE), +	}, { +		.type = IIO_EV_TYPE_THRESH, +		.dir = IIO_EV_DIR_FALLING, +		.mask_separate = BIT(IIO_EV_INFO_VALUE) | +			BIT(IIO_EV_INFO_ENABLE), +	}, { +		.type = IIO_EV_TYPE_THRESH_ADAPTIVE, +		.dir = IIO_EV_DIR_RISING, +		.mask_separate = BIT(IIO_EV_INFO_VALUE) | +			BIT(IIO_EV_INFO_ENABLE), +	}, { +		.type = IIO_EV_TYPE_THRESH_ADAPTIVE, +		.dir = IIO_EV_DIR_FALLING, +		.mask_separate = BIT(IIO_EV_INFO_VALUE) | +			BIT(IIO_EV_INFO_ENABLE), +	}, { +		.type = IIO_EV_TYPE_MAG_ADAPTIVE, +		.dir = IIO_EV_DIR_RISING, +		.mask_separate = BIT(IIO_EV_INFO_VALUE) | +			BIT(IIO_EV_INFO_ENABLE), +	}, { +		.type = IIO_EV_TYPE_MAG_ADAPTIVE, +		.dir = IIO_EV_DIR_FALLING, +		.mask_separate = BIT(IIO_EV_INFO_VALUE) | +			BIT(IIO_EV_INFO_ENABLE), +	}, +}; + +static const struct iio_chan_spec ad7150_channels[] = { +	{ +		.type = IIO_CAPACITANCE, +		.indexed = 1, +		.channel = 0, +		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | +		BIT(IIO_CHAN_INFO_AVERAGE_RAW), +		.event_spec = ad7150_events, +		.num_event_specs = ARRAY_SIZE(ad7150_events), +	}, { +		.type = IIO_CAPACITANCE, +		.indexed = 1, +		.channel = 1, +		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | +		BIT(IIO_CHAN_INFO_AVERAGE_RAW), +		.event_spec = ad7150_events, +		.num_event_specs = ARRAY_SIZE(ad7150_events), +	}, +}; + +/* + * threshold events + */ + +static irqreturn_t ad7150_event_handler(int irq, void *private) +{ +	struct iio_dev *indio_dev = private; +	struct ad7150_chip_info *chip = iio_priv(indio_dev); +	u8 int_status; +	s64 timestamp = iio_get_time_ns(); +	int ret; + +	ret = i2c_smbus_read_byte_data(chip->client, AD7150_STATUS); +	if (ret < 0) +		return IRQ_HANDLED; + +	int_status = ret; + +	if ((int_status & AD7150_STATUS_OUT1) && +	    !(chip->old_state & AD7150_STATUS_OUT1)) +		iio_push_event(indio_dev, +			       IIO_UNMOD_EVENT_CODE(IIO_CAPACITANCE, +						    0, +						    IIO_EV_TYPE_THRESH, +						    IIO_EV_DIR_RISING), +				timestamp); +	else if ((!(int_status & AD7150_STATUS_OUT1)) && +		 (chip->old_state & AD7150_STATUS_OUT1)) +		iio_push_event(indio_dev, +			       IIO_UNMOD_EVENT_CODE(IIO_CAPACITANCE, +						    0, +						    IIO_EV_TYPE_THRESH, +						    IIO_EV_DIR_FALLING), +			       timestamp); + +	if ((int_status & AD7150_STATUS_OUT2) && +	    !(chip->old_state & AD7150_STATUS_OUT2)) +		iio_push_event(indio_dev, +			       IIO_UNMOD_EVENT_CODE(IIO_CAPACITANCE, +						    1, +						    IIO_EV_TYPE_THRESH, +						    IIO_EV_DIR_RISING), +			       timestamp); +	else if ((!(int_status & AD7150_STATUS_OUT2)) && +		 (chip->old_state & AD7150_STATUS_OUT2)) +		iio_push_event(indio_dev, +			       IIO_UNMOD_EVENT_CODE(IIO_CAPACITANCE, +						    1, +						    IIO_EV_TYPE_THRESH, +						    IIO_EV_DIR_FALLING), +			       timestamp); +	/* store the status to avoid repushing same events */ +	chip->old_state = int_status; + +	return IRQ_HANDLED; +} + +/* Timeouts not currently handled by core */ +static struct attribute *ad7150_event_attributes[] = { +	&iio_dev_attr_in_capacitance0_mag_adaptive_rising_timeout +	.dev_attr.attr, +	&iio_dev_attr_in_capacitance0_mag_adaptive_falling_timeout +	.dev_attr.attr, +	&iio_dev_attr_in_capacitance1_mag_adaptive_rising_timeout +	.dev_attr.attr, +	&iio_dev_attr_in_capacitance1_mag_adaptive_falling_timeout +	.dev_attr.attr, +	&iio_dev_attr_in_capacitance0_thresh_adaptive_rising_timeout +	.dev_attr.attr, +	&iio_dev_attr_in_capacitance0_thresh_adaptive_falling_timeout +	.dev_attr.attr, +	&iio_dev_attr_in_capacitance1_thresh_adaptive_rising_timeout +	.dev_attr.attr, +	&iio_dev_attr_in_capacitance1_thresh_adaptive_falling_timeout +	.dev_attr.attr, +	NULL, +}; + +static struct attribute_group ad7150_event_attribute_group = { +	.attrs = ad7150_event_attributes, +	.name = "events", +}; + +static const struct iio_info ad7150_info = { +	.event_attrs = &ad7150_event_attribute_group, +	.driver_module = THIS_MODULE, +	.read_raw = &ad7150_read_raw, +	.read_event_config = &ad7150_read_event_config, +	.write_event_config = &ad7150_write_event_config, +	.read_event_value = &ad7150_read_event_value, +	.write_event_value = &ad7150_write_event_value, +}; + +/* + * device probe and remove + */ + +static int ad7150_probe(struct i2c_client *client, +		const struct i2c_device_id *id) +{ +	int ret; +	struct ad7150_chip_info *chip; +	struct iio_dev *indio_dev; + +	indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*chip)); +	if (!indio_dev) +		return -ENOMEM; +	chip = iio_priv(indio_dev); +	mutex_init(&chip->state_lock); +	/* this is only used for device removal purposes */ +	i2c_set_clientdata(client, indio_dev); + +	chip->client = client; + +	indio_dev->name = id->name; +	indio_dev->channels = ad7150_channels; +	indio_dev->num_channels = ARRAY_SIZE(ad7150_channels); +	/* Establish that the iio_dev is a child of the i2c device */ +	indio_dev->dev.parent = &client->dev; + +	indio_dev->info = &ad7150_info; + +	indio_dev->modes = INDIO_DIRECT_MODE; + +	if (client->irq) { +		ret = devm_request_threaded_irq(&client->dev, client->irq, +					   NULL, +					   &ad7150_event_handler, +					   IRQF_TRIGGER_RISING | +					   IRQF_TRIGGER_FALLING | +					   IRQF_ONESHOT, +					   "ad7150_irq1", +					   indio_dev); +		if (ret) +			return ret; +	} + +	if (client->dev.platform_data) { +		ret = devm_request_threaded_irq(&client->dev, *(unsigned int *) +					   client->dev.platform_data, +					   NULL, +					   &ad7150_event_handler, +					   IRQF_TRIGGER_RISING | +					   IRQF_TRIGGER_FALLING | +					   IRQF_ONESHOT, +					   "ad7150_irq2", +					   indio_dev); +		if (ret) +			return ret; +	} + +	ret = iio_device_register(indio_dev); +	if (ret) +		return ret; + +	dev_info(&client->dev, "%s capacitive sensor registered,irq: %d\n", +		 id->name, client->irq); + +	return 0; +} + +static int ad7150_remove(struct i2c_client *client) +{ +	struct iio_dev *indio_dev = i2c_get_clientdata(client); + +	iio_device_unregister(indio_dev); + +	return 0; +} + +static const struct i2c_device_id ad7150_id[] = { +	{ "ad7150", 0 }, +	{ "ad7151", 0 }, +	{ "ad7156", 0 }, +	{} +}; + +MODULE_DEVICE_TABLE(i2c, ad7150_id); + +static struct i2c_driver ad7150_driver = { +	.driver = { +		.name = "ad7150", +	}, +	.probe = ad7150_probe, +	.remove = ad7150_remove, +	.id_table = ad7150_id, +}; +module_i2c_driver(ad7150_driver); + +MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>"); +MODULE_DESCRIPTION("Analog Devices AD7150/1/6 capacitive sensor driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/iio/cdc/ad7152.c b/drivers/staging/iio/cdc/ad7152.c new file mode 100644 index 00000000000..87110d940e9 --- /dev/null +++ b/drivers/staging/iio/cdc/ad7152.c @@ -0,0 +1,543 @@ +/* + * AD7152 capacitive sensor driver supporting AD7152/3 + * + * Copyright 2010-2011a Analog Devices Inc. + * + * Licensed under the GPL-2 or later. + */ + +#include <linux/interrupt.h> +#include <linux/device.h> +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/sysfs.h> +#include <linux/i2c.h> +#include <linux/module.h> +#include <linux/delay.h> + +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> + +/* + * TODO: Check compliance of calibbias with abi (units) + */ +/* + * AD7152 registers definition + */ + +#define AD7152_REG_STATUS		0 +#define AD7152_REG_CH1_DATA_HIGH	1 +#define AD7152_REG_CH2_DATA_HIGH	3 +#define AD7152_REG_CH1_OFFS_HIGH	5 +#define AD7152_REG_CH2_OFFS_HIGH	7 +#define AD7152_REG_CH1_GAIN_HIGH	9 +#define AD7152_REG_CH1_SETUP		11 +#define AD7152_REG_CH2_GAIN_HIGH	12 +#define AD7152_REG_CH2_SETUP		14 +#define AD7152_REG_CFG			15 +#define AD7152_REG_RESEVERD		16 +#define AD7152_REG_CAPDAC_POS		17 +#define AD7152_REG_CAPDAC_NEG		18 +#define AD7152_REG_CFG2			26 + +/* Status Register Bit Designations (AD7152_REG_STATUS) */ +#define AD7152_STATUS_RDY1		(1 << 0) +#define AD7152_STATUS_RDY2		(1 << 1) +#define AD7152_STATUS_C1C2		(1 << 2) +#define AD7152_STATUS_PWDN		(1 << 7) + +/* Setup Register Bit Designations (AD7152_REG_CHx_SETUP) */ +#define AD7152_SETUP_CAPDIFF		(1 << 5) +#define AD7152_SETUP_RANGE_2pF		(0 << 6) +#define AD7152_SETUP_RANGE_0_5pF	(1 << 6) +#define AD7152_SETUP_RANGE_1pF		(2 << 6) +#define AD7152_SETUP_RANGE_4pF		(3 << 6) +#define AD7152_SETUP_RANGE(x)		((x) << 6) + +/* Config Register Bit Designations (AD7152_REG_CFG) */ +#define AD7152_CONF_CH2EN		(1 << 3) +#define AD7152_CONF_CH1EN		(1 << 4) +#define AD7152_CONF_MODE_IDLE		(0 << 0) +#define AD7152_CONF_MODE_CONT_CONV	(1 << 0) +#define AD7152_CONF_MODE_SINGLE_CONV	(2 << 0) +#define AD7152_CONF_MODE_OFFS_CAL	(5 << 0) +#define AD7152_CONF_MODE_GAIN_CAL	(6 << 0) + +/* Capdac Register Bit Designations (AD7152_REG_CAPDAC_XXX) */ +#define AD7152_CAPDAC_DACEN		(1 << 7) +#define AD7152_CAPDAC_DACP(x)		((x) & 0x1F) + +/* CFG2 Register Bit Designations (AD7152_REG_CFG2) */ +#define AD7152_CFG2_OSR(x)		(((x) & 0x3) << 4) + +enum { +	AD7152_DATA, +	AD7152_OFFS, +	AD7152_GAIN, +	AD7152_SETUP +}; + +/* + * struct ad7152_chip_info - chip specific information + */ + +struct ad7152_chip_info { +	struct i2c_client *client; +	/* +	 * Capacitive channel digital filter setup; +	 * conversion time/update rate setup per channel +	 */ +	u8	filter_rate_setup; +	u8	setup[2]; +}; + +static inline ssize_t ad7152_start_calib(struct device *dev, +					 struct device_attribute *attr, +					 const char *buf, +					 size_t len, +					 u8 regval) +{ +	struct iio_dev *indio_dev = dev_to_iio_dev(dev); +	struct ad7152_chip_info *chip = iio_priv(indio_dev); +	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); +	bool doit; +	int ret, timeout = 10; + +	ret = strtobool(buf, &doit); +	if (ret < 0) +		return ret; + +	if (!doit) +		return 0; + +	if (this_attr->address == 0) +		regval |= AD7152_CONF_CH1EN; +	else +		regval |= AD7152_CONF_CH2EN; + +	mutex_lock(&indio_dev->mlock); +	ret = i2c_smbus_write_byte_data(chip->client, AD7152_REG_CFG, regval); +	if (ret < 0) { +		mutex_unlock(&indio_dev->mlock); +		return ret; +	} + +	do { +		mdelay(20); +		ret = i2c_smbus_read_byte_data(chip->client, AD7152_REG_CFG); +		if (ret < 0) { +			mutex_unlock(&indio_dev->mlock); +			return ret; +		} +	} while ((ret == regval) && timeout--); + +	mutex_unlock(&indio_dev->mlock); +	return len; +} +static ssize_t ad7152_start_offset_calib(struct device *dev, +					 struct device_attribute *attr, +					 const char *buf, +					 size_t len) +{ +	return ad7152_start_calib(dev, attr, buf, len, +				  AD7152_CONF_MODE_OFFS_CAL); +} +static ssize_t ad7152_start_gain_calib(struct device *dev, +				       struct device_attribute *attr, +				       const char *buf, +				       size_t len) +{ +	return ad7152_start_calib(dev, attr, buf, len, +				  AD7152_CONF_MODE_GAIN_CAL); +} + +static IIO_DEVICE_ATTR(in_capacitance0_calibbias_calibration, +		       S_IWUSR, NULL, ad7152_start_offset_calib, 0); +static IIO_DEVICE_ATTR(in_capacitance1_calibbias_calibration, +		       S_IWUSR, NULL, ad7152_start_offset_calib, 1); +static IIO_DEVICE_ATTR(in_capacitance0_calibscale_calibration, +		       S_IWUSR, NULL, ad7152_start_gain_calib, 0); +static IIO_DEVICE_ATTR(in_capacitance1_calibscale_calibration, +		       S_IWUSR, NULL, ad7152_start_gain_calib, 1); + +/* Values are Update Rate (Hz), Conversion Time (ms) + 1*/ +static const unsigned char ad7152_filter_rate_table[][2] = { +	{200, 5 + 1}, {50, 20 + 1}, {20, 50 + 1}, {17, 60 + 1}, +}; + +static ssize_t ad7152_show_filter_rate_setup(struct device *dev, +		struct device_attribute *attr, +		char *buf) +{ +	struct iio_dev *indio_dev = dev_to_iio_dev(dev); +	struct ad7152_chip_info *chip = iio_priv(indio_dev); + +	return sprintf(buf, "%d\n", +		       ad7152_filter_rate_table[chip->filter_rate_setup][0]); +} + +static ssize_t ad7152_store_filter_rate_setup(struct device *dev, +		struct device_attribute *attr, +		const char *buf, +		size_t len) +{ +	struct iio_dev *indio_dev = dev_to_iio_dev(dev); +	struct ad7152_chip_info *chip = iio_priv(indio_dev); +	u8 data; +	int ret, i; + +	ret = kstrtou8(buf, 10, &data); +	if (ret < 0) +		return ret; + +	for (i = 0; i < ARRAY_SIZE(ad7152_filter_rate_table); i++) +		if (data >= ad7152_filter_rate_table[i][0]) +			break; + +	if (i >= ARRAY_SIZE(ad7152_filter_rate_table)) +		i = ARRAY_SIZE(ad7152_filter_rate_table) - 1; + +	mutex_lock(&indio_dev->mlock); +	ret = i2c_smbus_write_byte_data(chip->client, +			AD7152_REG_CFG2, AD7152_CFG2_OSR(i)); +	if (ret < 0) { +		mutex_unlock(&indio_dev->mlock); +		return ret; +	} + +	chip->filter_rate_setup = i; +	mutex_unlock(&indio_dev->mlock); + +	return len; +} + +static IIO_DEV_ATTR_SAMP_FREQ(S_IRUGO | S_IWUSR, +		ad7152_show_filter_rate_setup, +		ad7152_store_filter_rate_setup); + +static IIO_CONST_ATTR_SAMP_FREQ_AVAIL("200 50 20 17"); + +static IIO_CONST_ATTR(in_capacitance_scale_available, +		      "0.000061050 0.000030525 0.000015263 0.000007631"); + +static struct attribute *ad7152_attributes[] = { +	&iio_dev_attr_sampling_frequency.dev_attr.attr, +	&iio_dev_attr_in_capacitance0_calibbias_calibration.dev_attr.attr, +	&iio_dev_attr_in_capacitance1_calibbias_calibration.dev_attr.attr, +	&iio_dev_attr_in_capacitance0_calibscale_calibration.dev_attr.attr, +	&iio_dev_attr_in_capacitance1_calibscale_calibration.dev_attr.attr, +	&iio_const_attr_in_capacitance_scale_available.dev_attr.attr, +	&iio_const_attr_sampling_frequency_available.dev_attr.attr, +	NULL, +}; + +static const struct attribute_group ad7152_attribute_group = { +	.attrs = ad7152_attributes, +}; + +static const u8 ad7152_addresses[][4] = { +	{ AD7152_REG_CH1_DATA_HIGH, AD7152_REG_CH1_OFFS_HIGH, +	  AD7152_REG_CH1_GAIN_HIGH, AD7152_REG_CH1_SETUP }, +	{ AD7152_REG_CH2_DATA_HIGH, AD7152_REG_CH2_OFFS_HIGH, +	  AD7152_REG_CH2_GAIN_HIGH, AD7152_REG_CH2_SETUP }, +}; + +/* Values are nano relative to pf base. */ +static const int ad7152_scale_table[] = { +	30525, 7631, 15263, 61050 +}; + +static int ad7152_write_raw(struct iio_dev *indio_dev, +			    struct iio_chan_spec const *chan, +			    int val, +			    int val2, +			    long mask) +{ +	struct ad7152_chip_info *chip = iio_priv(indio_dev); +	int ret, i; + +	mutex_lock(&indio_dev->mlock); + +	switch (mask) { +	case IIO_CHAN_INFO_CALIBSCALE: +		if (val != 1) { +			ret = -EINVAL; +			goto out; +		} + +		val = (val2 * 1024) / 15625; + +		ret = i2c_smbus_write_word_data(chip->client, +				ad7152_addresses[chan->channel][AD7152_GAIN], +				swab16(val)); +		if (ret < 0) +			goto out; + +		ret = 0; +		break; + +	case IIO_CHAN_INFO_CALIBBIAS: +		if ((val < 0) | (val > 0xFFFF)) { +			ret = -EINVAL; +			goto out; +		} +		ret = i2c_smbus_write_word_data(chip->client, +				ad7152_addresses[chan->channel][AD7152_OFFS], +				swab16(val)); +		if (ret < 0) +			goto out; + +		ret = 0; +		break; +	case IIO_CHAN_INFO_SCALE: +		if (val != 0) { +			ret = -EINVAL; +			goto out; +		} +		for (i = 0; i < ARRAY_SIZE(ad7152_scale_table); i++) +			if (val2 == ad7152_scale_table[i]) +				break; + +		chip->setup[chan->channel] &= ~AD7152_SETUP_RANGE_4pF; +		chip->setup[chan->channel] |= AD7152_SETUP_RANGE(i); + +		ret = i2c_smbus_write_byte_data(chip->client, +				ad7152_addresses[chan->channel][AD7152_SETUP], +				chip->setup[chan->channel]); +		if (ret < 0) +			goto out; + +		ret = 0; +		break; +	default: +		ret = -EINVAL; +	} + +out: +	mutex_unlock(&indio_dev->mlock); +	return ret; +} +static int ad7152_read_raw(struct iio_dev *indio_dev, +			   struct iio_chan_spec const *chan, +			   int *val, int *val2, +			   long mask) +{ +	struct ad7152_chip_info *chip = iio_priv(indio_dev); +	int ret; +	u8 regval = 0; + +	mutex_lock(&indio_dev->mlock); + +	switch (mask) { +	case IIO_CHAN_INFO_RAW: +		/* First set whether in differential mode */ + +		regval = chip->setup[chan->channel]; + +		if (chan->differential) +			chip->setup[chan->channel] |= AD7152_SETUP_CAPDIFF; +		else +			chip->setup[chan->channel] &= ~AD7152_SETUP_CAPDIFF; + +		if (regval != chip->setup[chan->channel]) { +			ret = i2c_smbus_write_byte_data(chip->client, +				ad7152_addresses[chan->channel][AD7152_SETUP], +				chip->setup[chan->channel]); +			if (ret < 0) +				goto out; +		} +		/* Make sure the channel is enabled */ +		if (chan->channel == 0) +			regval = AD7152_CONF_CH1EN; +		else +			regval = AD7152_CONF_CH2EN; + +		/* Trigger a single read */ +		regval |= AD7152_CONF_MODE_SINGLE_CONV; +		ret = i2c_smbus_write_byte_data(chip->client, AD7152_REG_CFG, +				regval); +		if (ret < 0) +			goto out; + +		msleep(ad7152_filter_rate_table[chip->filter_rate_setup][1]); +		/* Now read the actual register */ +		ret = i2c_smbus_read_word_data(chip->client, +				ad7152_addresses[chan->channel][AD7152_DATA]); +		if (ret < 0) +			goto out; +		*val = swab16(ret); + +		if (chan->differential) +			*val -= 0x8000; + +		ret = IIO_VAL_INT; +		break; +	case IIO_CHAN_INFO_CALIBSCALE: + +		ret = i2c_smbus_read_word_data(chip->client, +				ad7152_addresses[chan->channel][AD7152_GAIN]); +		if (ret < 0) +			goto out; +		/* 1 + gain_val / 2^16 */ +		*val = 1; +		*val2 = (15625 * swab16(ret)) / 1024; + +		ret = IIO_VAL_INT_PLUS_MICRO; +		break; +	case IIO_CHAN_INFO_CALIBBIAS: +		ret = i2c_smbus_read_word_data(chip->client, +				ad7152_addresses[chan->channel][AD7152_OFFS]); +		if (ret < 0) +			goto out; +		*val = swab16(ret); + +		ret = IIO_VAL_INT; +		break; +	case IIO_CHAN_INFO_SCALE: +		ret = i2c_smbus_read_byte_data(chip->client, +				ad7152_addresses[chan->channel][AD7152_SETUP]); +		if (ret < 0) +			goto out; +		*val = 0; +		*val2 = ad7152_scale_table[ret >> 6]; + +		ret = IIO_VAL_INT_PLUS_NANO; +		break; +	default: +		ret = -EINVAL; +	} +out: +	mutex_unlock(&indio_dev->mlock); +	return ret; +} + +static int ad7152_write_raw_get_fmt(struct iio_dev *indio_dev, +			       struct iio_chan_spec const *chan, +			       long mask) +{ +	switch (mask) { +	case IIO_CHAN_INFO_SCALE: +		return IIO_VAL_INT_PLUS_NANO; +	default: +		return IIO_VAL_INT_PLUS_MICRO; +	} +} + +static const struct iio_info ad7152_info = { +	.attrs = &ad7152_attribute_group, +	.read_raw = &ad7152_read_raw, +	.write_raw = &ad7152_write_raw, +	.write_raw_get_fmt = &ad7152_write_raw_get_fmt, +	.driver_module = THIS_MODULE, +}; + +static const struct iio_chan_spec ad7152_channels[] = { +	{ +		.type = IIO_CAPACITANCE, +		.indexed = 1, +		.channel = 0, +		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | +		BIT(IIO_CHAN_INFO_CALIBSCALE) | +		BIT(IIO_CHAN_INFO_CALIBBIAS) | +		BIT(IIO_CHAN_INFO_SCALE), +	}, { +		.type = IIO_CAPACITANCE, +		.differential = 1, +		.indexed = 1, +		.channel = 0, +		.channel2 = 2, +		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | +		BIT(IIO_CHAN_INFO_CALIBSCALE) | +		BIT(IIO_CHAN_INFO_CALIBBIAS) | +		BIT(IIO_CHAN_INFO_SCALE), +	}, { +		.type = IIO_CAPACITANCE, +		.indexed = 1, +		.channel = 1, +		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | +		BIT(IIO_CHAN_INFO_CALIBSCALE) | +		BIT(IIO_CHAN_INFO_CALIBBIAS) | +		BIT(IIO_CHAN_INFO_SCALE), +	}, { +		.type = IIO_CAPACITANCE, +		.differential = 1, +		.indexed = 1, +		.channel = 1, +		.channel2 = 3, +		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | +		BIT(IIO_CHAN_INFO_CALIBSCALE) | +		BIT(IIO_CHAN_INFO_CALIBBIAS) | +		BIT(IIO_CHAN_INFO_SCALE), +	} +}; +/* + * device probe and remove + */ + +static int ad7152_probe(struct i2c_client *client, +		const struct i2c_device_id *id) +{ +	int ret = 0; +	struct ad7152_chip_info *chip; +	struct iio_dev *indio_dev; + +	indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*chip)); +	if (!indio_dev) +		return -ENOMEM; +	chip = iio_priv(indio_dev); +	/* this is only used for device removal purposes */ +	i2c_set_clientdata(client, indio_dev); + +	chip->client = client; + +	/* Establish that the iio_dev is a child of the i2c device */ +	indio_dev->name = id->name; +	indio_dev->dev.parent = &client->dev; +	indio_dev->info = &ad7152_info; +	indio_dev->channels = ad7152_channels; +	if (id->driver_data == 0) +		indio_dev->num_channels = ARRAY_SIZE(ad7152_channels); +	else +		indio_dev->num_channels = 2; +	indio_dev->num_channels = ARRAY_SIZE(ad7152_channels); +	indio_dev->modes = INDIO_DIRECT_MODE; + +	ret = iio_device_register(indio_dev); +	if (ret) +		return ret; + +	dev_err(&client->dev, "%s capacitive sensor registered\n", id->name); + +	return 0; +} + +static int ad7152_remove(struct i2c_client *client) +{ +	struct iio_dev *indio_dev = i2c_get_clientdata(client); + +	iio_device_unregister(indio_dev); + +	return 0; +} + +static const struct i2c_device_id ad7152_id[] = { +	{ "ad7152", 0 }, +	{ "ad7153", 1 }, +	{} +}; + +MODULE_DEVICE_TABLE(i2c, ad7152_id); + +static struct i2c_driver ad7152_driver = { +	.driver = { +		.name = KBUILD_MODNAME, +	}, +	.probe = ad7152_probe, +	.remove = ad7152_remove, +	.id_table = ad7152_id, +}; +module_i2c_driver(ad7152_driver); + +MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>"); +MODULE_DESCRIPTION("Analog Devices AD7152/3 capacitive sensor driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/iio/cdc/ad7746.c b/drivers/staging/iio/cdc/ad7746.c new file mode 100644 index 00000000000..e6e9eaa9eab --- /dev/null +++ b/drivers/staging/iio/cdc/ad7746.c @@ -0,0 +1,791 @@ +/* + * AD7746 capacitive sensor driver supporting AD7745, AD7746 and AD7747 + * + * Copyright 2011 Analog Devices Inc. + * + * Licensed under the GPL-2. + */ + +#include <linux/interrupt.h> +#include <linux/device.h> +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/sysfs.h> +#include <linux/i2c.h> +#include <linux/delay.h> +#include <linux/module.h> +#include <linux/stat.h> + +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> + +#include "ad7746.h" + +/* + * AD7746 Register Definition + */ + +#define AD7746_REG_STATUS		0 +#define AD7746_REG_CAP_DATA_HIGH	1 +#define AD7746_REG_CAP_DATA_MID		2 +#define AD7746_REG_CAP_DATA_LOW		3 +#define AD7746_REG_VT_DATA_HIGH		4 +#define AD7746_REG_VT_DATA_MID		5 +#define AD7746_REG_VT_DATA_LOW		6 +#define AD7746_REG_CAP_SETUP		7 +#define AD7746_REG_VT_SETUP		8 +#define AD7746_REG_EXC_SETUP		9 +#define AD7746_REG_CFG			10 +#define AD7746_REG_CAPDACA		11 +#define AD7746_REG_CAPDACB		12 +#define AD7746_REG_CAP_OFFH		13 +#define AD7746_REG_CAP_OFFL		14 +#define AD7746_REG_CAP_GAINH		15 +#define AD7746_REG_CAP_GAINL		16 +#define AD7746_REG_VOLT_GAINH		17 +#define AD7746_REG_VOLT_GAINL		18 + +/* Status Register Bit Designations (AD7746_REG_STATUS) */ +#define AD7746_STATUS_EXCERR		(1 << 3) +#define AD7746_STATUS_RDY		(1 << 2) +#define AD7746_STATUS_RDYVT		(1 << 1) +#define AD7746_STATUS_RDYCAP		(1 << 0) + +/* Capacitive Channel Setup Register Bit Designations (AD7746_REG_CAP_SETUP) */ +#define AD7746_CAPSETUP_CAPEN		(1 << 7) +#define AD7746_CAPSETUP_CIN2		(1 << 6) /* AD7746 only */ +#define AD7746_CAPSETUP_CAPDIFF		(1 << 5) +#define AD7746_CAPSETUP_CACHOP		(1 << 0) + +/* Voltage/Temperature Setup Register Bit Designations (AD7746_REG_VT_SETUP) */ +#define AD7746_VTSETUP_VTEN		(1 << 7) +#define AD7746_VTSETUP_VTMD_INT_TEMP	(0 << 5) +#define AD7746_VTSETUP_VTMD_EXT_TEMP	(1 << 5) +#define AD7746_VTSETUP_VTMD_VDD_MON	(2 << 5) +#define AD7746_VTSETUP_VTMD_EXT_VIN	(3 << 5) +#define AD7746_VTSETUP_EXTREF		(1 << 4) +#define AD7746_VTSETUP_VTSHORT		(1 << 1) +#define AD7746_VTSETUP_VTCHOP		(1 << 0) + +/* Excitation Setup Register Bit Designations (AD7746_REG_EXC_SETUP) */ +#define AD7746_EXCSETUP_CLKCTRL		(1 << 7) +#define AD7746_EXCSETUP_EXCON		(1 << 6) +#define AD7746_EXCSETUP_EXCB		(1 << 5) +#define AD7746_EXCSETUP_NEXCB		(1 << 4) +#define AD7746_EXCSETUP_EXCA		(1 << 3) +#define AD7746_EXCSETUP_NEXCA		(1 << 2) +#define AD7746_EXCSETUP_EXCLVL(x)	(((x) & 0x3) << 0) + +/* Config Register Bit Designations (AD7746_REG_CFG) */ +#define AD7746_CONF_VTFS(x)		((x) << 6) +#define AD7746_CONF_CAPFS(x)		((x) << 3) +#define AD7746_CONF_MODE_IDLE		(0 << 0) +#define AD7746_CONF_MODE_CONT_CONV	(1 << 0) +#define AD7746_CONF_MODE_SINGLE_CONV	(2 << 0) +#define AD7746_CONF_MODE_PWRDN		(3 << 0) +#define AD7746_CONF_MODE_OFFS_CAL	(5 << 0) +#define AD7746_CONF_MODE_GAIN_CAL	(6 << 0) + +/* CAPDAC Register Bit Designations (AD7746_REG_CAPDACx) */ +#define AD7746_CAPDAC_DACEN		(1 << 7) +#define AD7746_CAPDAC_DACP(x)		((x) & 0x7F) + +/* + * struct ad7746_chip_info - chip specific information + */ + +struct ad7746_chip_info { +	struct i2c_client *client; +	/* +	 * Capacitive channel digital filter setup; +	 * conversion time/update rate setup per channel +	 */ +	u8	config; +	u8	cap_setup; +	u8	vt_setup; +	u8	capdac[2][2]; +	s8	capdac_set; + +	union { +		__be32 d32; +		u8 d8[4]; +	} data ____cacheline_aligned; +}; + +enum ad7746_chan { +	VIN, +	VIN_VDD, +	TEMP_INT, +	TEMP_EXT, +	CIN1, +	CIN1_DIFF, +	CIN2, +	CIN2_DIFF, +}; + +static const struct iio_chan_spec ad7746_channels[] = { +	[VIN] = { +		.type = IIO_VOLTAGE, +		.indexed = 1, +		.channel = 0, +		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), +		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), +		.address = AD7746_REG_VT_DATA_HIGH << 8 | +			AD7746_VTSETUP_VTMD_EXT_VIN, +	}, +	[VIN_VDD] = { +		.type = IIO_VOLTAGE, +		.indexed = 1, +		.channel = 1, +		.extend_name = "supply", +		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), +		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), +		.address = AD7746_REG_VT_DATA_HIGH << 8 | +			AD7746_VTSETUP_VTMD_VDD_MON, +	}, +	[TEMP_INT] = { +		.type = IIO_TEMP, +		.indexed = 1, +		.channel = 0, +		.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), +		.address = AD7746_REG_VT_DATA_HIGH << 8 | +			AD7746_VTSETUP_VTMD_INT_TEMP, +	}, +	[TEMP_EXT] = { +		.type = IIO_TEMP, +		.indexed = 1, +		.channel = 1, +		.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), +		.address = AD7746_REG_VT_DATA_HIGH << 8 | +			AD7746_VTSETUP_VTMD_EXT_TEMP, +	}, +	[CIN1] = { +		.type = IIO_CAPACITANCE, +		.indexed = 1, +		.channel = 0, +		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | +		BIT(IIO_CHAN_INFO_CALIBSCALE) | BIT(IIO_CHAN_INFO_OFFSET), +		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_CALIBBIAS) | +		BIT(IIO_CHAN_INFO_SCALE), +		.address = AD7746_REG_CAP_DATA_HIGH << 8, +	}, +	[CIN1_DIFF] = { +		.type = IIO_CAPACITANCE, +		.differential = 1, +		.indexed = 1, +		.channel = 0, +		.channel2 = 2, +		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | +		BIT(IIO_CHAN_INFO_CALIBSCALE) | BIT(IIO_CHAN_INFO_OFFSET), +		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_CALIBBIAS) | +		BIT(IIO_CHAN_INFO_SCALE), +		.address = AD7746_REG_CAP_DATA_HIGH << 8 | +			AD7746_CAPSETUP_CAPDIFF +	}, +	[CIN2] = { +		.type = IIO_CAPACITANCE, +		.indexed = 1, +		.channel = 1, +		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | +		BIT(IIO_CHAN_INFO_CALIBSCALE) | BIT(IIO_CHAN_INFO_OFFSET), +		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_CALIBBIAS) | +		BIT(IIO_CHAN_INFO_SCALE), +		.address = AD7746_REG_CAP_DATA_HIGH << 8 | +			AD7746_CAPSETUP_CIN2, +	}, +	[CIN2_DIFF] = { +		.type = IIO_CAPACITANCE, +		.differential = 1, +		.indexed = 1, +		.channel = 1, +		.channel2 = 3, +		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | +		BIT(IIO_CHAN_INFO_CALIBSCALE) | BIT(IIO_CHAN_INFO_OFFSET), +		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_CALIBBIAS) | +		BIT(IIO_CHAN_INFO_SCALE), +		.address = AD7746_REG_CAP_DATA_HIGH << 8 | +			AD7746_CAPSETUP_CAPDIFF | AD7746_CAPSETUP_CIN2, +	} +}; + +/* Values are Update Rate (Hz), Conversion Time (ms) + 1*/ +static const unsigned char ad7746_vt_filter_rate_table[][2] = { +	{50, 20 + 1}, {31, 32 + 1}, {16, 62 + 1}, {8, 122 + 1}, +}; + +static const unsigned char ad7746_cap_filter_rate_table[][2] = { +	{91, 11 + 1}, {84, 12 + 1}, {50, 20 + 1}, {26, 38 + 1}, +	{16, 62 + 1}, {13, 77 + 1}, {11, 92 + 1}, {9, 110 + 1}, +}; + +static int ad7746_select_channel(struct iio_dev *indio_dev, +			    struct iio_chan_spec const *chan) +{ +	struct ad7746_chip_info *chip = iio_priv(indio_dev); +	int ret, delay; +	u8 vt_setup, cap_setup; + +	switch (chan->type) { +	case IIO_CAPACITANCE: +		cap_setup = (chan->address & 0xFF) | AD7746_CAPSETUP_CAPEN; +		vt_setup = chip->vt_setup & ~AD7746_VTSETUP_VTEN; +		delay = ad7746_cap_filter_rate_table[(chip->config >> 3) & +			0x7][1]; + +		if (chip->capdac_set != chan->channel) { +			ret = i2c_smbus_write_byte_data(chip->client, +				AD7746_REG_CAPDACA, +				chip->capdac[chan->channel][0]); +			if (ret < 0) +				return ret; +			ret = i2c_smbus_write_byte_data(chip->client, +				AD7746_REG_CAPDACB, +				chip->capdac[chan->channel][1]); +			if (ret < 0) +				return ret; + +			chip->capdac_set = chan->channel; +		} +		break; +	case IIO_VOLTAGE: +	case IIO_TEMP: +		vt_setup = (chan->address & 0xFF) | AD7746_VTSETUP_VTEN; +		cap_setup = chip->cap_setup & ~AD7746_CAPSETUP_CAPEN; +		delay = ad7746_cap_filter_rate_table[(chip->config >> 6) & +			0x3][1]; +		break; +	default: +		return -EINVAL; +	} + +	if (chip->cap_setup != cap_setup) { +		ret = i2c_smbus_write_byte_data(chip->client, +						AD7746_REG_CAP_SETUP, +						cap_setup); +		if (ret < 0) +			return ret; + +		chip->cap_setup = cap_setup; +	} + +	if (chip->vt_setup != vt_setup) { +		ret = i2c_smbus_write_byte_data(chip->client, +						AD7746_REG_VT_SETUP, +						vt_setup); +		if (ret < 0) +			return ret; + +		chip->vt_setup = vt_setup; +	} + +	return delay; +} + +static inline ssize_t ad7746_start_calib(struct device *dev, +					 struct device_attribute *attr, +					 const char *buf, +					 size_t len, +					 u8 regval) +{ +	struct iio_dev *indio_dev = dev_to_iio_dev(dev); +	struct ad7746_chip_info *chip = iio_priv(indio_dev); +	bool doit; +	int ret, timeout = 10; + +	ret = strtobool(buf, &doit); +	if (ret < 0) +		return ret; + +	if (!doit) +		return 0; + +	mutex_lock(&indio_dev->mlock); +	regval |= chip->config; +	ret = i2c_smbus_write_byte_data(chip->client, AD7746_REG_CFG, regval); +	if (ret < 0) { +		mutex_unlock(&indio_dev->mlock); +		return ret; +	} + +	do { +		msleep(20); +		ret = i2c_smbus_read_byte_data(chip->client, AD7746_REG_CFG); +		if (ret < 0) { +			mutex_unlock(&indio_dev->mlock); +			return ret; +		} +	} while ((ret == regval) && timeout--); + +	mutex_unlock(&indio_dev->mlock); + +	return len; +} + +static ssize_t ad7746_start_offset_calib(struct device *dev, +					 struct device_attribute *attr, +					 const char *buf, +					 size_t len) +{ +	struct iio_dev *indio_dev = dev_to_iio_dev(dev); +	int ret = ad7746_select_channel(indio_dev, +			      &ad7746_channels[to_iio_dev_attr(attr)->address]); +	if (ret < 0) +		return ret; + +	return ad7746_start_calib(dev, attr, buf, len, +				  AD7746_CONF_MODE_OFFS_CAL); +} + +static ssize_t ad7746_start_gain_calib(struct device *dev, +				       struct device_attribute *attr, +				       const char *buf, +				       size_t len) +{ +	struct iio_dev *indio_dev = dev_to_iio_dev(dev); +	int ret = ad7746_select_channel(indio_dev, +			      &ad7746_channels[to_iio_dev_attr(attr)->address]); +	if (ret < 0) +		return ret; + +	return ad7746_start_calib(dev, attr, buf, len, +				  AD7746_CONF_MODE_GAIN_CAL); +} + +static IIO_DEVICE_ATTR(in_capacitance0_calibbias_calibration, +		       S_IWUSR, NULL, ad7746_start_offset_calib, CIN1); +static IIO_DEVICE_ATTR(in_capacitance1_calibbias_calibration, +		       S_IWUSR, NULL, ad7746_start_offset_calib, CIN2); +static IIO_DEVICE_ATTR(in_capacitance0_calibscale_calibration, +		       S_IWUSR, NULL, ad7746_start_gain_calib, CIN1); +static IIO_DEVICE_ATTR(in_capacitance1_calibscale_calibration, +		       S_IWUSR, NULL, ad7746_start_gain_calib, CIN2); +static IIO_DEVICE_ATTR(in_voltage0_calibscale_calibration, +		       S_IWUSR, NULL, ad7746_start_gain_calib, VIN); + +static ssize_t ad7746_show_cap_filter_rate_setup(struct device *dev, +		struct device_attribute *attr, +		char *buf) +{ +	struct iio_dev *indio_dev = dev_to_iio_dev(dev); +	struct ad7746_chip_info *chip = iio_priv(indio_dev); + +	return sprintf(buf, "%d\n", ad7746_cap_filter_rate_table[ +			(chip->config >> 3) & 0x7][0]); +} + +static ssize_t ad7746_store_cap_filter_rate_setup(struct device *dev, +		struct device_attribute *attr, +		const char *buf, +		size_t len) +{ +	struct iio_dev *indio_dev = dev_to_iio_dev(dev); +	struct ad7746_chip_info *chip = iio_priv(indio_dev); +	u8 data; +	int ret, i; + +	ret = kstrtou8(buf, 10, &data); +	if (ret < 0) +		return ret; + +	for (i = 0; i < ARRAY_SIZE(ad7746_cap_filter_rate_table); i++) +		if (data >= ad7746_cap_filter_rate_table[i][0]) +			break; + +	if (i >= ARRAY_SIZE(ad7746_cap_filter_rate_table)) +		i = ARRAY_SIZE(ad7746_cap_filter_rate_table) - 1; + +	mutex_lock(&indio_dev->mlock); +	chip->config &= ~AD7746_CONF_CAPFS(0x7); +	chip->config |= AD7746_CONF_CAPFS(i); +	mutex_unlock(&indio_dev->mlock); + +	return len; +} + +static ssize_t ad7746_show_vt_filter_rate_setup(struct device *dev, +		struct device_attribute *attr, +		char *buf) +{ +	struct iio_dev *indio_dev = dev_to_iio_dev(dev); +	struct ad7746_chip_info *chip = iio_priv(indio_dev); + +	return sprintf(buf, "%d\n", ad7746_vt_filter_rate_table[ +			(chip->config >> 6) & 0x3][0]); +} + +static ssize_t ad7746_store_vt_filter_rate_setup(struct device *dev, +		struct device_attribute *attr, +		const char *buf, +		size_t len) +{ +	struct iio_dev *indio_dev = dev_to_iio_dev(dev); +	struct ad7746_chip_info *chip = iio_priv(indio_dev); +	u8 data; +	int ret, i; + +	ret = kstrtou8(buf, 10, &data); +	if (ret < 0) +		return ret; + +	for (i = 0; i < ARRAY_SIZE(ad7746_vt_filter_rate_table); i++) +		if (data >= ad7746_vt_filter_rate_table[i][0]) +			break; + +	if (i >= ARRAY_SIZE(ad7746_vt_filter_rate_table)) +		i = ARRAY_SIZE(ad7746_vt_filter_rate_table) - 1; + +	mutex_lock(&indio_dev->mlock); +	chip->config &= ~AD7746_CONF_VTFS(0x3); +	chip->config |= AD7746_CONF_VTFS(i); +	mutex_unlock(&indio_dev->mlock); + +	return len; +} + +static IIO_DEVICE_ATTR(in_capacitance_sampling_frequency, +		       S_IRUGO | S_IWUSR, ad7746_show_cap_filter_rate_setup, +			ad7746_store_cap_filter_rate_setup, 0); + +static IIO_DEVICE_ATTR(in_voltage_sampling_frequency, +		       S_IRUGO | S_IWUSR, ad7746_show_vt_filter_rate_setup, +		       ad7746_store_vt_filter_rate_setup, 0); + +static IIO_CONST_ATTR(in_voltage_sampling_frequency_available, "50 31 16 8"); +static IIO_CONST_ATTR(in_capacitance_sampling_frequency_available, +		       "91 84 50 26 16 13 11 9"); + +static struct attribute *ad7746_attributes[] = { +	&iio_dev_attr_in_capacitance_sampling_frequency.dev_attr.attr, +	&iio_dev_attr_in_voltage_sampling_frequency.dev_attr.attr, +	&iio_dev_attr_in_capacitance0_calibbias_calibration.dev_attr.attr, +	&iio_dev_attr_in_capacitance0_calibscale_calibration.dev_attr.attr, +	&iio_dev_attr_in_capacitance1_calibscale_calibration.dev_attr.attr, +	&iio_dev_attr_in_capacitance1_calibbias_calibration.dev_attr.attr, +	&iio_dev_attr_in_voltage0_calibscale_calibration.dev_attr.attr, +	&iio_const_attr_in_voltage_sampling_frequency_available.dev_attr.attr, +	&iio_const_attr_in_capacitance_sampling_frequency_available. +	dev_attr.attr, +	NULL, +}; + +static const struct attribute_group ad7746_attribute_group = { +	.attrs = ad7746_attributes, +}; + +static int ad7746_write_raw(struct iio_dev *indio_dev, +			    struct iio_chan_spec const *chan, +			    int val, +			    int val2, +			    long mask) +{ +	struct ad7746_chip_info *chip = iio_priv(indio_dev); +	int ret, reg; + +	mutex_lock(&indio_dev->mlock); + +	switch (mask) { +	case IIO_CHAN_INFO_CALIBSCALE: +		if (val != 1) { +			ret = -EINVAL; +			goto out; +		} + +		val = (val2 * 1024) / 15625; + +		switch (chan->type) { +		case IIO_CAPACITANCE: +			reg = AD7746_REG_CAP_GAINH; +			break; +		case IIO_VOLTAGE: +			reg = AD7746_REG_VOLT_GAINH; +			break; +		default: +			ret = -EINVAL; +			goto out; +		} + +		ret = i2c_smbus_write_word_data(chip->client, reg, swab16(val)); +		if (ret < 0) +			goto out; + +		ret = 0; +		break; +	case IIO_CHAN_INFO_CALIBBIAS: +		if ((val < 0) | (val > 0xFFFF)) { +			ret = -EINVAL; +			goto out; +		} +		ret = i2c_smbus_write_word_data(chip->client, +				AD7746_REG_CAP_OFFH, swab16(val)); +		if (ret < 0) +			goto out; + +		ret = 0; +		break; +	case IIO_CHAN_INFO_OFFSET: +		if ((val < 0) | (val > 43008000)) { /* 21pF */ +			ret = -EINVAL; +			goto out; +		} + +		/* CAPDAC Scale = 21pF_typ / 127 +		 * CIN Scale = 8.192pF / 2^24 +		 * Offset Scale = CAPDAC Scale / CIN Scale = 338646 +		 * */ + +		val /= 338646; + +		chip->capdac[chan->channel][chan->differential] = (val > 0 ? +			AD7746_CAPDAC_DACP(val) | AD7746_CAPDAC_DACEN : 0); + +		ret = i2c_smbus_write_byte_data(chip->client, +			AD7746_REG_CAPDACA, +			chip->capdac[chan->channel][0]); +		if (ret < 0) +			goto out; +		ret = i2c_smbus_write_byte_data(chip->client, +			AD7746_REG_CAPDACB, +			chip->capdac[chan->channel][1]); +		if (ret < 0) +			goto out; + +		chip->capdac_set = chan->channel; + +		ret = 0; +		break; +	default: +		ret = -EINVAL; +	} + +out: +	mutex_unlock(&indio_dev->mlock); +	return ret; +} + +static int ad7746_read_raw(struct iio_dev *indio_dev, +			   struct iio_chan_spec const *chan, +			   int *val, int *val2, +			   long mask) +{ +	struct ad7746_chip_info *chip = iio_priv(indio_dev); +	int ret, delay; +	u8 regval, reg; + +	mutex_lock(&indio_dev->mlock); + +	switch (mask) { +	case IIO_CHAN_INFO_RAW: +	case IIO_CHAN_INFO_PROCESSED: +		ret = ad7746_select_channel(indio_dev, chan); +		if (ret < 0) +			goto out; +		delay = ret; + +		regval = chip->config | AD7746_CONF_MODE_SINGLE_CONV; +		ret = i2c_smbus_write_byte_data(chip->client, AD7746_REG_CFG, +				regval); +		if (ret < 0) +			goto out; + +		msleep(delay); +		/* Now read the actual register */ + +		ret = i2c_smbus_read_i2c_block_data(chip->client, +			chan->address >> 8, 3, &chip->data.d8[1]); + +		if (ret < 0) +			goto out; + +		*val = (be32_to_cpu(chip->data.d32) & 0xFFFFFF) - 0x800000; + +		switch (chan->type) { +		case IIO_TEMP: +		/* temperature in milli degrees Celsius +		 * T = ((*val / 2048) - 4096) * 1000 +		 */ +			*val = (*val * 125) / 256; +			break; +		case IIO_VOLTAGE: +			if (chan->channel == 1) /* supply_raw*/ +				*val = *val * 6; +			break; +		default: +			break; +		} + +		ret = IIO_VAL_INT; +		break; +	case IIO_CHAN_INFO_CALIBSCALE: +		switch (chan->type) { +		case IIO_CAPACITANCE: +			reg = AD7746_REG_CAP_GAINH; +			break; +		case IIO_VOLTAGE: +			reg = AD7746_REG_VOLT_GAINH; +			break; +		default: +			ret = -EINVAL; +			goto out; +		} + +		ret = i2c_smbus_read_word_data(chip->client, reg); +		if (ret < 0) +			goto out; +		/* 1 + gain_val / 2^16 */ +		*val = 1; +		*val2 = (15625 * swab16(ret)) / 1024; + +		ret = IIO_VAL_INT_PLUS_MICRO; +		break; +	case IIO_CHAN_INFO_CALIBBIAS: +		ret = i2c_smbus_read_word_data(chip->client, +					       AD7746_REG_CAP_OFFH); +		if (ret < 0) +			goto out; +		*val = swab16(ret); + +		ret = IIO_VAL_INT; +		break; +	case IIO_CHAN_INFO_OFFSET: +		*val = AD7746_CAPDAC_DACP(chip->capdac[chan->channel] +			[chan->differential]) * 338646; + +		ret = IIO_VAL_INT; +		break; +	case IIO_CHAN_INFO_SCALE: +		switch (chan->type) { +		case IIO_CAPACITANCE: +			/* 8.192pf / 2^24 */ +			*val =  0; +			*val2 = 488; +			ret = IIO_VAL_INT_PLUS_NANO; +			break; +		case IIO_VOLTAGE: +			/* 1170mV / 2^23 */ +			*val = 1170; +			*val2 = 23; +			ret = IIO_VAL_FRACTIONAL_LOG2; +			break; +		default: +			ret = -EINVAL; +			break; +		} + +		break; +	default: +		ret = -EINVAL; +	} +out: +	mutex_unlock(&indio_dev->mlock); +	return ret; +} + +static const struct iio_info ad7746_info = { +	.attrs = &ad7746_attribute_group, +	.read_raw = &ad7746_read_raw, +	.write_raw = &ad7746_write_raw, +	.driver_module = THIS_MODULE, +}; + +/* + * device probe and remove + */ + +static int ad7746_probe(struct i2c_client *client, +		const struct i2c_device_id *id) +{ +	struct ad7746_platform_data *pdata = client->dev.platform_data; +	struct ad7746_chip_info *chip; +	struct iio_dev *indio_dev; +	int ret = 0; +	unsigned char regval = 0; + +	indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*chip)); +	if (!indio_dev) +		return -ENOMEM; +	chip = iio_priv(indio_dev); +	/* this is only used for device removal purposes */ +	i2c_set_clientdata(client, indio_dev); + +	chip->client = client; +	chip->capdac_set = -1; + +	/* Establish that the iio_dev is a child of the i2c device */ +	indio_dev->name = id->name; +	indio_dev->dev.parent = &client->dev; +	indio_dev->info = &ad7746_info; +	indio_dev->channels = ad7746_channels; +	if (id->driver_data == 7746) +		indio_dev->num_channels = ARRAY_SIZE(ad7746_channels); +	else +		indio_dev->num_channels =  ARRAY_SIZE(ad7746_channels) - 2; +	indio_dev->num_channels = ARRAY_SIZE(ad7746_channels); +	indio_dev->modes = INDIO_DIRECT_MODE; + +	if (pdata) { +		if (pdata->exca_en) { +			if (pdata->exca_inv_en) +				regval |= AD7746_EXCSETUP_NEXCA; +			else +				regval |= AD7746_EXCSETUP_EXCA; +		} + +		if (pdata->excb_en) { +			if (pdata->excb_inv_en) +				regval |= AD7746_EXCSETUP_NEXCB; +			else +				regval |= AD7746_EXCSETUP_EXCB; +		} + +		regval |= AD7746_EXCSETUP_EXCLVL(pdata->exclvl); +	} else { +		dev_warn(&client->dev, "No platform data? using default\n"); +		regval = AD7746_EXCSETUP_EXCA | AD7746_EXCSETUP_EXCB | +			AD7746_EXCSETUP_EXCLVL(3); +	} + +	ret = i2c_smbus_write_byte_data(chip->client, +					AD7746_REG_EXC_SETUP, regval); +	if (ret < 0) +		return ret; + +	ret = iio_device_register(indio_dev); +	if (ret) +		return ret; + +	dev_info(&client->dev, "%s capacitive sensor registered\n", id->name); + +	return 0; +} + +static int ad7746_remove(struct i2c_client *client) +{ +	struct iio_dev *indio_dev = i2c_get_clientdata(client); + +	iio_device_unregister(indio_dev); + +	return 0; +} + +static const struct i2c_device_id ad7746_id[] = { +	{ "ad7745", 7745 }, +	{ "ad7746", 7746 }, +	{ "ad7747", 7747 }, +	{} +}; + +MODULE_DEVICE_TABLE(i2c, ad7746_id); + +static struct i2c_driver ad7746_driver = { +	.driver = { +		.name = KBUILD_MODNAME, +	}, +	.probe = ad7746_probe, +	.remove = ad7746_remove, +	.id_table = ad7746_id, +}; +module_i2c_driver(ad7746_driver); + +MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>"); +MODULE_DESCRIPTION("Analog Devices AD7746/5/7 capacitive sensor driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/iio/cdc/ad7746.h b/drivers/staging/iio/cdc/ad7746.h new file mode 100644 index 00000000000..ea8572d1df0 --- /dev/null +++ b/drivers/staging/iio/cdc/ad7746.h @@ -0,0 +1,29 @@ +/* + * AD7746 capacitive sensor driver supporting AD7745, AD7746 and AD7747 + * + * Copyright 2011 Analog Devices Inc. + * + * Licensed under the GPL-2. + */ + +#ifndef IIO_CDC_AD7746_H_ +#define IIO_CDC_AD7746_H_ + +/* + * TODO: struct ad7746_platform_data needs to go into include/linux/iio + */ + +#define AD7466_EXCLVL_0		0 /* +-VDD/8 */ +#define AD7466_EXCLVL_1		1 /* +-VDD/4 */ +#define AD7466_EXCLVL_2		2 /* +-VDD * 3/8 */ +#define AD7466_EXCLVL_3		3 /* +-VDD/2 */ + +struct ad7746_platform_data { +	unsigned char exclvl;	/*Excitation Voltage Level */ +	bool exca_en;		/* enables EXCA pin as the excitation output */ +	bool exca_inv_en;	/* enables /EXCA pin as the excitation output */ +	bool excb_en;		/* enables EXCB pin as the excitation output */ +	bool excb_inv_en;	/* enables /EXCB pin as the excitation output */ +}; + +#endif /* IIO_CDC_AD7746_H_ */ diff --git a/drivers/staging/iio/chrdev.h b/drivers/staging/iio/chrdev.h deleted file mode 100644 index 98d1a2c12df..00000000000 --- a/drivers/staging/iio/chrdev.h +++ /dev/null @@ -1,115 +0,0 @@ -/* The industrial I/O core - character device related - * - * Copyright (c) 2008 Jonathan Cameron - * - * 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 _IIO_CHRDEV_H_ -#define _IIO_CHRDEV_H_ -struct iio_dev; - -/** - * struct iio_handler - Structure used to specify file operations - *			for a particular chrdev - * @chrdev:	character device structure - * @id:		the location in the handler table - used for deallocation. - * @flags:	file operations related flags including busy flag. - * @private:	handler specific data used by the fileops registered with - *		the chrdev. - */ -struct iio_handler { -	struct cdev	chrdev; -	int		id; -	unsigned long	flags; -	void		*private; -}; - -#define iio_cdev_to_handler(cd)				\ -	container_of(cd, struct iio_handler, chrdev) - -/** - * struct iio_event_data - The actual event being pushed to userspace - * @id:		event identifier - * @timestamp:	best estimate of time of event occurance (often from - *		the interrupt handler) - */ -struct iio_event_data { -	int	id; -	s64	timestamp; -}; - -/** - * struct iio_detected_event_list - list element for events that have occured - * @list:		linked list header - * @ev:			the event itself - * @shared_pointer:	used when the event is shared - i.e. can be escallated - *			on demand (eg ring buffer 50%->100% full) - */ -struct iio_detected_event_list { -	struct list_head		list; -	struct iio_event_data		ev; -	struct iio_shared_ev_pointer	*shared_pointer; -}; -/** - * struct iio_shared_ev_pointer - allows shared events to identify if currently - *				in the detected event list - * @ev_p:	pointer to detected event list element (null if not in list) - * @lock:	protect this element to prevent simultaneous edit and remove - */ -struct iio_shared_ev_pointer { -	struct iio_detected_event_list	*ev_p; -	spinlock_t			lock; -}; - -/** - * struct iio_event_interface - chrdev interface for an event line - * @dev:		device assocated with event interface - * @handler:		fileoperations and related control for the chrdev - * @wait:		wait queue to allow blocking reads of events - * @event_list_lock:	mutex to protect the list of detected events - * @det_events:		list of detected events - * @max_events:		maximum number of events before new ones are dropped - * @current_events:	number of events in detected list - * @owner:		ensure the driver module owns the file, not iio - * @private:		driver specific data - * @_name:		used internally to store the sysfs name for minor id - *			attribute - * @_attrname:		the event interface's attribute name - */ -struct iio_event_interface { -	struct device				dev; -	struct iio_handler			handler; -	wait_queue_head_t			wait; -	struct mutex				event_list_lock; -	struct iio_detected_event_list		det_events; -	int					max_events; -	int					current_events; -	struct module				*owner; -	void					*private; -	char					_name[35]; -	char					_attrname[20]; -}; - -/** - * struct iio_event_handler_list - element in list of handlers for events - * @list:		list header - * @refcount:		as the handler may be shared between multiple device - *			side events, reference counting ensures clean removal - * @exist_lock:		prevents race conditions related to refcount useage. - * @handler:		event handler function - called on event if this - *			event_handler is enabled. - * - * Each device has one list of these per interrupt line. - **/ -struct iio_event_handler_list { -	struct list_head	list; -	int			refcount; -	struct mutex		exist_lock; -	int (*handler)(struct iio_dev *dev_info, int index, s64 timestamp, -		       int no_test); -}; - -#endif diff --git a/drivers/staging/iio/frequency/Kconfig b/drivers/staging/iio/frequency/Kconfig new file mode 100644 index 00000000000..93b7141b2c1 --- /dev/null +++ b/drivers/staging/iio/frequency/Kconfig @@ -0,0 +1,61 @@ +# +# Direct Digital Synthesis drivers +# +menu "Direct Digital Synthesis" + +config AD5930 +	tristate "Analog Devices ad5930/5932 driver" +	depends on SPI +	help +	  Say yes here to build support for Analog Devices DDS chip +	  ad5930/ad5932, provides direct access via sysfs. + +config AD9832 +	tristate "Analog Devices ad9832/5 driver" +	depends on SPI +	help +	  Say yes here to build support for Analog Devices DDS chip +	  AD9832 and AD9835, provides direct access via sysfs. + +	  To compile this driver as a module, choose M here: the +	  module will be called ad9832. + +config AD9834 +	tristate "Analog Devices AD9833/4/7/8 driver" +	depends on SPI +	help +	  Say yes here to build support for Analog Devices DDS chip +	  AD9833, AD9834, AD9837 and AD9838, provides direct access via sysfs. + +	  To compile this driver as a module, choose M here: the +	  module will be called ad9834. + +config AD9850 +	tristate "Analog Devices ad9850/1 driver" +	depends on SPI +	help +	  Say yes here to build support for Analog Devices DDS chip +	  ad9850/1, provides direct access via sysfs. + +config AD9852 +	tristate "Analog Devices ad9852/4 driver" +	depends on SPI +	help +	  Say yes here to build support for Analog Devices DDS chip +	  ad9852/4, provides direct access via sysfs. + +config AD9910 +	tristate "Analog Devices ad9910 driver" +	depends on SPI +	help +	  Say yes here to build support for Analog Devices DDS chip +	  ad9910, provides direct access via sysfs. + +config AD9951 +	tristate "Analog Devices ad9951 driver" +	depends on SPI +	help +	  Say yes here to build support for Analog Devices DDS chip +	  ad9951, provides direct access via sysfs. + +endmenu diff --git a/drivers/staging/iio/frequency/Makefile b/drivers/staging/iio/frequency/Makefile new file mode 100644 index 00000000000..147746176b9 --- /dev/null +++ b/drivers/staging/iio/frequency/Makefile @@ -0,0 +1,11 @@ +# +# Makefile for Direct Digital Synthesis drivers +# + +obj-$(CONFIG_AD5930) += ad5930.o +obj-$(CONFIG_AD9832) += ad9832.o +obj-$(CONFIG_AD9834) += ad9834.o +obj-$(CONFIG_AD9850) += ad9850.o +obj-$(CONFIG_AD9852) += ad9852.o +obj-$(CONFIG_AD9910) += ad9910.o +obj-$(CONFIG_AD9951) += ad9951.o diff --git a/drivers/staging/iio/frequency/ad5930.c b/drivers/staging/iio/frequency/ad5930.c new file mode 100644 index 00000000000..a4aeee6ffdf --- /dev/null +++ b/drivers/staging/iio/frequency/ad5930.c @@ -0,0 +1,140 @@ +/* + * Driver for ADI Direct Digital Synthesis ad5930 + * + * Copyright (c) 2010-2010 Analog Devices Inc. + * + * 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/types.h> +#include <linux/mutex.h> +#include <linux/device.h> +#include <linux/spi/spi.h> +#include <linux/slab.h> +#include <linux/sysfs.h> +#include <linux/module.h> + +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> + +#define DRV_NAME "ad5930" + +#define value_mask (u16)0xf000 +#define addr_shift 12 + +/* Register format: 4 bits addr + 12 bits value */ +struct ad5903_config { +	u16 control; +	u16 incnum; +	u16 frqdelt[2]; +	u16 incitvl; +	u16 buritvl; +	u16 strtfrq[2]; +}; + +struct ad5930_state { +	struct mutex lock; +	struct spi_device *sdev; +}; + +static ssize_t ad5930_set_parameter(struct device *dev, +					struct device_attribute *attr, +					const char *buf, +					size_t len) +{ +	struct spi_transfer xfer; +	int ret; +	struct ad5903_config *config = (struct ad5903_config *)buf; +	struct iio_dev *idev = dev_to_iio_dev(dev); +	struct ad5930_state *st = iio_priv(idev); + +	config->control = (config->control & ~value_mask); +	config->incnum = (config->control & ~value_mask) | (1 << addr_shift); +	config->frqdelt[0] = (config->control & ~value_mask) | (2 << addr_shift); +	config->frqdelt[1] = (config->control & ~value_mask) | 3 << addr_shift; +	config->incitvl = (config->control & ~value_mask) | 4 << addr_shift; +	config->buritvl = (config->control & ~value_mask) | 8 << addr_shift; +	config->strtfrq[0] = (config->control & ~value_mask) | 0xc << addr_shift; +	config->strtfrq[1] = (config->control & ~value_mask) | 0xd << addr_shift; + +	xfer.len = len; +	xfer.tx_buf = config; +	mutex_lock(&st->lock); + +	ret = spi_sync_transfer(st->sdev, &xfer, 1); +	if (ret) +		goto error_ret; +error_ret: +	mutex_unlock(&st->lock); + +	return ret ? ret : len; +} + +static IIO_DEVICE_ATTR(dds, S_IWUSR, NULL, ad5930_set_parameter, 0); + +static struct attribute *ad5930_attributes[] = { +	&iio_dev_attr_dds.dev_attr.attr, +	NULL, +}; + +static const struct attribute_group ad5930_attribute_group = { +	.attrs = ad5930_attributes, +}; + +static const struct iio_info ad5930_info = { +	.attrs = &ad5930_attribute_group, +	.driver_module = THIS_MODULE, +}; + +static int ad5930_probe(struct spi_device *spi) +{ +	struct ad5930_state *st; +	struct iio_dev *idev; +	int ret = 0; + +	idev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); +	if (!idev) +		return -ENOMEM; +	spi_set_drvdata(spi, idev); +	st = iio_priv(idev); + +	mutex_init(&st->lock); +	st->sdev = spi; +	idev->dev.parent = &spi->dev; +	idev->info = &ad5930_info; +	idev->modes = INDIO_DIRECT_MODE; + +	ret = iio_device_register(idev); +	if (ret) +		return ret; +	spi->max_speed_hz = 2000000; +	spi->mode = SPI_MODE_3; +	spi->bits_per_word = 16; +	spi_setup(spi); + +	return 0; +} + +static int ad5930_remove(struct spi_device *spi) +{ +	iio_device_unregister(spi_get_drvdata(spi)); + +	return 0; +} + +static struct spi_driver ad5930_driver = { +	.driver = { +		.name = DRV_NAME, +		.owner = THIS_MODULE, +	}, +	.probe = ad5930_probe, +	.remove = ad5930_remove, +}; +module_spi_driver(ad5930_driver); + +MODULE_AUTHOR("Cliff Cai"); +MODULE_DESCRIPTION("Analog Devices ad5930 driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("spi:" DRV_NAME); diff --git a/drivers/staging/iio/frequency/ad9832.c b/drivers/staging/iio/frequency/ad9832.c new file mode 100644 index 00000000000..c7d0307c8e7 --- /dev/null +++ b/drivers/staging/iio/frequency/ad9832.c @@ -0,0 +1,354 @@ +/* + * AD9832 SPI DDS driver + * + * Copyright 2011 Analog Devices Inc. + * + * Licensed under the GPL-2. + */ + +#include <linux/device.h> +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/sysfs.h> +#include <linux/spi/spi.h> +#include <linux/regulator/consumer.h> +#include <linux/err.h> +#include <linux/module.h> +#include <asm/div64.h> + +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> +#include "dds.h" + +#include "ad9832.h" + +static unsigned long ad9832_calc_freqreg(unsigned long mclk, unsigned long fout) +{ +	unsigned long long freqreg = (u64) fout * +				     (u64) ((u64) 1L << AD9832_FREQ_BITS); +	do_div(freqreg, mclk); +	return freqreg; +} + +static int ad9832_write_frequency(struct ad9832_state *st, +				  unsigned addr, unsigned long fout) +{ +	unsigned long regval; + +	if (fout > (st->mclk / 2)) +		return -EINVAL; + +	regval = ad9832_calc_freqreg(st->mclk, fout); + +	st->freq_data[0] = cpu_to_be16((AD9832_CMD_FRE8BITSW << CMD_SHIFT) | +					(addr << ADD_SHIFT) | +					((regval >> 24) & 0xFF)); +	st->freq_data[1] = cpu_to_be16((AD9832_CMD_FRE16BITSW << CMD_SHIFT) | +					((addr - 1) << ADD_SHIFT) | +					((regval >> 16) & 0xFF)); +	st->freq_data[2] = cpu_to_be16((AD9832_CMD_FRE8BITSW << CMD_SHIFT) | +					((addr - 2) << ADD_SHIFT) | +					((regval >> 8) & 0xFF)); +	st->freq_data[3] = cpu_to_be16((AD9832_CMD_FRE16BITSW << CMD_SHIFT) | +					((addr - 3) << ADD_SHIFT) | +					((regval >> 0) & 0xFF)); + +	return spi_sync(st->spi, &st->freq_msg); +} + +static int ad9832_write_phase(struct ad9832_state *st, +				  unsigned long addr, unsigned long phase) +{ +	if (phase > (1 << AD9832_PHASE_BITS)) +		return -EINVAL; + +	st->phase_data[0] = cpu_to_be16((AD9832_CMD_PHA8BITSW << CMD_SHIFT) | +					(addr << ADD_SHIFT) | +					((phase >> 8) & 0xFF)); +	st->phase_data[1] = cpu_to_be16((AD9832_CMD_PHA16BITSW << CMD_SHIFT) | +					((addr - 1) << ADD_SHIFT) | +					(phase & 0xFF)); + +	return spi_sync(st->spi, &st->phase_msg); +} + +static ssize_t ad9832_write(struct device *dev, +		struct device_attribute *attr, +		const char *buf, +		size_t len) +{ +	struct iio_dev *indio_dev = dev_to_iio_dev(dev); +	struct ad9832_state *st = iio_priv(indio_dev); +	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); +	int ret; +	unsigned long val; + +	ret = kstrtoul(buf, 10, &val); +	if (ret) +		goto error_ret; + +	mutex_lock(&indio_dev->mlock); +	switch ((u32) this_attr->address) { +	case AD9832_FREQ0HM: +	case AD9832_FREQ1HM: +		ret = ad9832_write_frequency(st, this_attr->address, val); +		break; +	case AD9832_PHASE0H: +	case AD9832_PHASE1H: +	case AD9832_PHASE2H: +	case AD9832_PHASE3H: +		ret = ad9832_write_phase(st, this_attr->address, val); +		break; +	case AD9832_PINCTRL_EN: +		if (val) +			st->ctrl_ss &= ~AD9832_SELSRC; +		else +			st->ctrl_ss |= AD9832_SELSRC; +		st->data = cpu_to_be16((AD9832_CMD_SYNCSELSRC << CMD_SHIFT) | +					st->ctrl_ss); +		ret = spi_sync(st->spi, &st->msg); +		break; +	case AD9832_FREQ_SYM: +		if (val == 1) +			st->ctrl_fp |= AD9832_FREQ; +		else if (val == 0) +			st->ctrl_fp &= ~AD9832_FREQ; +		else { +			ret = -EINVAL; +			break; +		} +		st->data = cpu_to_be16((AD9832_CMD_FPSELECT << CMD_SHIFT) | +					st->ctrl_fp); +		ret = spi_sync(st->spi, &st->msg); +		break; +	case AD9832_PHASE_SYM: +		if (val < 0 || val > 3) { +			ret = -EINVAL; +			break; +		} + +		st->ctrl_fp &= ~AD9832_PHASE(3); +		st->ctrl_fp |= AD9832_PHASE(val); + +		st->data = cpu_to_be16((AD9832_CMD_FPSELECT << CMD_SHIFT) | +					st->ctrl_fp); +		ret = spi_sync(st->spi, &st->msg); +		break; +	case AD9832_OUTPUT_EN: +		if (val) +			st->ctrl_src &= ~(AD9832_RESET | AD9832_SLEEP | +					AD9832_CLR); +		else +			st->ctrl_src |= AD9832_RESET; + +		st->data = cpu_to_be16((AD9832_CMD_SLEEPRESCLR << CMD_SHIFT) | +					st->ctrl_src); +		ret = spi_sync(st->spi, &st->msg); +		break; +	default: +		ret = -ENODEV; +	} +	mutex_unlock(&indio_dev->mlock); + +error_ret: +	return ret ? ret : len; +} + +/** + * see dds.h for further information + */ + +static IIO_DEV_ATTR_FREQ(0, 0, S_IWUSR, NULL, ad9832_write, AD9832_FREQ0HM); +static IIO_DEV_ATTR_FREQ(0, 1, S_IWUSR, NULL, ad9832_write, AD9832_FREQ1HM); +static IIO_DEV_ATTR_FREQSYMBOL(0, S_IWUSR, NULL, ad9832_write, AD9832_FREQ_SYM); +static IIO_CONST_ATTR_FREQ_SCALE(0, "1"); /* 1Hz */ + +static IIO_DEV_ATTR_PHASE(0, 0, S_IWUSR, NULL, ad9832_write, AD9832_PHASE0H); +static IIO_DEV_ATTR_PHASE(0, 1, S_IWUSR, NULL, ad9832_write, AD9832_PHASE1H); +static IIO_DEV_ATTR_PHASE(0, 2, S_IWUSR, NULL, ad9832_write, AD9832_PHASE2H); +static IIO_DEV_ATTR_PHASE(0, 3, S_IWUSR, NULL, ad9832_write, AD9832_PHASE3H); +static IIO_DEV_ATTR_PHASESYMBOL(0, S_IWUSR, NULL, +				ad9832_write, AD9832_PHASE_SYM); +static IIO_CONST_ATTR_PHASE_SCALE(0, "0.0015339808"); /* 2PI/2^12 rad*/ + +static IIO_DEV_ATTR_PINCONTROL_EN(0, S_IWUSR, NULL, +				ad9832_write, AD9832_PINCTRL_EN); +static IIO_DEV_ATTR_OUT_ENABLE(0, S_IWUSR, NULL, +				ad9832_write, AD9832_OUTPUT_EN); + +static struct attribute *ad9832_attributes[] = { +	&iio_dev_attr_out_altvoltage0_frequency0.dev_attr.attr, +	&iio_dev_attr_out_altvoltage0_frequency1.dev_attr.attr, +	&iio_const_attr_out_altvoltage0_frequency_scale.dev_attr.attr, +	&iio_dev_attr_out_altvoltage0_phase0.dev_attr.attr, +	&iio_dev_attr_out_altvoltage0_phase1.dev_attr.attr, +	&iio_dev_attr_out_altvoltage0_phase2.dev_attr.attr, +	&iio_dev_attr_out_altvoltage0_phase3.dev_attr.attr, +	&iio_const_attr_out_altvoltage0_phase_scale.dev_attr.attr, +	&iio_dev_attr_out_altvoltage0_pincontrol_en.dev_attr.attr, +	&iio_dev_attr_out_altvoltage0_frequencysymbol.dev_attr.attr, +	&iio_dev_attr_out_altvoltage0_phasesymbol.dev_attr.attr, +	&iio_dev_attr_out_altvoltage0_out_enable.dev_attr.attr, +	NULL, +}; + +static const struct attribute_group ad9832_attribute_group = { +	.attrs = ad9832_attributes, +}; + +static const struct iio_info ad9832_info = { +	.attrs = &ad9832_attribute_group, +	.driver_module = THIS_MODULE, +}; + +static int ad9832_probe(struct spi_device *spi) +{ +	struct ad9832_platform_data *pdata = spi->dev.platform_data; +	struct iio_dev *indio_dev; +	struct ad9832_state *st; +	struct regulator *reg; +	int ret; + +	if (!pdata) { +		dev_dbg(&spi->dev, "no platform data?\n"); +		return -ENODEV; +	} + +	reg = devm_regulator_get(&spi->dev, "vcc"); +	if (!IS_ERR(reg)) { +		ret = regulator_enable(reg); +		if (ret) +			return ret; +	} + +	indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); +	if (indio_dev == NULL) { +		ret = -ENOMEM; +		goto error_disable_reg; +	} +	spi_set_drvdata(spi, indio_dev); +	st = iio_priv(indio_dev); +	st->reg = reg; +	st->mclk = pdata->mclk; +	st->spi = spi; + +	indio_dev->dev.parent = &spi->dev; +	indio_dev->name = spi_get_device_id(spi)->name; +	indio_dev->info = &ad9832_info; +	indio_dev->modes = INDIO_DIRECT_MODE; + +	/* Setup default messages */ + +	st->xfer.tx_buf = &st->data; +	st->xfer.len = 2; + +	spi_message_init(&st->msg); +	spi_message_add_tail(&st->xfer, &st->msg); + +	st->freq_xfer[0].tx_buf = &st->freq_data[0]; +	st->freq_xfer[0].len = 2; +	st->freq_xfer[0].cs_change = 1; +	st->freq_xfer[1].tx_buf = &st->freq_data[1]; +	st->freq_xfer[1].len = 2; +	st->freq_xfer[1].cs_change = 1; +	st->freq_xfer[2].tx_buf = &st->freq_data[2]; +	st->freq_xfer[2].len = 2; +	st->freq_xfer[2].cs_change = 1; +	st->freq_xfer[3].tx_buf = &st->freq_data[3]; +	st->freq_xfer[3].len = 2; + +	spi_message_init(&st->freq_msg); +	spi_message_add_tail(&st->freq_xfer[0], &st->freq_msg); +	spi_message_add_tail(&st->freq_xfer[1], &st->freq_msg); +	spi_message_add_tail(&st->freq_xfer[2], &st->freq_msg); +	spi_message_add_tail(&st->freq_xfer[3], &st->freq_msg); + +	st->phase_xfer[0].tx_buf = &st->phase_data[0]; +	st->phase_xfer[0].len = 2; +	st->phase_xfer[0].cs_change = 1; +	st->phase_xfer[1].tx_buf = &st->phase_data[1]; +	st->phase_xfer[1].len = 2; + +	spi_message_init(&st->phase_msg); +	spi_message_add_tail(&st->phase_xfer[0], &st->phase_msg); +	spi_message_add_tail(&st->phase_xfer[1], &st->phase_msg); + +	st->ctrl_src = AD9832_SLEEP | AD9832_RESET | AD9832_CLR; +	st->data = cpu_to_be16((AD9832_CMD_SLEEPRESCLR << CMD_SHIFT) | +					st->ctrl_src); +	ret = spi_sync(st->spi, &st->msg); +	if (ret) { +		dev_err(&spi->dev, "device init failed\n"); +		goto error_disable_reg; +	} + +	ret = ad9832_write_frequency(st, AD9832_FREQ0HM, pdata->freq0); +	if (ret) +		goto error_disable_reg; + +	ret = ad9832_write_frequency(st, AD9832_FREQ1HM, pdata->freq1); +	if (ret) +		goto error_disable_reg; + +	ret = ad9832_write_phase(st, AD9832_PHASE0H, pdata->phase0); +	if (ret) +		goto error_disable_reg; + +	ret = ad9832_write_phase(st, AD9832_PHASE1H, pdata->phase1); +	if (ret) +		goto error_disable_reg; + +	ret = ad9832_write_phase(st, AD9832_PHASE2H, pdata->phase2); +	if (ret) +		goto error_disable_reg; + +	ret = ad9832_write_phase(st, AD9832_PHASE3H, pdata->phase3); +	if (ret) +		goto error_disable_reg; + +	ret = iio_device_register(indio_dev); +	if (ret) +		goto error_disable_reg; + +	return 0; + +error_disable_reg: +	if (!IS_ERR(reg)) +		regulator_disable(reg); + +	return ret; +} + +static int ad9832_remove(struct spi_device *spi) +{ +	struct iio_dev *indio_dev = spi_get_drvdata(spi); +	struct ad9832_state *st = iio_priv(indio_dev); + +	iio_device_unregister(indio_dev); +	if (!IS_ERR(st->reg)) +		regulator_disable(st->reg); + +	return 0; +} + +static const struct spi_device_id ad9832_id[] = { +	{"ad9832", 0}, +	{"ad9835", 0}, +	{} +}; +MODULE_DEVICE_TABLE(spi, ad9832_id); + +static struct spi_driver ad9832_driver = { +	.driver = { +		.name	= "ad9832", +		.owner	= THIS_MODULE, +	}, +	.probe		= ad9832_probe, +	.remove		= ad9832_remove, +	.id_table	= ad9832_id, +}; +module_spi_driver(ad9832_driver); + +MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>"); +MODULE_DESCRIPTION("Analog Devices AD9832/AD9835 DDS"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/iio/frequency/ad9832.h b/drivers/staging/iio/frequency/ad9832.h new file mode 100644 index 00000000000..386f4dc8c9a --- /dev/null +++ b/drivers/staging/iio/frequency/ad9832.h @@ -0,0 +1,126 @@ +/* + * AD9832 SPI DDS driver + * + * Copyright 2011 Analog Devices Inc. + * + * Licensed under the GPL-2 or later. + */ +#ifndef IIO_DDS_AD9832_H_ +#define IIO_DDS_AD9832_H_ + +/* Registers */ + +#define AD9832_FREQ0LL		0x0 +#define AD9832_FREQ0HL		0x1 +#define AD9832_FREQ0LM		0x2 +#define AD9832_FREQ0HM		0x3 +#define AD9832_FREQ1LL		0x4 +#define AD9832_FREQ1HL		0x5 +#define AD9832_FREQ1LM		0x6 +#define AD9832_FREQ1HM		0x7 +#define AD9832_PHASE0L		0x8 +#define AD9832_PHASE0H		0x9 +#define AD9832_PHASE1L		0xA +#define AD9832_PHASE1H		0xB +#define AD9832_PHASE2L		0xC +#define AD9832_PHASE2H		0xD +#define AD9832_PHASE3L		0xE +#define AD9832_PHASE3H		0xF + +#define AD9832_PHASE_SYM	0x10 +#define AD9832_FREQ_SYM		0x11 +#define AD9832_PINCTRL_EN	0x12 +#define AD9832_OUTPUT_EN	0x13 + +/* Command Control Bits */ + +#define AD9832_CMD_PHA8BITSW	0x1 +#define AD9832_CMD_PHA16BITSW	0x0 +#define AD9832_CMD_FRE8BITSW	0x3 +#define AD9832_CMD_FRE16BITSW	0x2 +#define AD9832_CMD_FPSELECT	0x6 +#define AD9832_CMD_SYNCSELSRC	0x8 +#define AD9832_CMD_SLEEPRESCLR	0xC + +#define AD9832_FREQ		(1 << 11) +#define AD9832_PHASE(x)		(((x) & 3) << 9) +#define AD9832_SYNC		(1 << 13) +#define AD9832_SELSRC		(1 << 12) +#define AD9832_SLEEP		(1 << 13) +#define AD9832_RESET		(1 << 12) +#define AD9832_CLR		(1 << 11) +#define CMD_SHIFT		12 +#define ADD_SHIFT		8 +#define AD9832_FREQ_BITS	32 +#define AD9832_PHASE_BITS	12 +#define RES_MASK(bits)		((1 << (bits)) - 1) + +/** + * struct ad9832_state - driver instance specific data + * @spi:		spi_device + * @reg:		supply regulator + * @mclk:		external master clock + * @ctrl_fp:		cached frequency/phase control word + * @ctrl_ss:		cached sync/selsrc control word + * @ctrl_src:		cached sleep/reset/clr word + * @xfer:		default spi transfer + * @msg:		default spi message + * @freq_xfer:		tuning word spi transfer + * @freq_msg:		tuning word spi message + * @phase_xfer:		tuning word spi transfer + * @phase_msg:		tuning word spi message + * @data:		spi transmit buffer + * @phase_data:		tuning word spi transmit buffer + * @freq_data:		tuning word spi transmit buffer + */ + +struct ad9832_state { +	struct spi_device		*spi; +	struct regulator		*reg; +	unsigned long			mclk; +	unsigned short			ctrl_fp; +	unsigned short			ctrl_ss; +	unsigned short			ctrl_src; +	struct spi_transfer		xfer; +	struct spi_message		msg; +	struct spi_transfer		freq_xfer[4]; +	struct spi_message		freq_msg; +	struct spi_transfer		phase_xfer[2]; +	struct spi_message		phase_msg; +	/* +	 * DMA (thus cache coherency maintenance) requires the +	 * transfer buffers to live in their own cache lines. +	 */ +	union { +		__be16			freq_data[4]____cacheline_aligned; +		__be16			phase_data[2]; +		__be16			data; +	}; +}; + +/* + * TODO: struct ad9832_platform_data needs to go into include/linux/iio + */ + +/** + * struct ad9832_platform_data - platform specific information + * @mclk:		master clock in Hz + * @freq0:		power up freq0 tuning word in Hz + * @freq1:		power up freq1 tuning word in Hz + * @phase0:		power up phase0 value [0..4095] correlates with 0..2PI + * @phase1:		power up phase1 value [0..4095] correlates with 0..2PI + * @phase2:		power up phase2 value [0..4095] correlates with 0..2PI + * @phase3:		power up phase3 value [0..4095] correlates with 0..2PI + */ + +struct ad9832_platform_data { +	unsigned long		mclk; +	unsigned long		freq0; +	unsigned long		freq1; +	unsigned short		phase0; +	unsigned short		phase1; +	unsigned short		phase2; +	unsigned short		phase3; +}; + +#endif /* IIO_DDS_AD9832_H_ */ diff --git a/drivers/staging/iio/frequency/ad9834.c b/drivers/staging/iio/frequency/ad9834.c new file mode 100644 index 00000000000..86cda617609 --- /dev/null +++ b/drivers/staging/iio/frequency/ad9834.c @@ -0,0 +1,457 @@ +/* + * AD9833/AD9834/AD9837/AD9838 SPI DDS driver + * + * Copyright 2010-2011 Analog Devices Inc. + * + * Licensed under the GPL-2. + */ + +#include <linux/interrupt.h> +#include <linux/workqueue.h> +#include <linux/device.h> +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/sysfs.h> +#include <linux/list.h> +#include <linux/spi/spi.h> +#include <linux/regulator/consumer.h> +#include <linux/err.h> +#include <linux/module.h> +#include <asm/div64.h> + +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> +#include "dds.h" + +#include "ad9834.h" + +static unsigned int ad9834_calc_freqreg(unsigned long mclk, unsigned long fout) +{ +	unsigned long long freqreg = (u64) fout * (u64) (1 << AD9834_FREQ_BITS); +	do_div(freqreg, mclk); +	return freqreg; +} + +static int ad9834_write_frequency(struct ad9834_state *st, +				  unsigned long addr, unsigned long fout) +{ +	unsigned long regval; + +	if (fout > (st->mclk / 2)) +		return -EINVAL; + +	regval = ad9834_calc_freqreg(st->mclk, fout); + +	st->freq_data[0] = cpu_to_be16(addr | (regval & +				       RES_MASK(AD9834_FREQ_BITS / 2))); +	st->freq_data[1] = cpu_to_be16(addr | ((regval >> +				       (AD9834_FREQ_BITS / 2)) & +				       RES_MASK(AD9834_FREQ_BITS / 2))); + +	return spi_sync(st->spi, &st->freq_msg); +} + +static int ad9834_write_phase(struct ad9834_state *st, +				  unsigned long addr, unsigned long phase) +{ +	if (phase > (1 << AD9834_PHASE_BITS)) +		return -EINVAL; +	st->data = cpu_to_be16(addr | phase); + +	return spi_sync(st->spi, &st->msg); +} + +static ssize_t ad9834_write(struct device *dev, +		struct device_attribute *attr, +		const char *buf, +		size_t len) +{ +	struct iio_dev *indio_dev = dev_to_iio_dev(dev); +	struct ad9834_state *st = iio_priv(indio_dev); +	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); +	int ret; +	unsigned long val; + +	ret = kstrtoul(buf, 10, &val); +	if (ret) +		goto error_ret; + +	mutex_lock(&indio_dev->mlock); +	switch ((u32) this_attr->address) { +	case AD9834_REG_FREQ0: +	case AD9834_REG_FREQ1: +		ret = ad9834_write_frequency(st, this_attr->address, val); +		break; +	case AD9834_REG_PHASE0: +	case AD9834_REG_PHASE1: +		ret = ad9834_write_phase(st, this_attr->address, val); +		break; +	case AD9834_OPBITEN: +		if (st->control & AD9834_MODE) { +			ret = -EINVAL;  /* AD9843 reserved mode */ +			break; +		} + +		if (val) +			st->control |= AD9834_OPBITEN; +		else +			st->control &= ~AD9834_OPBITEN; + +		st->data = cpu_to_be16(AD9834_REG_CMD | st->control); +		ret = spi_sync(st->spi, &st->msg); +		break; +	case AD9834_PIN_SW: +		if (val) +			st->control |= AD9834_PIN_SW; +		else +			st->control &= ~AD9834_PIN_SW; +		st->data = cpu_to_be16(AD9834_REG_CMD | st->control); +		ret = spi_sync(st->spi, &st->msg); +		break; +	case AD9834_FSEL: +	case AD9834_PSEL: +		if (val == 0) +			st->control &= ~(this_attr->address | AD9834_PIN_SW); +		else if (val == 1) { +			st->control |= this_attr->address; +			st->control &= ~AD9834_PIN_SW; +		} else { +			ret = -EINVAL; +			break; +		} +		st->data = cpu_to_be16(AD9834_REG_CMD | st->control); +		ret = spi_sync(st->spi, &st->msg); +		break; +	case AD9834_RESET: +		if (val) +			st->control &= ~AD9834_RESET; +		else +			st->control |= AD9834_RESET; + +		st->data = cpu_to_be16(AD9834_REG_CMD | st->control); +		ret = spi_sync(st->spi, &st->msg); +		break; +	default: +		ret = -ENODEV; +	} +	mutex_unlock(&indio_dev->mlock); + +error_ret: +	return ret ? ret : len; +} + +static ssize_t ad9834_store_wavetype(struct device *dev, +				 struct device_attribute *attr, +				 const char *buf, +				 size_t len) +{ +	struct iio_dev *indio_dev = dev_to_iio_dev(dev); +	struct ad9834_state *st = iio_priv(indio_dev); +	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); +	int ret = 0; +	bool is_ad9833_7 = (st->devid == ID_AD9833) || (st->devid == ID_AD9837); + +	mutex_lock(&indio_dev->mlock); + +	switch ((u32) this_attr->address) { +	case 0: +		if (sysfs_streq(buf, "sine")) { +			st->control &= ~AD9834_MODE; +			if (is_ad9833_7) +				st->control &= ~AD9834_OPBITEN; +		} else if (sysfs_streq(buf, "triangle")) { +			if (is_ad9833_7) { +				st->control &= ~AD9834_OPBITEN; +				st->control |= AD9834_MODE; +			} else if (st->control & AD9834_OPBITEN) { +				ret = -EINVAL;	/* AD9843 reserved mode */ +			} else { +				st->control |= AD9834_MODE; +			} +		} else if (is_ad9833_7 && sysfs_streq(buf, "square")) { +			st->control &= ~AD9834_MODE; +			st->control |= AD9834_OPBITEN; +		} else { +			ret = -EINVAL; +		} + +		break; +	case 1: +		if (sysfs_streq(buf, "square") && +			!(st->control & AD9834_MODE)) { +			st->control &= ~AD9834_MODE; +			st->control |= AD9834_OPBITEN; +		} else { +			ret = -EINVAL; +		} +		break; +	default: +		ret = -EINVAL; +		break; +	} + +	if (!ret) { +		st->data = cpu_to_be16(AD9834_REG_CMD | st->control); +		ret = spi_sync(st->spi, &st->msg); +	} +	mutex_unlock(&indio_dev->mlock); + +	return ret ? ret : len; +} + +static ssize_t ad9834_show_out0_wavetype_available(struct device *dev, +						struct device_attribute *attr, +						char *buf) +{ +	struct iio_dev *indio_dev = dev_to_iio_dev(dev); +	struct ad9834_state *st = iio_priv(indio_dev); +	char *str; + +	if ((st->devid == ID_AD9833) || (st->devid == ID_AD9837)) +		str = "sine triangle square"; +	else if (st->control & AD9834_OPBITEN) +		str = "sine"; +	else +		str = "sine triangle"; + +	return sprintf(buf, "%s\n", str); +} + + +static IIO_DEVICE_ATTR(out_altvoltage0_out0_wavetype_available, S_IRUGO, +		       ad9834_show_out0_wavetype_available, NULL, 0); + +static ssize_t ad9834_show_out1_wavetype_available(struct device *dev, +						struct device_attribute *attr, +						char *buf) +{ +	struct iio_dev *indio_dev = dev_to_iio_dev(dev); +	struct ad9834_state *st = iio_priv(indio_dev); +	char *str; + +	if (st->control & AD9834_MODE) +		str = ""; +	else +		str = "square"; + +	return sprintf(buf, "%s\n", str); +} + +static IIO_DEVICE_ATTR(out_altvoltage0_out1_wavetype_available, S_IRUGO, +		       ad9834_show_out1_wavetype_available, NULL, 0); + +/** + * see dds.h for further information + */ + +static IIO_DEV_ATTR_FREQ(0, 0, S_IWUSR, NULL, ad9834_write, AD9834_REG_FREQ0); +static IIO_DEV_ATTR_FREQ(0, 1, S_IWUSR, NULL, ad9834_write, AD9834_REG_FREQ1); +static IIO_DEV_ATTR_FREQSYMBOL(0, S_IWUSR, NULL, ad9834_write, AD9834_FSEL); +static IIO_CONST_ATTR_FREQ_SCALE(0, "1"); /* 1Hz */ + +static IIO_DEV_ATTR_PHASE(0, 0, S_IWUSR, NULL, ad9834_write, AD9834_REG_PHASE0); +static IIO_DEV_ATTR_PHASE(0, 1, S_IWUSR, NULL, ad9834_write, AD9834_REG_PHASE1); +static IIO_DEV_ATTR_PHASESYMBOL(0, S_IWUSR, NULL, ad9834_write, AD9834_PSEL); +static IIO_CONST_ATTR_PHASE_SCALE(0, "0.0015339808"); /* 2PI/2^12 rad*/ + +static IIO_DEV_ATTR_PINCONTROL_EN(0, S_IWUSR, NULL, +	ad9834_write, AD9834_PIN_SW); +static IIO_DEV_ATTR_OUT_ENABLE(0, S_IWUSR, NULL, ad9834_write, AD9834_RESET); +static IIO_DEV_ATTR_OUTY_ENABLE(0, 1, S_IWUSR, NULL, +	ad9834_write, AD9834_OPBITEN); +static IIO_DEV_ATTR_OUT_WAVETYPE(0, 0, ad9834_store_wavetype, 0); +static IIO_DEV_ATTR_OUT_WAVETYPE(0, 1, ad9834_store_wavetype, 1); + +static struct attribute *ad9834_attributes[] = { +	&iio_dev_attr_out_altvoltage0_frequency0.dev_attr.attr, +	&iio_dev_attr_out_altvoltage0_frequency1.dev_attr.attr, +	&iio_const_attr_out_altvoltage0_frequency_scale.dev_attr.attr, +	&iio_dev_attr_out_altvoltage0_phase0.dev_attr.attr, +	&iio_dev_attr_out_altvoltage0_phase1.dev_attr.attr, +	&iio_const_attr_out_altvoltage0_phase_scale.dev_attr.attr, +	&iio_dev_attr_out_altvoltage0_pincontrol_en.dev_attr.attr, +	&iio_dev_attr_out_altvoltage0_frequencysymbol.dev_attr.attr, +	&iio_dev_attr_out_altvoltage0_phasesymbol.dev_attr.attr, +	&iio_dev_attr_out_altvoltage0_out_enable.dev_attr.attr, +	&iio_dev_attr_out_altvoltage0_out1_enable.dev_attr.attr, +	&iio_dev_attr_out_altvoltage0_out0_wavetype.dev_attr.attr, +	&iio_dev_attr_out_altvoltage0_out1_wavetype.dev_attr.attr, +	&iio_dev_attr_out_altvoltage0_out0_wavetype_available.dev_attr.attr, +	&iio_dev_attr_out_altvoltage0_out1_wavetype_available.dev_attr.attr, +	NULL, +}; + +static struct attribute *ad9833_attributes[] = { +	&iio_dev_attr_out_altvoltage0_frequency0.dev_attr.attr, +	&iio_dev_attr_out_altvoltage0_frequency1.dev_attr.attr, +	&iio_const_attr_out_altvoltage0_frequency_scale.dev_attr.attr, +	&iio_dev_attr_out_altvoltage0_phase0.dev_attr.attr, +	&iio_dev_attr_out_altvoltage0_phase1.dev_attr.attr, +	&iio_const_attr_out_altvoltage0_phase_scale.dev_attr.attr, +	&iio_dev_attr_out_altvoltage0_frequencysymbol.dev_attr.attr, +	&iio_dev_attr_out_altvoltage0_phasesymbol.dev_attr.attr, +	&iio_dev_attr_out_altvoltage0_out_enable.dev_attr.attr, +	&iio_dev_attr_out_altvoltage0_out0_wavetype.dev_attr.attr, +	&iio_dev_attr_out_altvoltage0_out0_wavetype_available.dev_attr.attr, +	NULL, +}; + +static const struct attribute_group ad9834_attribute_group = { +	.attrs = ad9834_attributes, +}; + +static const struct attribute_group ad9833_attribute_group = { +	.attrs = ad9833_attributes, +}; + +static const struct iio_info ad9834_info = { +	.attrs = &ad9834_attribute_group, +	.driver_module = THIS_MODULE, +}; + +static const struct iio_info ad9833_info = { +	.attrs = &ad9833_attribute_group, +	.driver_module = THIS_MODULE, +}; + +static int ad9834_probe(struct spi_device *spi) +{ +	struct ad9834_platform_data *pdata = spi->dev.platform_data; +	struct ad9834_state *st; +	struct iio_dev *indio_dev; +	struct regulator *reg; +	int ret; + +	if (!pdata) { +		dev_dbg(&spi->dev, "no platform data?\n"); +		return -ENODEV; +	} + +	reg = devm_regulator_get(&spi->dev, "vcc"); +	if (!IS_ERR(reg)) { +		ret = regulator_enable(reg); +		if (ret) +			return ret; +	} + +	indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); +	if (indio_dev == NULL) { +		ret = -ENOMEM; +		goto error_disable_reg; +	} +	spi_set_drvdata(spi, indio_dev); +	st = iio_priv(indio_dev); +	st->mclk = pdata->mclk; +	st->spi = spi; +	st->devid = spi_get_device_id(spi)->driver_data; +	st->reg = reg; +	indio_dev->dev.parent = &spi->dev; +	indio_dev->name = spi_get_device_id(spi)->name; +	switch (st->devid) { +	case ID_AD9833: +	case ID_AD9837: +		indio_dev->info = &ad9833_info; +		break; +	default: +		indio_dev->info = &ad9834_info; +		break; +	} +	indio_dev->modes = INDIO_DIRECT_MODE; + +	/* Setup default messages */ + +	st->xfer.tx_buf = &st->data; +	st->xfer.len = 2; + +	spi_message_init(&st->msg); +	spi_message_add_tail(&st->xfer, &st->msg); + +	st->freq_xfer[0].tx_buf = &st->freq_data[0]; +	st->freq_xfer[0].len = 2; +	st->freq_xfer[0].cs_change = 1; +	st->freq_xfer[1].tx_buf = &st->freq_data[1]; +	st->freq_xfer[1].len = 2; + +	spi_message_init(&st->freq_msg); +	spi_message_add_tail(&st->freq_xfer[0], &st->freq_msg); +	spi_message_add_tail(&st->freq_xfer[1], &st->freq_msg); + +	st->control = AD9834_B28 | AD9834_RESET; + +	if (!pdata->en_div2) +		st->control |= AD9834_DIV2; + +	if (!pdata->en_signbit_msb_out && (st->devid == ID_AD9834)) +		st->control |= AD9834_SIGN_PIB; + +	st->data = cpu_to_be16(AD9834_REG_CMD | st->control); +	ret = spi_sync(st->spi, &st->msg); +	if (ret) { +		dev_err(&spi->dev, "device init failed\n"); +		goto error_disable_reg; +	} + +	ret = ad9834_write_frequency(st, AD9834_REG_FREQ0, pdata->freq0); +	if (ret) +		goto error_disable_reg; + +	ret = ad9834_write_frequency(st, AD9834_REG_FREQ1, pdata->freq1); +	if (ret) +		goto error_disable_reg; + +	ret = ad9834_write_phase(st, AD9834_REG_PHASE0, pdata->phase0); +	if (ret) +		goto error_disable_reg; + +	ret = ad9834_write_phase(st, AD9834_REG_PHASE1, pdata->phase1); +	if (ret) +		goto error_disable_reg; + +	ret = iio_device_register(indio_dev); +	if (ret) +		goto error_disable_reg; + +	return 0; + +error_disable_reg: +	if (!IS_ERR(reg)) +		regulator_disable(reg); + +	return ret; +} + +static int ad9834_remove(struct spi_device *spi) +{ +	struct iio_dev *indio_dev = spi_get_drvdata(spi); +	struct ad9834_state *st = iio_priv(indio_dev); + +	iio_device_unregister(indio_dev); +	if (!IS_ERR(st->reg)) +		regulator_disable(st->reg); + +	return 0; +} + +static const struct spi_device_id ad9834_id[] = { +	{"ad9833", ID_AD9833}, +	{"ad9834", ID_AD9834}, +	{"ad9837", ID_AD9837}, +	{"ad9838", ID_AD9838}, +	{} +}; +MODULE_DEVICE_TABLE(spi, ad9834_id); + +static struct spi_driver ad9834_driver = { +	.driver = { +		.name	= "ad9834", +		.owner	= THIS_MODULE, +	}, +	.probe		= ad9834_probe, +	.remove		= ad9834_remove, +	.id_table	= ad9834_id, +}; +module_spi_driver(ad9834_driver); + +MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>"); +MODULE_DESCRIPTION("Analog Devices AD9833/AD9834/AD9837/AD9838 DDS"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/iio/frequency/ad9834.h b/drivers/staging/iio/frequency/ad9834.h new file mode 100644 index 00000000000..8ca6e52bae6 --- /dev/null +++ b/drivers/staging/iio/frequency/ad9834.h @@ -0,0 +1,112 @@ +/* + * AD9833/AD9834/AD9837/AD9838 SPI DDS driver + * + * Copyright 2010-2011 Analog Devices Inc. + * + * Licensed under the GPL-2. + */ +#ifndef IIO_DDS_AD9834_H_ +#define IIO_DDS_AD9834_H_ + +/* Registers */ + +#define AD9834_REG_CMD		(0 << 14) +#define AD9834_REG_FREQ0	(1 << 14) +#define AD9834_REG_FREQ1	(2 << 14) +#define AD9834_REG_PHASE0	(6 << 13) +#define AD9834_REG_PHASE1	(7 << 13) + +/* Command Control Bits */ + +#define AD9834_B28		(1 << 13) +#define AD9834_HLB		(1 << 12) +#define AD9834_FSEL		(1 << 11) +#define AD9834_PSEL		(1 << 10) +#define AD9834_PIN_SW		(1 << 9) +#define AD9834_RESET		(1 << 8) +#define AD9834_SLEEP1		(1 << 7) +#define AD9834_SLEEP12		(1 << 6) +#define AD9834_OPBITEN		(1 << 5) +#define AD9834_SIGN_PIB		(1 << 4) +#define AD9834_DIV2		(1 << 3) +#define AD9834_MODE		(1 << 1) + +#define AD9834_FREQ_BITS	28 +#define AD9834_PHASE_BITS	12 + +#define RES_MASK(bits)	((1 << (bits)) - 1) + +/** + * struct ad9834_state - driver instance specific data + * @spi:		spi_device + * @reg:		supply regulator + * @mclk:		external master clock + * @control:		cached control word + * @xfer:		default spi transfer + * @msg:		default spi message + * @freq_xfer:		tuning word spi transfer + * @freq_msg:		tuning word spi message + * @data:		spi transmit buffer + * @freq_data:		tuning word spi transmit buffer + */ + +struct ad9834_state { +	struct spi_device		*spi; +	struct regulator		*reg; +	unsigned int			mclk; +	unsigned short			control; +	unsigned short			devid; +	struct spi_transfer		xfer; +	struct spi_message		msg; +	struct spi_transfer		freq_xfer[2]; +	struct spi_message		freq_msg; + +	/* +	 * DMA (thus cache coherency maintenance) requires the +	 * transfer buffers to live in their own cache lines. +	 */ +	__be16				data ____cacheline_aligned; +	__be16				freq_data[2]; +}; + + +/* + * TODO: struct ad7887_platform_data needs to go into include/linux/iio + */ + +/** + * struct ad9834_platform_data - platform specific information + * @mclk:		master clock in Hz + * @freq0:		power up freq0 tuning word in Hz + * @freq1:		power up freq1 tuning word in Hz + * @phase0:		power up phase0 value [0..4095] correlates with 0..2PI + * @phase1:		power up phase1 value [0..4095] correlates with 0..2PI + * @en_div2:		digital output/2 is passed to the SIGN BIT OUT pin + * @en_signbit_msb_out:	the MSB (or MSB/2) of the DAC data is connected to the + *			SIGN BIT OUT pin. en_div2 controls whether it is the MSB + *			or MSB/2 that is output. if en_signbit_msb_out=false, + *			the on-board comparator is connected to SIGN BIT OUT + */ + +struct ad9834_platform_data { +	unsigned int		mclk; +	unsigned int		freq0; +	unsigned int		freq1; +	unsigned short		phase0; +	unsigned short		phase1; +	bool			en_div2; +	bool			en_signbit_msb_out; +}; + +/** + * ad9834_supported_device_ids: + */ + +enum ad9834_supported_device_ids { +	ID_AD9833, +	ID_AD9834, +	ID_AD9837, +	ID_AD9838, +}; + +#endif /* IIO_DDS_AD9834_H_ */ diff --git a/drivers/staging/iio/frequency/ad9850.c b/drivers/staging/iio/frequency/ad9850.c new file mode 100644 index 00000000000..af877ff680e --- /dev/null +++ b/drivers/staging/iio/frequency/ad9850.c @@ -0,0 +1,126 @@ +/* + * Driver for ADI Direct Digital Synthesis ad9850 + * + * Copyright (c) 2010-2010 Analog Devices Inc. + * + * 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/types.h> +#include <linux/mutex.h> +#include <linux/device.h> +#include <linux/spi/spi.h> +#include <linux/slab.h> +#include <linux/sysfs.h> +#include <linux/module.h> + +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> + +#define DRV_NAME "ad9850" + +#define value_mask (u16)0xf000 +#define addr_shift 12 + +/* Register format: 4 bits addr + 12 bits value */ +struct ad9850_config { +	u8 control[5]; +}; + +struct ad9850_state { +	struct mutex lock; +	struct spi_device *sdev; +}; + +static ssize_t ad9850_set_parameter(struct device *dev, +					struct device_attribute *attr, +					const char *buf, +					size_t len) +{ +	struct spi_transfer xfer; +	int ret; +	struct ad9850_config *config = (struct ad9850_config *)buf; +	struct iio_dev *idev = dev_to_iio_dev(dev); +	struct ad9850_state *st = iio_priv(idev); + +	xfer.len = len; +	xfer.tx_buf = config; +	mutex_lock(&st->lock); + +	ret = spi_sync_transfer(st->sdev, &xfer, 1); +	if (ret) +		goto error_ret; +error_ret: +	mutex_unlock(&st->lock); + +	return ret ? ret : len; +} + +static IIO_DEVICE_ATTR(dds, S_IWUSR, NULL, ad9850_set_parameter, 0); + +static struct attribute *ad9850_attributes[] = { +	&iio_dev_attr_dds.dev_attr.attr, +	NULL, +}; + +static const struct attribute_group ad9850_attribute_group = { +	.attrs = ad9850_attributes, +}; + +static const struct iio_info ad9850_info = { +	.attrs = &ad9850_attribute_group, +	.driver_module = THIS_MODULE, +}; + +static int ad9850_probe(struct spi_device *spi) +{ +	struct ad9850_state *st; +	struct iio_dev *idev; +	int ret = 0; + +	idev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); +	if (!idev) +		return -ENOMEM; +	spi_set_drvdata(spi, idev); +	st = iio_priv(idev); +	mutex_init(&st->lock); +	st->sdev = spi; + +	idev->dev.parent = &spi->dev; +	idev->info = &ad9850_info; +	idev->modes = INDIO_DIRECT_MODE; + +	ret = iio_device_register(idev); +	if (ret) +		return ret; +	spi->max_speed_hz = 2000000; +	spi->mode = SPI_MODE_3; +	spi->bits_per_word = 16; +	spi_setup(spi); + +	return 0; +} + +static int ad9850_remove(struct spi_device *spi) +{ +	iio_device_unregister(spi_get_drvdata(spi)); + +	return 0; +} + +static struct spi_driver ad9850_driver = { +	.driver = { +		.name = DRV_NAME, +		.owner = THIS_MODULE, +	}, +	.probe = ad9850_probe, +	.remove = ad9850_remove, +}; +module_spi_driver(ad9850_driver); + +MODULE_AUTHOR("Cliff Cai"); +MODULE_DESCRIPTION("Analog Devices ad9850 driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("spi:" DRV_NAME); diff --git a/drivers/staging/iio/frequency/ad9852.c b/drivers/staging/iio/frequency/ad9852.c new file mode 100644 index 00000000000..11e4367375d --- /dev/null +++ b/drivers/staging/iio/frequency/ad9852.c @@ -0,0 +1,253 @@ +/* + * Driver for ADI Direct Digital Synthesis ad9852 + * + * Copyright (c) 2010 Analog Devices Inc. + * + * 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/types.h> +#include <linux/mutex.h> +#include <linux/device.h> +#include <linux/spi/spi.h> +#include <linux/slab.h> +#include <linux/sysfs.h> +#include <linux/module.h> + +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> + +#define DRV_NAME "ad9852" + +#define addr_phaad1 0x0 +#define addr_phaad2 0x1 +#define addr_fretu1 0x2 +#define addr_fretu2 0x3 +#define addr_delfre 0x4 +#define addr_updclk 0x5 +#define addr_ramclk 0x6 +#define addr_contrl 0x7 +#define addr_optskm 0x8 +#define addr_optskr 0xa +#define addr_dacctl 0xb + +#define COMPPD		(1 << 4) +#define REFMULT2	(1 << 2) +#define BYPPLL		(1 << 5) +#define PLLRANG		(1 << 6) +#define IEUPCLK		(1) +#define OSKEN		(1 << 5) + +#define read_bit	(1 << 7) + +/* Register format: 1 byte addr + value */ +struct ad9852_config { +	u8 phajst0[3]; +	u8 phajst1[3]; +	u8 fretun1[6]; +	u8 fretun2[6]; +	u8 dltafre[6]; +	u8 updtclk[5]; +	u8 ramprat[4]; +	u8 control[5]; +	u8 outpskm[3]; +	u8 outpskr[2]; +	u8 daccntl[3]; +}; + +struct ad9852_state { +	struct mutex lock; +	struct spi_device *sdev; +}; + +static ssize_t ad9852_set_parameter(struct device *dev, +					struct device_attribute *attr, +					const char *buf, +					size_t len) +{ +	struct spi_transfer xfer; +	int ret; +	struct ad9852_config *config = (struct ad9852_config *)buf; +	struct iio_dev *idev = dev_to_iio_dev(dev); +	struct ad9852_state *st = iio_priv(idev); + +	xfer.len = 3; +	xfer.tx_buf = &config->phajst0[0]; +	mutex_lock(&st->lock); + +	ret = spi_sync_transfer(st->sdev, &xfer, 1); +	if (ret) +		goto error_ret; + +	xfer.len = 3; +	xfer.tx_buf = &config->phajst1[0]; + +	ret = spi_sync_transfer(st->sdev, &xfer, 1); +	if (ret) +		goto error_ret; + +	xfer.len = 6; +	xfer.tx_buf = &config->fretun1[0]; + +	ret = spi_sync_transfer(st->sdev, &xfer, 1); +	if (ret) +		goto error_ret; + +	xfer.len = 6; +	xfer.tx_buf = &config->fretun2[0]; + +	ret = spi_sync_transfer(st->sdev, &xfer, 1); +	if (ret) +		goto error_ret; + +	xfer.len = 6; +	xfer.tx_buf = &config->dltafre[0]; + +	ret = spi_sync_transfer(st->sdev, &xfer, 1); +	if (ret) +		goto error_ret; + +	xfer.len = 5; +	xfer.tx_buf = &config->updtclk[0]; + +	ret = spi_sync_transfer(st->sdev, &xfer, 1); +	if (ret) +		goto error_ret; + +	xfer.len = 4; +	xfer.tx_buf = &config->ramprat[0]; + +	ret = spi_sync_transfer(st->sdev, &xfer, 1); +	if (ret) +		goto error_ret; + +	xfer.len = 5; +	xfer.tx_buf = &config->control[0]; + +	ret = spi_sync_transfer(st->sdev, &xfer, 1); +	if (ret) +		goto error_ret; + +	xfer.len = 3; +	xfer.tx_buf = &config->outpskm[0]; + +	ret = spi_sync_transfer(st->sdev, &xfer, 1); +	if (ret) +		goto error_ret; + +	xfer.len = 2; +	xfer.tx_buf = &config->outpskr[0]; + +	ret = spi_sync_transfer(st->sdev, &xfer, 1); +	if (ret) +		goto error_ret; + +	xfer.len = 3; +	xfer.tx_buf = &config->daccntl[0]; + +	ret = spi_sync_transfer(st->sdev, &xfer, 1); +	if (ret) +		goto error_ret; +error_ret: +	mutex_unlock(&st->lock); + +	return ret ? ret : len; +} + +static IIO_DEVICE_ATTR(dds, S_IWUSR, NULL, ad9852_set_parameter, 0); + +static void ad9852_init(struct ad9852_state *st) +{ +	struct spi_transfer xfer; +	int ret; +	u8 config[5]; + +	config[0] = addr_contrl; +	config[1] = COMPPD; +	config[2] = REFMULT2 | BYPPLL | PLLRANG; +	config[3] = IEUPCLK; +	config[4] = OSKEN; + +	mutex_lock(&st->lock); + +	xfer.len = 5; +	xfer.tx_buf = &config; + +	ret = spi_sync_transfer(st->sdev, &xfer, 1); +	if (ret) +		goto error_ret; + +error_ret: +	mutex_unlock(&st->lock); + + + +} + +static struct attribute *ad9852_attributes[] = { +	&iio_dev_attr_dds.dev_attr.attr, +	NULL, +}; + +static const struct attribute_group ad9852_attribute_group = { +	.attrs = ad9852_attributes, +}; + +static const struct iio_info ad9852_info = { +	.attrs = &ad9852_attribute_group, +	.driver_module = THIS_MODULE, +}; + +static int ad9852_probe(struct spi_device *spi) +{ +	struct ad9852_state *st; +	struct iio_dev *idev; +	int ret = 0; + +	idev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); +	if (!idev) +		return -ENOMEM; +	st = iio_priv(idev); +	spi_set_drvdata(spi, idev); +	mutex_init(&st->lock); +	st->sdev = spi; + +	idev->dev.parent = &spi->dev; +	idev->info = &ad9852_info; +	idev->modes = INDIO_DIRECT_MODE; + +	ret = iio_device_register(idev); +	if (ret) +		return ret; +	spi->max_speed_hz = 2000000; +	spi->mode = SPI_MODE_3; +	spi->bits_per_word = 8; +	spi_setup(spi); +	ad9852_init(st); + +	return 0; +} + +static int ad9852_remove(struct spi_device *spi) +{ +	iio_device_unregister(spi_get_drvdata(spi)); + +	return 0; +} + +static struct spi_driver ad9852_driver = { +	.driver = { +		.name = DRV_NAME, +		.owner = THIS_MODULE, +	}, +	.probe = ad9852_probe, +	.remove = ad9852_remove, +}; +module_spi_driver(ad9852_driver); + +MODULE_AUTHOR("Cliff Cai"); +MODULE_DESCRIPTION("Analog Devices ad9852 driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("spi:" DRV_NAME); diff --git a/drivers/staging/iio/frequency/ad9910.c b/drivers/staging/iio/frequency/ad9910.c new file mode 100644 index 00000000000..755e0482681 --- /dev/null +++ b/drivers/staging/iio/frequency/ad9910.c @@ -0,0 +1,371 @@ +/* + * Driver for ADI Direct Digital Synthesis ad9910 + * + * Copyright (c) 2010 Analog Devices Inc. + * + * 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/types.h> +#include <linux/mutex.h> +#include <linux/device.h> +#include <linux/spi/spi.h> +#include <linux/slab.h> +#include <linux/sysfs.h> +#include <linux/module.h> + +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> + +#define DRV_NAME "ad9910" + +#define CFR1 0x0 +#define CFR2 0x1 +#define CFR3 0x2 + +#define AUXDAC 0x3 +#define IOUPD 0x4 +#define FTW 0x7 +#define POW 0x8 +#define ASF 0x9 +#define MULTC 0x0A +#define DIG_RAMPL 0x0B +#define DIG_RAMPS 0x0C +#define DIG_RAMPR 0x0D +#define SIN_TONEP0 0x0E +#define SIN_TONEP1 0x0F +#define SIN_TONEP2 0x10 +#define SIN_TONEP3 0x11 +#define SIN_TONEP4 0x12 +#define SIN_TONEP5 0x13 +#define SIN_TONEP6 0x14 +#define SIN_TONEP7 0x15 + +#define RAM_ENABLE	(1 << 7) + +#define MANUAL_OSK	(1 << 7) +#define INVSIC		(1 << 6) +#define DDS_SINEOP	(1) + +#define AUTO_OSK	(1) +#define OSKEN		(1 << 1) +#define LOAD_ARR	(1 << 2) +#define CLR_PHA		(1 << 3) +#define CLR_DIG		(1 << 4) +#define ACLR_PHA	(1 << 5) +#define ACLR_DIG	(1 << 6) +#define LOAD_LRR	(1 << 7) + +#define LSB_FST		(1) +#define SDIO_IPT	(1 << 1) +#define EXT_PWD		(1 << 3) +#define ADAC_PWD	(1 << 4) +#define REFCLK_PWD	(1 << 5) +#define DAC_PWD		(1 << 6) +#define DIG_PWD		(1 << 7) + +#define ENA_AMP		(1) +#define READ_FTW	(1) +#define DIGR_LOW	(1 << 1) +#define DIGR_HIGH	(1 << 2) +#define DIGR_ENA	(1 << 3) +#define SYNCCLK_ENA	(1 << 6) +#define ITER_IOUPD	(1 << 7) + +#define TX_ENA		(1 << 1) +#define PDCLK_INV	(1 << 2) +#define PDCLK_ENB	(1 << 3) + +#define PARA_ENA	(1 << 4) +#define SYNC_DIS	(1 << 5) +#define DATA_ASS	(1 << 6) +#define MATCH_ENA	(1 << 7) + +#define PLL_ENA		(1) +#define PFD_RST		(1 << 2) +#define REFCLK_RST	(1 << 6) +#define REFCLK_BYP	(1 << 7) + +/* Register format: 1 byte addr + value */ +struct ad9910_config { +	u8 auxdac[5]; +	u8 ioupd[5]; +	u8 ftw[5]; +	u8 pow[3]; +	u8 asf[5]; +	u8 multc[5]; +	u8 dig_rampl[9]; +	u8 dig_ramps[9]; +	u8 dig_rampr[5]; +	u8 sin_tonep0[9]; +	u8 sin_tonep1[9]; +	u8 sin_tonep2[9]; +	u8 sin_tonep3[9]; +	u8 sin_tonep4[9]; +	u8 sin_tonep5[9]; +	u8 sin_tonep6[9]; +	u8 sin_tonep7[9]; +}; + +struct ad9910_state { +	struct mutex lock; +	struct spi_device *sdev; +}; + +static ssize_t ad9910_set_parameter(struct device *dev, +					struct device_attribute *attr, +					const char *buf, +					size_t len) +{ +	struct spi_transfer xfer; +	int ret; +	struct ad9910_config *config = (struct ad9910_config *)buf; +	struct iio_dev *idev = dev_to_iio_dev(dev); +	struct ad9910_state *st = iio_priv(idev); + +	xfer.len = 5; +	xfer.tx_buf = &config->auxdac[0]; +	mutex_lock(&st->lock); + +	ret = spi_sync_transfer(st->sdev, &xfer, 1); +	if (ret) +		goto error_ret; + +	xfer.len = 5; +	xfer.tx_buf = &config->ioupd[0]; + +	ret = spi_sync_transfer(st->sdev, &xfer, 1); +	if (ret) +		goto error_ret; + +	xfer.len = 5; +	xfer.tx_buf = &config->ftw[0]; + +	ret = spi_sync_transfer(st->sdev, &xfer, 1); +	if (ret) +		goto error_ret; + +	xfer.len = 3; +	xfer.tx_buf = &config->pow[0]; + +	ret = spi_sync_transfer(st->sdev, &xfer, 1); +	if (ret) +		goto error_ret; + +	xfer.len = 5; +	xfer.tx_buf = &config->asf[0]; + +	ret = spi_sync_transfer(st->sdev, &xfer, 1); +	if (ret) +		goto error_ret; + +	xfer.len = 5; +	xfer.tx_buf = &config->multc[0]; + +	ret = spi_sync_transfer(st->sdev, &xfer, 1); +	if (ret) +		goto error_ret; + +	xfer.len = 9; +	xfer.tx_buf = &config->dig_rampl[0]; + +	ret = spi_sync_transfer(st->sdev, &xfer, 1); +	if (ret) +		goto error_ret; + +	xfer.len = 9; +	xfer.tx_buf = &config->dig_ramps[0]; + +	ret = spi_sync_transfer(st->sdev, &xfer, 1); +	if (ret) +		goto error_ret; + +	xfer.len = 5; +	xfer.tx_buf = &config->dig_rampr[0]; + +	ret = spi_sync_transfer(st->sdev, &xfer, 1); +	if (ret) +		goto error_ret; + +	xfer.len = 9; +	xfer.tx_buf = &config->sin_tonep0[0]; + +	ret = spi_sync_transfer(st->sdev, &xfer, 1); +	if (ret) +		goto error_ret; + +	xfer.len = 9; +	xfer.tx_buf = &config->sin_tonep1[0]; + +	ret = spi_sync_transfer(st->sdev, &xfer, 1); +	if (ret) +		goto error_ret; + +	xfer.len = 9; +	xfer.tx_buf = &config->sin_tonep2[0]; + +	ret = spi_sync_transfer(st->sdev, &xfer, 1); +	if (ret) +		goto error_ret; +	xfer.len = 9; +	xfer.tx_buf = &config->sin_tonep3[0]; + +	ret = spi_sync_transfer(st->sdev, &xfer, 1); +	if (ret) +		goto error_ret; + +	xfer.len = 9; +	xfer.tx_buf = &config->sin_tonep4[0]; + +	ret = spi_sync_transfer(st->sdev, &xfer, 1); +	if (ret) +		goto error_ret; + +	xfer.len = 9; +	xfer.tx_buf = &config->sin_tonep5[0]; + +	ret = spi_sync_transfer(st->sdev, &xfer, 1); +	if (ret) +		goto error_ret; + +	xfer.len = 9; +	xfer.tx_buf = &config->sin_tonep6[0]; + +	ret = spi_sync_transfer(st->sdev, &xfer, 1); +	if (ret) +		goto error_ret; + +	xfer.len = 9; +	xfer.tx_buf = &config->sin_tonep7[0]; + +	ret = spi_sync_transfer(st->sdev, &xfer, 1); +	if (ret) +		goto error_ret; +error_ret: +	mutex_unlock(&st->lock); + +	return ret ? ret : len; +} + +static IIO_DEVICE_ATTR(dds, S_IWUSR, NULL, ad9910_set_parameter, 0); + +static void ad9910_init(struct ad9910_state *st) +{ +	struct spi_transfer xfer; +	int ret; +	u8 cfr[5]; + +	cfr[0] = CFR1; +	cfr[1] = 0; +	cfr[2] = MANUAL_OSK | INVSIC | DDS_SINEOP; +	cfr[3] = AUTO_OSK | OSKEN | ACLR_PHA | ACLR_DIG | LOAD_LRR; +	cfr[4] = 0; + +	mutex_lock(&st->lock); + +	xfer.len = 5; +	xfer.tx_buf = 𝔠 + +	ret = spi_sync_transfer(st->sdev, &xfer, 1); +	if (ret) +		goto error_ret; + +	cfr[0] = CFR2; +	cfr[1] = ENA_AMP; +	cfr[2] = READ_FTW | DIGR_ENA | ITER_IOUPD; +	cfr[3] = TX_ENA | PDCLK_INV | PDCLK_ENB; +	cfr[4] = PARA_ENA; + +	xfer.len = 5; +	xfer.tx_buf = 𝔠 + +	ret = spi_sync_transfer(st->sdev, &xfer, 1); +	if (ret) +		goto error_ret; + +	cfr[0] = CFR3; +	cfr[1] = PLL_ENA; +	cfr[2] = 0; +	cfr[3] = REFCLK_RST | REFCLK_BYP; +	cfr[4] = 0; + +	xfer.len = 5; +	xfer.tx_buf = 𝔠 + +	ret = spi_sync_transfer(st->sdev, &xfer, 1); +	if (ret) +		goto error_ret; + +error_ret: +	mutex_unlock(&st->lock); + + + +} + +static struct attribute *ad9910_attributes[] = { +	&iio_dev_attr_dds.dev_attr.attr, +	NULL, +}; + +static const struct attribute_group ad9910_attribute_group = { +	.attrs = ad9910_attributes, +}; + +static const struct iio_info ad9910_info = { +	.attrs = &ad9910_attribute_group, +	.driver_module = THIS_MODULE, +}; + +static int ad9910_probe(struct spi_device *spi) +{ +	struct ad9910_state *st; +	struct iio_dev *idev; +	int ret = 0; + +	idev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); +	if (!idev) +		return -ENOMEM; +	spi_set_drvdata(spi, idev); +	st = iio_priv(idev); +	mutex_init(&st->lock); +	st->sdev = spi; + +	idev->dev.parent = &spi->dev; +	idev->info = &ad9910_info; +	idev->modes = INDIO_DIRECT_MODE; + +	ret = iio_device_register(idev); +	if (ret) +		return ret; +	spi->max_speed_hz = 2000000; +	spi->mode = SPI_MODE_3; +	spi->bits_per_word = 8; +	spi_setup(spi); +	ad9910_init(st); +	return 0; +} + +static int ad9910_remove(struct spi_device *spi) +{ +	iio_device_unregister(spi_get_drvdata(spi)); + +	return 0; +} + +static struct spi_driver ad9910_driver = { +	.driver = { +		.name = DRV_NAME, +		.owner = THIS_MODULE, +	}, +	.probe = ad9910_probe, +	.remove = ad9910_remove, +}; +module_spi_driver(ad9910_driver); + +MODULE_AUTHOR("Cliff Cai"); +MODULE_DESCRIPTION("Analog Devices ad9910 driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("spi:" DRV_NAME); diff --git a/drivers/staging/iio/frequency/ad9951.c b/drivers/staging/iio/frequency/ad9951.c new file mode 100644 index 00000000000..5e8990a0210 --- /dev/null +++ b/drivers/staging/iio/frequency/ad9951.c @@ -0,0 +1,209 @@ +/* + * Driver for ADI Direct Digital Synthesis ad9951 + * + * Copyright (c) 2010 Analog Devices Inc. + * + * 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/types.h> +#include <linux/mutex.h> +#include <linux/device.h> +#include <linux/spi/spi.h> +#include <linux/slab.h> +#include <linux/sysfs.h> +#include <linux/module.h> + +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> + +#define DRV_NAME "ad9951" + +#define CFR1 0x0 +#define CFR2 0x1 + +#define AUTO_OSK	(1) +#define OSKEN		(1 << 1) +#define LOAD_ARR	(1 << 2) + +#define AUTO_SYNC	(1 << 7) + +#define LSB_FST		(1) +#define SDIO_IPT	(1 << 1) +#define CLR_PHA		(1 << 2) +#define SINE_OPT	(1 << 4) +#define ACLR_PHA	(1 << 5) + +#define VCO_RANGE	(1 << 2) + +#define CRS_OPT		(1 << 1) +#define HMANU_SYNC	(1 << 2) +#define HSPD_SYNC	(1 << 3) + +/* Register format: 1 byte addr + value */ +struct ad9951_config { +	u8 asf[3]; +	u8 arr[2]; +	u8 ftw0[5]; +	u8 ftw1[3]; +}; + +struct ad9951_state { +	struct mutex lock; +	struct spi_device *sdev; +}; + +static ssize_t ad9951_set_parameter(struct device *dev, +					struct device_attribute *attr, +					const char *buf, +					size_t len) +{ +	struct spi_transfer xfer; +	int ret; +	struct ad9951_config *config = (struct ad9951_config *)buf; +	struct iio_dev *idev = dev_to_iio_dev(dev); +	struct ad9951_state *st = iio_priv(idev); + +	xfer.len = 3; +	xfer.tx_buf = &config->asf[0]; +	mutex_lock(&st->lock); + +	ret = spi_sync_transfer(st->sdev, &xfer, 1); +	if (ret) +		goto error_ret; + +	xfer.len = 2; +	xfer.tx_buf = &config->arr[0]; + +	ret = spi_sync_transfer(st->sdev, &xfer, 1); +	if (ret) +		goto error_ret; + +	xfer.len = 5; +	xfer.tx_buf = &config->ftw0[0]; + +	ret = spi_sync_transfer(st->sdev, &xfer, 1); +	if (ret) +		goto error_ret; + +	xfer.len = 3; +	xfer.tx_buf = &config->ftw1[0]; + +	ret = spi_sync_transfer(st->sdev, &xfer, 1); +	if (ret) +		goto error_ret; +error_ret: +	mutex_unlock(&st->lock); + +	return ret ? ret : len; +} + +static IIO_DEVICE_ATTR(dds, S_IWUSR, NULL, ad9951_set_parameter, 0); + +static void ad9951_init(struct ad9951_state *st) +{ +	struct spi_transfer xfer; +	int ret; +	u8 cfr[5]; + +	cfr[0] = CFR1; +	cfr[1] = 0; +	cfr[2] = LSB_FST | CLR_PHA | SINE_OPT | ACLR_PHA; +	cfr[3] = AUTO_OSK | OSKEN | LOAD_ARR; +	cfr[4] = 0; + +	mutex_lock(&st->lock); + +	xfer.len = 5; +	xfer.tx_buf = 𝔠 + +	ret = spi_sync_transfer(st->sdev, &xfer, 1); +	if (ret) +		goto error_ret; + +	cfr[0] = CFR2; +	cfr[1] = VCO_RANGE; +	cfr[2] = HSPD_SYNC; +	cfr[3] = 0; + +	xfer.len = 4; +	xfer.tx_buf = 𝔠 + +	ret = spi_sync_transfer(st->sdev, &xfer, 1); +	if (ret) +		goto error_ret; + +error_ret: +	mutex_unlock(&st->lock); + + + +} + +static struct attribute *ad9951_attributes[] = { +	&iio_dev_attr_dds.dev_attr.attr, +	NULL, +}; + +static const struct attribute_group ad9951_attribute_group = { +	.attrs = ad9951_attributes, +}; + +static const struct iio_info ad9951_info = { +	.attrs = &ad9951_attribute_group, +	.driver_module = THIS_MODULE, +}; + +static int ad9951_probe(struct spi_device *spi) +{ +	struct ad9951_state *st; +	struct iio_dev *idev; +	int ret = 0; + +	idev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); +	if (!idev) +		return -ENOMEM; +	spi_set_drvdata(spi, idev); +	st = iio_priv(idev); +	mutex_init(&st->lock); +	st->sdev = spi; + +	idev->dev.parent = &spi->dev; + +	idev->info = &ad9951_info; +	idev->modes = INDIO_DIRECT_MODE; + +	ret = iio_device_register(idev); +	if (ret) +		return ret; +	spi->max_speed_hz = 2000000; +	spi->mode = SPI_MODE_3; +	spi->bits_per_word = 8; +	spi_setup(spi); +	ad9951_init(st); +	return 0; +} + +static int ad9951_remove(struct spi_device *spi) +{ +	iio_device_unregister(spi_get_drvdata(spi)); + +	return 0; +} + +static struct spi_driver ad9951_driver = { +	.driver = { +		.name = DRV_NAME, +		.owner = THIS_MODULE, +	}, +	.probe = ad9951_probe, +	.remove = ad9951_remove, +}; +module_spi_driver(ad9951_driver); + +MODULE_AUTHOR("Cliff Cai"); +MODULE_DESCRIPTION("Analog Devices ad9951 driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("spi:" DRV_NAME); diff --git a/drivers/staging/iio/frequency/dds.h b/drivers/staging/iio/frequency/dds.h new file mode 100644 index 00000000000..c3342f6e052 --- /dev/null +++ b/drivers/staging/iio/frequency/dds.h @@ -0,0 +1,110 @@ +/* + * dds.h - sysfs attributes associated with DDS devices + * + * Copyright (c) 2010 Analog Devices Inc. + * + * Licensed under the GPL-2 or later. + */ + +/** + * /sys/bus/iio/devices/.../out_altvoltageX_frequencyY + */ + +#define IIO_DEV_ATTR_FREQ(_channel, _num, _mode, _show, _store, _addr)	\ +	IIO_DEVICE_ATTR(out_altvoltage##_channel##_frequency##_num,	\ +			_mode, _show, _store, _addr) + +/** + * /sys/bus/iio/devices/.../out_altvoltageX_frequencyY_scale + */ + +#define IIO_CONST_ATTR_FREQ_SCALE(_channel, _string)			\ +	IIO_CONST_ATTR(out_altvoltage##_channel##_frequency_scale, _string) + +/** + * /sys/bus/iio/devices/.../out_altvoltageX_frequencysymbol + */ + +#define IIO_DEV_ATTR_FREQSYMBOL(_channel, _mode, _show, _store, _addr)	\ +	IIO_DEVICE_ATTR(out_altvoltage##_channel##_frequencysymbol,	\ +			_mode, _show, _store, _addr); + +/** + * /sys/bus/iio/devices/.../out_altvoltageX_phaseY + */ + +#define IIO_DEV_ATTR_PHASE(_channel, _num, _mode, _show, _store, _addr)	\ +	IIO_DEVICE_ATTR(out_altvoltage##_channel##_phase##_num,		\ +			_mode, _show, _store, _addr) + +/** + * /sys/bus/iio/devices/.../out_altvoltageX_phaseY_scale + */ + +#define IIO_CONST_ATTR_PHASE_SCALE(_channel, _string)			\ +	IIO_CONST_ATTR(out_altvoltage##_channel##_phase_scale, _string) + +/** + * /sys/bus/iio/devices/.../out_altvoltageX_phasesymbol + */ + +#define IIO_DEV_ATTR_PHASESYMBOL(_channel, _mode, _show, _store, _addr)	\ +	IIO_DEVICE_ATTR(out_altvoltage##_channel##_phasesymbol,		\ +			_mode, _show, _store, _addr); + +/** + * /sys/bus/iio/devices/.../out_altvoltageX_pincontrol_en + */ + +#define IIO_DEV_ATTR_PINCONTROL_EN(_channel, _mode, _show, _store, _addr)\ +	IIO_DEVICE_ATTR(out_altvoltage##_channel##_pincontrol_en,	\ +			_mode, _show, _store, _addr); + +/** + * /sys/bus/iio/devices/.../out_altvoltageX_pincontrol_frequency_en + */ + +#define IIO_DEV_ATTR_PINCONTROL_FREQ_EN(_channel, _mode, _show, _store, _addr)\ +	IIO_DEVICE_ATTR(out_altvoltage##_channel##_pincontrol_frequency_en,\ +			_mode, _show, _store, _addr); + +/** + * /sys/bus/iio/devices/.../out_altvoltageX_pincontrol_phase_en + */ + +#define IIO_DEV_ATTR_PINCONTROL_PHASE_EN(_channel, _mode, _show, _store, _addr)\ +	IIO_DEVICE_ATTR(out_altvoltage##_channel##_pincontrol_phase_en,	\ +			_mode, _show, _store, _addr); + +/** + * /sys/bus/iio/devices/.../out_altvoltageX_out_enable + */ + +#define IIO_DEV_ATTR_OUT_ENABLE(_channel, _mode, _show, _store, _addr)	\ +	IIO_DEVICE_ATTR(out_altvoltage##_channel##_out_enable,		\ +			_mode, _show, _store, _addr); + +/** + * /sys/bus/iio/devices/.../out_altvoltageX_outY_enable + */ + +#define IIO_DEV_ATTR_OUTY_ENABLE(_channel, _output,			\ +			_mode, _show, _store, _addr)			\ +	IIO_DEVICE_ATTR(out_altvoltage##_channel##_out##_output##_enable,\ +			_mode, _show, _store, _addr); + +/** + * /sys/bus/iio/devices/.../out_altvoltageX_outY_wavetype + */ + +#define IIO_DEV_ATTR_OUT_WAVETYPE(_channel, _output, _store, _addr)	\ +	IIO_DEVICE_ATTR(out_altvoltage##_channel##_out##_output##_wavetype,\ +			S_IWUSR, NULL, _store, _addr); + +/** + * /sys/bus/iio/devices/.../out_altvoltageX_outY_wavetype_available + */ + +#define IIO_CONST_ATTR_OUT_WAVETYPES_AVAILABLE(_channel, _output, _modes)\ +	IIO_CONST_ATTR(							\ +	out_altvoltage##_channel##_out##_output##_wavetype_available, _modes); diff --git a/drivers/staging/iio/gyro/Kconfig b/drivers/staging/iio/gyro/Kconfig index c4043610c0d..88b199bb292 100644 --- a/drivers/staging/iio/gyro/Kconfig +++ b/drivers/staging/iio/gyro/Kconfig @@ -1,13 +1,13 @@  #  # IIO Digital Gyroscope Sensor drivers configuration  # -comment "Digital gyroscope sensors" +menu "Digital gyroscope sensors" -config ADIS16260 -	tristate "Analog Devices ADIS16260/5 Digital Gyroscope Sensor SPI driver" +config ADIS16060 +	tristate "Analog Devices ADIS16060 Yaw Rate Gyroscope with 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. +	  Say yes here to build support for Analog Devices adis16060 wide bandwidth +	  yaw rate gyroscope with SPI. + +endmenu diff --git a/drivers/staging/iio/gyro/Makefile b/drivers/staging/iio/gyro/Makefile index b5f0dc01122..cf22d6d55e2 100644 --- a/drivers/staging/iio/gyro/Makefile +++ b/drivers/staging/iio/gyro/Makefile @@ -2,6 +2,5 @@  # 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 +adis16060-y             := adis16060_core.o +obj-$(CONFIG_ADIS16060) += adis16060.o diff --git a/drivers/staging/iio/gyro/adis16060_core.c b/drivers/staging/iio/gyro/adis16060_core.c new file mode 100644 index 00000000000..d5d395c2e3e --- /dev/null +++ b/drivers/staging/iio/gyro/adis16060_core.c @@ -0,0 +1,245 @@ +/* + * ADIS16060 Wide Bandwidth Yaw Rate Gyroscope with SPI driver + * + * Copyright 2010 Analog Devices Inc. + * + * Licensed under the GPL-2 or later. + */ + +#include <linux/module.h> +#include <linux/delay.h> +#include <linux/mutex.h> +#include <linux/device.h> +#include <linux/kernel.h> +#include <linux/spi/spi.h> +#include <linux/slab.h> +#include <linux/sysfs.h> + +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> + +#define ADIS16060_GYRO		0x20 /* Measure Angular Rate (Gyro) */ +#define ADIS16060_TEMP_OUT	0x10 /* Measure Temperature */ +#define ADIS16060_AIN2		0x80 /* Measure AIN2 */ +#define ADIS16060_AIN1		0x40 /* Measure AIN1 */ + +/** + * struct adis16060_state - device instance specific data + * @us_w:		actual spi_device to write config + * @us_r:		actual spi_device to read back data + * @buf:		transmit or receive buffer + * @buf_lock:		mutex to protect tx and rx + **/ +struct adis16060_state { +	struct spi_device		*us_w; +	struct spi_device		*us_r; +	struct mutex			buf_lock; + +	u8 buf[3] ____cacheline_aligned; +}; + +static struct iio_dev *adis16060_iio_dev; + +static int adis16060_spi_write(struct iio_dev *indio_dev, u8 val) +{ +	int ret; +	struct adis16060_state *st = iio_priv(indio_dev); + +	mutex_lock(&st->buf_lock); +	st->buf[2] = val; /* The last 8 bits clocked in are latched */ +	ret = spi_write(st->us_w, st->buf, 3); +	mutex_unlock(&st->buf_lock); + +	return ret; +} + +static int adis16060_spi_read(struct iio_dev *indio_dev, u16 *val) +{ +	int ret; +	struct adis16060_state *st = iio_priv(indio_dev); + +	mutex_lock(&st->buf_lock); + +	ret = spi_read(st->us_r, st->buf, 3); + +	/* The internal successive approximation ADC begins the +	 * conversion process on the falling edge of MSEL1 and +	 * starts to place data MSB first on the DOUT line at +	 * the 6th falling edge of SCLK +	 */ +	if (ret == 0) +		*val = ((st->buf[0] & 0x3) << 12) | +			(st->buf[1] << 4) | +			((st->buf[2] >> 4) & 0xF); +	mutex_unlock(&st->buf_lock); + +	return ret; +} + +static int adis16060_read_raw(struct iio_dev *indio_dev, +			      struct iio_chan_spec const *chan, +			      int *val, int *val2, +			      long mask) +{ +	u16 tval = 0; +	int ret; + +	switch (mask) { +	case IIO_CHAN_INFO_RAW: +		/* Take the iio_dev status lock */ +		mutex_lock(&indio_dev->mlock); +		ret = adis16060_spi_write(indio_dev, chan->address); +		if (ret < 0) { +			mutex_unlock(&indio_dev->mlock); +			return ret; +		} +		ret = adis16060_spi_read(indio_dev, &tval); +		mutex_unlock(&indio_dev->mlock); +		*val = tval; +		return IIO_VAL_INT; +	case IIO_CHAN_INFO_OFFSET: +		*val = -7; +		*val2 = 461117; +		return IIO_VAL_INT_PLUS_MICRO; +	case IIO_CHAN_INFO_SCALE: +		*val = 0; +		*val2 = 34000; +		return IIO_VAL_INT_PLUS_MICRO; +	} + +	return -EINVAL; +} + +static const struct iio_info adis16060_info = { +	.read_raw = &adis16060_read_raw, +	.driver_module = THIS_MODULE, +}; + +static const struct iio_chan_spec adis16060_channels[] = { +	{ +		.type = IIO_ANGL_VEL, +		.modified = 1, +		.channel2 = IIO_MOD_Z, +		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), +		.address = ADIS16060_GYRO, +	}, { +		.type = IIO_VOLTAGE, +		.indexed = 1, +		.channel = 0, +		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), +		.address = ADIS16060_AIN1, +	}, { +		.type = IIO_VOLTAGE, +		.indexed = 1, +		.channel = 1, +		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), +		.address = ADIS16060_AIN2, +	}, { +		.type = IIO_TEMP, +		.indexed = 1, +		.channel = 0, +		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | +		BIT(IIO_CHAN_INFO_OFFSET) | BIT(IIO_CHAN_INFO_SCALE), +		.address = ADIS16060_TEMP_OUT, +	} +}; + +static int adis16060_r_probe(struct spi_device *spi) +{ +	int ret; +	struct adis16060_state *st; +	struct iio_dev *indio_dev; + +	/* setup the industrialio driver allocated elements */ +	indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); +	if (!indio_dev) +		return -ENOMEM; +	/* this is only used for removal purposes */ +	spi_set_drvdata(spi, indio_dev); +	st = iio_priv(indio_dev); +	st->us_r = spi; +	mutex_init(&st->buf_lock); + +	indio_dev->name = spi->dev.driver->name; +	indio_dev->dev.parent = &spi->dev; +	indio_dev->info = &adis16060_info; +	indio_dev->modes = INDIO_DIRECT_MODE; +	indio_dev->channels = adis16060_channels; +	indio_dev->num_channels = ARRAY_SIZE(adis16060_channels); + +	ret = devm_iio_device_register(&spi->dev, indio_dev); +	if (ret) +		return ret; + +	adis16060_iio_dev = indio_dev; +	return 0; +} + +static int adis16060_w_probe(struct spi_device *spi) +{ +	int ret; +	struct iio_dev *indio_dev = adis16060_iio_dev; +	struct adis16060_state *st; +	if (!indio_dev) { +		ret =  -ENODEV; +		goto error_ret; +	} +	st = iio_priv(indio_dev); +	spi_set_drvdata(spi, indio_dev); +	st->us_w = spi; +	return 0; + +error_ret: +	return ret; +} + +static int adis16060_w_remove(struct spi_device *spi) +{ +	return 0; +} + +static struct spi_driver adis16060_r_driver = { +	.driver = { +		.name = "adis16060_r", +		.owner = THIS_MODULE, +	}, +	.probe = adis16060_r_probe, +}; + +static struct spi_driver adis16060_w_driver = { +	.driver = { +		.name = "adis16060_w", +		.owner = THIS_MODULE, +	}, +	.probe = adis16060_w_probe, +	.remove = adis16060_w_remove, +}; + +static __init int adis16060_init(void) +{ +	int ret; + +	ret = spi_register_driver(&adis16060_r_driver); +	if (ret < 0) +		return ret; + +	ret = spi_register_driver(&adis16060_w_driver); +	if (ret < 0) { +		spi_unregister_driver(&adis16060_r_driver); +		return ret; +	} + +	return 0; +} +module_init(adis16060_init); + +static __exit void adis16060_exit(void) +{ +	spi_unregister_driver(&adis16060_w_driver); +	spi_unregister_driver(&adis16060_r_driver); +} +module_exit(adis16060_exit); + +MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>"); +MODULE_DESCRIPTION("Analog Devices ADIS16060 Yaw Rate Gyroscope Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/iio/gyro/adis16260.h b/drivers/staging/iio/gyro/adis16260.h deleted file mode 100644 index 812440af57d..00000000000 --- a/drivers/staging/iio/gyro/adis16260.h +++ /dev/null @@ -1,160 +0,0 @@ -#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 - * @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; -	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! - */ - -#define ADIS16260_SCAN_SUPPLY	0 -#define ADIS16260_SCAN_GYRO	1 -#define ADIS16260_SCAN_AUX_ADC	2 -#define ADIS16260_SCAN_TEMP	3 -#define ADIS16260_SCAN_ANGL	4 - -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); - -#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) -{ -} - -#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 deleted file mode 100644 index 7d7716e5857..00000000000 --- a/drivers/staging/iio/gyro/adis16260_core.c +++ /dev/null @@ -1,661 +0,0 @@ -/* - * 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/slab.h> -#include <linux/sysfs.h> -#include <linux/list.h> - -#include "../iio.h" -#include "../sysfs.h" -#include "../ring_generic.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(0, supply, -				adis16260_read_12bit_unsigned, -				ADIS16260_SUPPLY_OUT); -static IIO_CONST_ATTR_IN_NAMED_SCALE(0, supply, "0.0018315"); - -static IIO_DEV_ATTR_GYRO(adis16260_read_14bit_signed, -		ADIS16260_GYRO_OUT); -static IIO_CONST_ATTR_GYRO_SCALE("0.00127862821"); -static IIO_DEV_ATTR_GYRO_CALIBSCALE(S_IWUSR | S_IRUGO, -		adis16260_read_14bit_signed, -		adis16260_write_16bit, -		ADIS16260_GYRO_SCALE); -static IIO_DEV_ATTR_GYRO_CALIBBIAS(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(1, adis16260_read_12bit_unsigned, -		ADIS16260_AUX_ADC); -static IIO_CONST_ATTR(in1_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_SAMP_FREQ_AVAIL("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_in0_supply_raw.dev_attr.attr, -	&iio_const_attr_in0_supply_scale.dev_attr.attr, -	&iio_dev_attr_gyro_raw.dev_attr.attr, -	&iio_const_attr_gyro_scale.dev_attr.attr, -	&iio_dev_attr_gyro_calibscale.dev_attr.attr, -	&iio_dev_attr_gyro_calibbias.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_in1_raw.dev_attr.attr, -	&iio_const_attr_in1_scale.dev_attr.attr, -	&iio_dev_attr_sampling_frequency.dev_attr.attr, -	&iio_const_attr_sampling_frequency_available.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 = iio_ring_buffer_register(st->indio_dev->ring, 0); -	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: -	iio_ring_buffer_unregister(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); - -	iio_ring_buffer_unregister(st->indio_dev->ring); -	iio_device_unregister(indio_dev); -	adis16260_unconfigure_ring(indio_dev); -	kfree(st->tx); -	kfree(st->rx); -	kfree(st); - -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 deleted file mode 100644 index 23428894b1e..00000000000 --- a/drivers/staging/iio/gyro/adis16260_ring.c +++ /dev/null @@ -1,204 +0,0 @@ -#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/slab.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" - -static IIO_SCAN_EL_C(in_supply, ADIS16260_SCAN_SUPPLY, -		ADIS16260_SUPPLY_OUT, NULL); -static IIO_CONST_ATTR_SCAN_EL_TYPE(in_supply, u, 12, 16); -static IIO_SCAN_EL_C(gyro, ADIS16260_SCAN_GYRO, ADIS16260_GYRO_OUT, NULL); -static IIO_CONST_ATTR_SCAN_EL_TYPE(gyro, s, 14, 16); -static IIO_SCAN_EL_C(in0, ADIS16260_SCAN_AUX_ADC, ADIS16260_AUX_ADC, NULL); -static IIO_CONST_ATTR_SCAN_EL_TYPE(in0, u, 12, 16); -static IIO_SCAN_EL_C(temp, ADIS16260_SCAN_TEMP, ADIS16260_TEMP_OUT, NULL); -static IIO_CONST_ATTR_SCAN_EL_TYPE(temp, u, 12, 16); -static IIO_SCAN_EL_C(angl, ADIS16260_SCAN_ANGL, ADIS16260_ANGL_OUT, NULL); -static IIO_CONST_ATTR_SCAN_EL_TYPE(angl, u, 14, 16); -static IIO_SCAN_EL_TIMESTAMP(5); -static IIO_CONST_ATTR_SCAN_EL_TYPE(timestamp, s, 64, 64); - -static struct attribute *adis16260_scan_el_attrs[] = { -	&iio_scan_el_in_supply.dev_attr.attr, -	&iio_const_attr_in_supply_index.dev_attr.attr, -	&iio_const_attr_in_supply_type.dev_attr.attr, -	&iio_scan_el_gyro.dev_attr.attr, -	&iio_const_attr_gyro_index.dev_attr.attr, -	&iio_const_attr_gyro_type.dev_attr.attr, -	&iio_scan_el_in0.dev_attr.attr, -	&iio_const_attr_in0_index.dev_attr.attr, -	&iio_const_attr_in0_type.dev_attr.attr, -	&iio_scan_el_temp.dev_attr.attr, -	&iio_const_attr_temp_index.dev_attr.attr, -	&iio_const_attr_temp_type.dev_attr.attr, -	&iio_scan_el_angl.dev_attr.attr, -	&iio_const_attr_angl_index.dev_attr.attr, -	&iio_const_attr_angl_type.dev_attr.attr, -	&iio_scan_el_timestamp.dev_attr.attr, -	&iio_const_attr_timestamp_index.dev_attr.attr, -	&iio_const_attr_timestamp_type.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, s64 time) -{ -	struct adis16260_state *st = iio_dev_get_devdata(indio_dev); -	st->last_timestamp = time; -	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); -	struct iio_ring_buffer *ring = st->indio_dev->ring; - -	int i = 0; -	s16 *data; -	size_t datasize = ring->access.get_bytes_per_datum(ring); - -	data = kmalloc(datasize , GFP_KERNEL); -	if (data == NULL) { -		dev_err(&st->us->dev, "memory alloc failed in ring bh"); -		return; -	} - -	if (ring->scan_count) -		if (adis16260_read_ring_data(&st->indio_dev->dev, st->rx) >= 0) -			for (; i < ring->scan_count; i++) -				data[i] = be16_to_cpup( -					(__be16 *)&(st->rx[i*2])); - -	/* Guaranteed to be aligned with 8 byte boundary */ -	if (ring->scan_timestamp) -		*((s64 *)(data + ((i + 3)/4)*4)) = st->last_timestamp; - -	ring->access.store_to(ring, -			(u8 *)data, -			st->last_timestamp); - -	iio_trigger_notify_done(st->indio_dev->trig); -	kfree(data); - -	return; -} - -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); - -	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->bpe = 2; -	ring->scan_el_attrs = &adis16260_scan_el_group; -	ring->scan_timestamp = true; -	ring->preenable = &iio_sw_ring_preenable; -	ring->postenable = &iio_triggered_ring_postenable; -	ring->predisable = &iio_triggered_ring_predisable; -	ring->owner = THIS_MODULE; - -	/* Set default scan mode */ -	iio_scan_mask_set(ring, iio_scan_el_in_supply.number); -	iio_scan_mask_set(ring, iio_scan_el_gyro.number); -	iio_scan_mask_set(ring, iio_scan_el_in0.number); -	iio_scan_mask_set(ring, iio_scan_el_temp.number); -	iio_scan_mask_set(ring, iio_scan_el_angl.number); - -	ret = iio_alloc_pollfunc(indio_dev, NULL, &adis16260_poll_func_th); -	if (ret) -		goto error_iio_sw_rb_free; - -	indio_dev->modes |= INDIO_RING_TRIGGERED; -	return 0; - -error_iio_sw_rb_free: -	iio_sw_rb_free(indio_dev->ring); -	return ret; -} diff --git a/drivers/staging/iio/gyro/adis16260_trigger.c b/drivers/staging/iio/gyro/adis16260_trigger.c deleted file mode 100644 index 4a744c11ca6..00000000000 --- a/drivers/staging/iio/gyro/adis16260_trigger.c +++ /dev/null @@ -1,122 +0,0 @@ -#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; - -	iio_trigger_poll(trig, timestamp); - -	return IRQ_HANDLED; -} - -IIO_EVENT_SH(data_rdy_trig, &adis16260_data_rdy_trig_poll); - -static IIO_TRIGGER_NAME_ATTR; - -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 = kasprintf(GFP_KERNEL, -				   "adis16260-dev%d", -				   indio_dev->id); -	if (!st->trig->name) { -		ret = -ENOMEM; -		goto error_free_trig; -	} -	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 deleted file mode 100644 index 98b837b775a..00000000000 --- a/drivers/staging/iio/gyro/gyro.h +++ /dev/null @@ -1,73 +0,0 @@ - -#include "../sysfs.h" - -/* Gyroscope types of attribute */ - -#define IIO_CONST_ATTR_GYRO_OFFSET(_string)	\ -	IIO_CONST_ATTR(gyro_offset, _string) - -#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_CONST_ATTR_GYRO_SCALE(_string)		\ -	IIO_CONST_ATTR(gyro_scale, _string) - -#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_X_SCALE(_mode, _show, _store, _addr)		\ -	IIO_DEVICE_ATTR(gyro_x_scale, _mode, _show, _store, _addr) - -#define IIO_DEV_ATTR_GYRO_Y_SCALE(_mode, _show, _store, _addr)		\ -	IIO_DEVICE_ATTR(gyro_y_scale, _mode, _show, _store, _addr) - -#define IIO_DEV_ATTR_GYRO_Z_SCALE(_mode, _show, _store, _addr)		\ -	IIO_DEVICE_ATTR(gyro_z_scale, _mode, _show, _store, _addr) - -#define IIO_DEV_ATTR_GYRO_CALIBBIAS(_mode, _show, _store, _addr)		\ -	IIO_DEVICE_ATTR(gyro_calibbias, S_IRUGO, _show, _store, _addr) - -#define IIO_DEV_ATTR_GYRO_X_CALIBBIAS(_mode, _show, _store, _addr)		\ -	IIO_DEVICE_ATTR(gyro_x_calibbias, _mode, _show, _store, _addr) - -#define IIO_DEV_ATTR_GYRO_Y_CALIBBIAS(_mode, _show, _store, _addr)		\ -	IIO_DEVICE_ATTR(gyro_y_calibbias, _mode, _show, _store, _addr) - -#define IIO_DEV_ATTR_GYRO_Z_CALIBBIAS(_mode, _show, _store, _addr)		\ -	IIO_DEVICE_ATTR(gyro_z_calibbias, _mode, _show, _store, _addr) - -#define IIO_DEV_ATTR_GYRO_CALIBSCALE(_mode, _show, _store, _addr)		\ -	IIO_DEVICE_ATTR(gyro_calibscale, S_IRUGO, _show, _store, _addr) - -#define IIO_DEV_ATTR_GYRO_X_CALIBSCALE(_mode, _show, _store, _addr)		\ -	IIO_DEVICE_ATTR(gyro_x_calibscale, _mode, _show, _store, _addr) - -#define IIO_DEV_ATTR_GYRO_Y_CALIBSCALE(_mode, _show, _store, _addr)		\ -	IIO_DEVICE_ATTR(gyro_y_calibscale, _mode, _show, _store, _addr) - -#define IIO_DEV_ATTR_GYRO_Z_CALIBSCALE(_mode, _show, _store, _addr)		\ -	IIO_DEVICE_ATTR(gyro_z_calibscale, _mode, _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 deleted file mode 100644 index 248bdd2846f..00000000000 --- a/drivers/staging/iio/iio.h +++ /dev/null @@ -1,309 +0,0 @@ -/* The industrial I/O core - * - * Copyright (c) 2008 Jonathan Cameron - * - * 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 _INDUSTRIAL_IO_H_ -#define _INDUSTRIAL_IO_H_ - -#include <linux/device.h> -#include <linux/cdev.h> -#include "sysfs.h" -#include "chrdev.h" - -/* IIO TODO LIST */ -/* - * Provide means of adjusting timer accuracy. - * Currently assumes nano seconds. - */ - -/* Event interface flags */ -#define IIO_BUSY_BIT_POS 1 - -struct iio_dev; - -/** - * iio_get_time_ns() - utility function to get a time stamp for events etc - **/ -static inline s64 iio_get_time_ns(void) -{ -	struct timespec ts; -	/* -	 * calls getnstimeofday. -	 * If hrtimers then up to ns accurate, if not microsecond. -	 */ -	ktime_get_real_ts(&ts); - -	return timespec_to_ns(&ts); -} - -/** - * iio_add_event_to_list() - Wraps adding to event lists - * @el:		the list element of the event to be handled. - * @head:	the list associated with the event handler being used. - * - * Does reference counting to allow shared handlers. - **/ -void iio_add_event_to_list(struct iio_event_handler_list *el, -			   struct list_head *head); - -/** - * iio_remove_event_from_list() - Wraps removing from event list - * @el:		element to be removed - * @head:	associate list head for the interrupt handler. - * - * Does reference counting to allow shared handlers. - **/ -void iio_remove_event_from_list(struct iio_event_handler_list *el, -				struct list_head *head); - -/* Device operating modes */ -#define INDIO_DIRECT_MODE		0x01 -#define INDIO_RING_TRIGGERED		0x02 -#define INDIO_RING_HARDWARE_BUFFER	0x08 - -#define INDIO_ALL_RING_MODES (INDIO_RING_TRIGGERED | INDIO_RING_HARDWARE_BUFFER) - -/* Vast majority of this is set by the industrialio subsystem on a - * call to iio_device_register. */ - -/** - * struct iio_dev - industrial I/O device - * @id:			[INTERN] used to identify device internally - * @dev_data:		[DRIVER] device specific data - * @modes:		[DRIVER] operating modes supported by device - * @currentmode:	[DRIVER] current operating mode - * @dev:		[DRIVER] device structure, should be assigned a parent - *			and owner - * @attrs:		[DRIVER] general purpose device attributes - * @driver_module:	[DRIVER] module structure used to ensure correct - *			ownership of chrdevs etc - * @num_interrupt_lines:[DRIVER] number of physical interrupt lines from device - * @interrupts:		[INTERN] interrupt line specific event lists etc - * @event_attrs:	[DRIVER] event control attributes - * @event_conf_attrs:	[DRIVER] event configuration attributes - * @event_interfaces:	[INTERN] event chrdevs associated with interrupt lines - * @ring:		[DRIVER] any ring buffer present - * @mlock:		[INTERN] lock used to prevent simultaneous device state - *			changes - * @available_scan_masks: [DRIVER] optional array of allowed bitmasks - * @trig:		[INTERN] current device trigger (ring buffer modes) - * @pollfunc:		[DRIVER] function run on trigger being recieved - **/ -struct iio_dev { -	int				id; -	void				*dev_data; -	int				modes; -	int				currentmode; -	struct device			dev; -	const struct attribute_group	*attrs; -	struct module			*driver_module; - -	int				num_interrupt_lines; -	struct iio_interrupt		**interrupts; -	struct attribute_group		*event_attrs; -	struct attribute_group		*event_conf_attrs; - -	struct iio_event_interface	*event_interfaces; - -	struct iio_ring_buffer		*ring; -	struct mutex			mlock; - -	u32				*available_scan_masks; -	struct iio_trigger		*trig; -	struct iio_poll_func		*pollfunc; -}; - -/** - * iio_device_register() - register a device with the IIO subsystem - * @dev_info:		Device structure filled by the device driver - **/ -int iio_device_register(struct iio_dev *dev_info); - -/** - * iio_device_unregister() - unregister a device from the IIO subsystem - * @dev_info:		Device structure representing the device. - **/ -void iio_device_unregister(struct iio_dev *dev_info); - -/** - * struct iio_interrupt - wrapper used to allow easy handling of multiple - *			physical interrupt lines - * @dev_info:		the iio device for which the is an interrupt line - * @line_number:	associated line number - * @id:			ida allocated unique id number - * @irq:		associate interrupt number - * @ev_list:		event handler list for associated events - * @ev_list_lock:	ensure only one access to list at a time - **/ -struct iio_interrupt { -	struct iio_dev			*dev_info; -	int				line_number; -	int				id; -	int				irq; -	struct list_head		ev_list; -	spinlock_t			ev_list_lock; -}; - -#define to_iio_interrupt(i) container_of(i, struct iio_interrupt, ev_list) - -/** - * iio_register_interrupt_line() - Tell IIO about interrupt lines - * - * @irq:		Typically provided via platform data - * @dev_info:		IIO device info structure for device - * @line_number:	Which interrupt line of the device is this? - * @type:		Interrupt type (e.g. edge triggered etc) - * @name:		Identifying name. - **/ -int iio_register_interrupt_line(unsigned int			irq, -				struct iio_dev			*dev_info, -				int				line_number, -				unsigned long			type, -				const char			*name); - -void iio_unregister_interrupt_line(struct iio_dev *dev_info, -				   int line_number); - - - -/** - * iio_push_event() - try to add event to the list for userspace reading - * @dev_info:		IIO device structure - * @ev_line:		Which event line (hardware interrupt) - * @ev_code:		What event - * @timestamp:		When the event occurred - **/ -int iio_push_event(struct iio_dev *dev_info, -		  int ev_line, -		  int ev_code, -		  s64 timestamp); - -/** - * __iio_push_event() - tries to add an event to the list associated with a chrdev - * @ev_int:		the event interface to which we are pushing the event - * @ev_code:		the outgoing event code - * @timestamp:		timestamp of the event - * @shared_pointer_p:	the shared event pointer - **/ -int __iio_push_event(struct iio_event_interface *ev_int, -		    int ev_code, -		    s64 timestamp, -		    struct iio_shared_ev_pointer* -		    shared_pointer_p); -/** - * __iio_change_event() - change an event code in case of event escalation - * @ev:			the event to be changed - * @ev_code:		new event code - * @timestamp:		new timestamp - **/ -void __iio_change_event(struct iio_detected_event_list *ev, -			int ev_code, -			s64 timestamp); - -/** - * iio_setup_ev_int() - configure an event interface (chrdev) - * @name:		name used for resulting sysfs directory etc. - * @ev_int:		interface we are configuring - * @owner:		module that is responsible for registering this ev_int - * @dev:		device whose ev_int this is - **/ -int iio_setup_ev_int(struct iio_event_interface *ev_int, -		     const char *name, -		     struct module *owner, -		     struct device *dev); - -void iio_free_ev_int(struct iio_event_interface *ev_int); - -/** - * iio_allocate_chrdev() - Allocate a chrdev - * @handler:	struct that contains relevant file handling for chrdev - * @dev_info:	iio_dev for which chrdev is being created - **/ -int iio_allocate_chrdev(struct iio_handler *handler, struct iio_dev *dev_info); -void iio_deallocate_chrdev(struct iio_handler *handler); - -/* Used to distinguish between bipolar and unipolar scan elemenents. - * Whilst this may seem obvious, we may well want to change the representation - * in the future!*/ -#define IIO_SIGNED(a) -(a) -#define IIO_UNSIGNED(a) (a) - -extern dev_t iio_devt; -extern struct bus_type iio_bus_type; - -/** - * iio_put_device() - reference counted deallocation of struct device - * @dev: the iio_device containing the device - **/ -static inline void iio_put_device(struct iio_dev *dev) -{ -	if (dev) -		put_device(&dev->dev); -}; - -/** - * to_iio_dev() - get iio_dev for which we have the struct device - * @d: the struct device - **/ -static inline struct iio_dev *to_iio_dev(struct device *d) -{ -	return container_of(d, struct iio_dev, dev); -}; - -/** - * iio_dev_get_devdata() - helper function gets device specific data - * @d: the iio_dev associated with the device - **/ -static inline void *iio_dev_get_devdata(struct iio_dev *d) -{ -	return d->dev_data; -} - -/** - * iio_allocate_device() - allocate an iio_dev from a driver - **/ -struct iio_dev *iio_allocate_device(void); - -/** - * iio_free_device() - free an iio_dev from a driver - * @dev: the iio_dev associated with the device - **/ -void iio_free_device(struct iio_dev *dev); - -/** - * iio_put() - internal module reference count reduce - **/ -void iio_put(void); - -/** - * iio_get() - internal module reference count increase - **/ -void iio_get(void); - -/** - * iio_device_get_chrdev_minor() - get an unused minor number - **/ -int iio_device_get_chrdev_minor(void); -void iio_device_free_chrdev_minor(int val); - -/** - * iio_ring_enabled() - helper function to test if any form of ring is enabled - * @dev_info:		IIO device info structure for device - **/ -static inline bool iio_ring_enabled(struct iio_dev *dev_info) -{ -	return dev_info->currentmode -		& (INDIO_RING_TRIGGERED -		   | INDIO_RING_HARDWARE_BUFFER); -}; - -struct ida; - -int iio_get_new_ida_val(struct ida *this_ida); -void iio_free_ida_val(struct ida *this_ida, int id); -#endif /* _INDUSTRIAL_IO_H_ */ diff --git a/drivers/staging/iio/iio_dummy_evgen.c b/drivers/staging/iio/iio_dummy_evgen.c new file mode 100644 index 00000000000..132d278c501 --- /dev/null +++ b/drivers/staging/iio/iio_dummy_evgen.c @@ -0,0 +1,221 @@ +/** + * Copyright (c) 2011 Jonathan Cameron + * + * 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. + * + * Companion module to the iio simple dummy example driver. + * The purpose of this is to generate 'fake' event interrupts thus + * allowing that driver's code to be as close as possible to that of + * a normal driver talking to hardware.  The approach used here + * is not intended to be general and just happens to work for this + * particular use case. + */ + +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/mutex.h> +#include <linux/module.h> +#include <linux/sysfs.h> + +#include "iio_dummy_evgen.h" +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> + +/* Fiddly bit of faking and irq without hardware */ +#define IIO_EVENTGEN_NO 10 +/** + * struct iio_dummy_evgen - evgen state + * @chip: irq chip we are faking + * @base: base of irq range + * @enabled: mask of which irqs are enabled + * @inuse: mask of which irqs are connected + * @lock: protect the evgen state + */ +struct iio_dummy_eventgen { +	struct irq_chip chip; +	int base; +	bool enabled[IIO_EVENTGEN_NO]; +	bool inuse[IIO_EVENTGEN_NO]; +	struct mutex lock; +}; + +/* We can only ever have one instance of this 'device' */ +static struct iio_dummy_eventgen *iio_evgen; +static const char *iio_evgen_name = "iio_dummy_evgen"; + +static void iio_dummy_event_irqmask(struct irq_data *d) +{ +	struct irq_chip *chip = irq_data_get_irq_chip(d); +	struct iio_dummy_eventgen *evgen = +		container_of(chip, struct iio_dummy_eventgen, chip); + +	evgen->enabled[d->irq - evgen->base] = false; +} + +static void iio_dummy_event_irqunmask(struct irq_data *d) +{ +	struct irq_chip *chip = irq_data_get_irq_chip(d); +	struct iio_dummy_eventgen *evgen = +		container_of(chip, struct iio_dummy_eventgen, chip); + +	evgen->enabled[d->irq - evgen->base] = true; +} + +static int iio_dummy_evgen_create(void) +{ +	int ret, i; + +	iio_evgen = kzalloc(sizeof(*iio_evgen), GFP_KERNEL); +	if (iio_evgen == NULL) +		return -ENOMEM; + +	iio_evgen->base = irq_alloc_descs(-1, 0, IIO_EVENTGEN_NO, 0); +	if (iio_evgen->base < 0) { +		ret = iio_evgen->base; +		kfree(iio_evgen); +		return ret; +	} +	iio_evgen->chip.name = iio_evgen_name; +	iio_evgen->chip.irq_mask = &iio_dummy_event_irqmask; +	iio_evgen->chip.irq_unmask = &iio_dummy_event_irqunmask; +	for (i = 0; i < IIO_EVENTGEN_NO; i++) { +		irq_set_chip(iio_evgen->base + i, &iio_evgen->chip); +		irq_set_handler(iio_evgen->base + i, &handle_simple_irq); +		irq_modify_status(iio_evgen->base + i, +				  IRQ_NOREQUEST | IRQ_NOAUTOEN, +				  IRQ_NOPROBE); +	} +	mutex_init(&iio_evgen->lock); +	return 0; +} + +/** + * iio_dummy_evgen_get_irq() - get an evgen provided irq for a device + * + * This function will give a free allocated irq to a client device. + * That irq can then be caused to 'fire' by using the associated sysfs file. + */ +int iio_dummy_evgen_get_irq(void) +{ +	int i, ret = 0; + +	if (iio_evgen == NULL) +		return -ENODEV; + +	mutex_lock(&iio_evgen->lock); +	for (i = 0; i < IIO_EVENTGEN_NO; i++) +		if (!iio_evgen->inuse[i]) { +			ret = iio_evgen->base + i; +			iio_evgen->inuse[i] = true; +			break; +		} +	mutex_unlock(&iio_evgen->lock); +	if (i == IIO_EVENTGEN_NO) +		return -ENOMEM; +	return ret; +} +EXPORT_SYMBOL_GPL(iio_dummy_evgen_get_irq); + +/** + * iio_dummy_evgen_release_irq() - give the irq back. + * @irq: irq being returned to the pool + * + * Used by client driver instances to give the irqs back when they disconnect + */ +int iio_dummy_evgen_release_irq(int irq) +{ +	mutex_lock(&iio_evgen->lock); +	iio_evgen->inuse[irq - iio_evgen->base] = false; +	mutex_unlock(&iio_evgen->lock); + +	return 0; +} +EXPORT_SYMBOL_GPL(iio_dummy_evgen_release_irq); + +static void iio_dummy_evgen_free(void) +{ +	irq_free_descs(iio_evgen->base, IIO_EVENTGEN_NO); +	kfree(iio_evgen); +} + +static void iio_evgen_release(struct device *dev) +{ +	iio_dummy_evgen_free(); +} + +static ssize_t iio_evgen_poke(struct device *dev, +			      struct device_attribute *attr, +			      const char *buf, +			      size_t len) +{ +	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); + +	if (iio_evgen->enabled[this_attr->address]) +		handle_nested_irq(iio_evgen->base + this_attr->address); + +	return len; +} + +static IIO_DEVICE_ATTR(poke_ev0, S_IWUSR, NULL, &iio_evgen_poke, 0); +static IIO_DEVICE_ATTR(poke_ev1, S_IWUSR, NULL, &iio_evgen_poke, 1); +static IIO_DEVICE_ATTR(poke_ev2, S_IWUSR, NULL, &iio_evgen_poke, 2); +static IIO_DEVICE_ATTR(poke_ev3, S_IWUSR, NULL, &iio_evgen_poke, 3); +static IIO_DEVICE_ATTR(poke_ev4, S_IWUSR, NULL, &iio_evgen_poke, 4); +static IIO_DEVICE_ATTR(poke_ev5, S_IWUSR, NULL, &iio_evgen_poke, 5); +static IIO_DEVICE_ATTR(poke_ev6, S_IWUSR, NULL, &iio_evgen_poke, 6); +static IIO_DEVICE_ATTR(poke_ev7, S_IWUSR, NULL, &iio_evgen_poke, 7); +static IIO_DEVICE_ATTR(poke_ev8, S_IWUSR, NULL, &iio_evgen_poke, 8); +static IIO_DEVICE_ATTR(poke_ev9, S_IWUSR, NULL, &iio_evgen_poke, 9); + +static struct attribute *iio_evgen_attrs[] = { +	&iio_dev_attr_poke_ev0.dev_attr.attr, +	&iio_dev_attr_poke_ev1.dev_attr.attr, +	&iio_dev_attr_poke_ev2.dev_attr.attr, +	&iio_dev_attr_poke_ev3.dev_attr.attr, +	&iio_dev_attr_poke_ev4.dev_attr.attr, +	&iio_dev_attr_poke_ev5.dev_attr.attr, +	&iio_dev_attr_poke_ev6.dev_attr.attr, +	&iio_dev_attr_poke_ev7.dev_attr.attr, +	&iio_dev_attr_poke_ev8.dev_attr.attr, +	&iio_dev_attr_poke_ev9.dev_attr.attr, +	NULL, +}; + +static const struct attribute_group iio_evgen_group = { +	.attrs = iio_evgen_attrs, +}; + +static const struct attribute_group *iio_evgen_groups[] = { +	&iio_evgen_group, +	NULL +}; + +static struct device iio_evgen_dev = { +	.bus = &iio_bus_type, +	.groups = iio_evgen_groups, +	.release = &iio_evgen_release, +}; +static __init int iio_dummy_evgen_init(void) +{ +	int ret = iio_dummy_evgen_create(); +	if (ret < 0) +		return ret; +	device_initialize(&iio_evgen_dev); +	dev_set_name(&iio_evgen_dev, "iio_evgen"); +	return device_add(&iio_evgen_dev); +} +module_init(iio_dummy_evgen_init); + +static __exit void iio_dummy_evgen_exit(void) +{ +	device_unregister(&iio_evgen_dev); +} +module_exit(iio_dummy_evgen_exit); + +MODULE_AUTHOR("Jonathan Cameron <jic23@kernel.org>"); +MODULE_DESCRIPTION("IIO dummy driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/iio/iio_dummy_evgen.h b/drivers/staging/iio/iio_dummy_evgen.h new file mode 100644 index 00000000000..d8845e2288b --- /dev/null +++ b/drivers/staging/iio/iio_dummy_evgen.h @@ -0,0 +1,2 @@ +int iio_dummy_evgen_get_irq(void); +int iio_dummy_evgen_release_irq(int irq); diff --git a/drivers/staging/iio/iio_simple_dummy.c b/drivers/staging/iio/iio_simple_dummy.c new file mode 100644 index 00000000000..fd334a03a49 --- /dev/null +++ b/drivers/staging/iio/iio_simple_dummy.c @@ -0,0 +1,588 @@ +/** + * Copyright (c) 2011 Jonathan Cameron + * + * 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. + * + * A reference industrial I/O driver to illustrate the functionality available. + * + * There are numerous real drivers to illustrate the finer points. + * The purpose of this driver is to provide a driver with far more comments + * and explanatory notes than any 'real' driver would have. + * Anyone starting out writing an IIO driver should first make sure they + * understand all of this driver except those bits specifically marked + * as being present to allow us to 'fake' the presence of hardware. + */ +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/module.h> +#include <linux/moduleparam.h> + +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> +#include <linux/iio/events.h> +#include <linux/iio/buffer.h> +#include "iio_simple_dummy.h" + +/* + * A few elements needed to fake a bus for this driver + * Note instances parameter controls how many of these + * dummy devices are registered. + */ +static unsigned instances = 1; +module_param(instances, int, 0); + +/* Pointer array used to fake bus elements */ +static struct iio_dev **iio_dummy_devs; + +/* Fake a name for the part number, usually obtained from the id table */ +static const char *iio_dummy_part_number = "iio_dummy_part_no"; + +/** + * struct iio_dummy_accel_calibscale - realworld to register mapping + * @val: first value in read_raw - here integer part. + * @val2: second value in read_raw etc - here micro part. + * @regval: register value - magic device specific numbers. + */ +struct iio_dummy_accel_calibscale { +	int val; +	int val2; +	int regval; /* what would be written to hardware */ +}; + +static const struct iio_dummy_accel_calibscale dummy_scales[] = { +	{ 0, 100, 0x8 }, /* 0.000100 */ +	{ 0, 133, 0x7 }, /* 0.000133 */ +	{ 733, 13, 0x9 }, /* 733.000013 */ +}; + +#ifdef CONFIG_IIO_SIMPLE_DUMMY_EVENTS + +/* + * simple event - triggered when value rises above + * a threshold + */ +static const struct iio_event_spec iio_dummy_event = { +	.type = IIO_EV_TYPE_THRESH, +	.dir = IIO_EV_DIR_RISING, +	.mask_separate = BIT(IIO_EV_INFO_VALUE) | BIT(IIO_EV_INFO_ENABLE), +}; + +#endif + +/* + * iio_dummy_channels - Description of available channels + * + * This array of structures tells the IIO core about what the device + * actually provides for a given channel. + */ +static const struct iio_chan_spec iio_dummy_channels[] = { +	/* indexed ADC channel in_voltage0_raw etc */ +	{ +		.type = IIO_VOLTAGE, +		/* Channel has a numeric index of 0 */ +		.indexed = 1, +		.channel = 0, +		/* What other information is available? */ +		.info_mask_separate = +		/* +		 * in_voltage0_raw +		 * Raw (unscaled no bias removal etc) measurement +		 * from the device. +		 */ +		BIT(IIO_CHAN_INFO_RAW) | +		/* +		 * in_voltage0_offset +		 * Offset for userspace to apply prior to scale +		 * when converting to standard units (microvolts) +		 */ +		BIT(IIO_CHAN_INFO_OFFSET) | +		/* +		 * in_voltage0_scale +		 * Multipler for userspace to apply post offset +		 * when converting to standard units (microvolts) +		 */ +		BIT(IIO_CHAN_INFO_SCALE), +		/* +		 * sampling_frequency +		 * The frequency in Hz at which the channels are sampled +		 */ +		.info_mask_shared_by_dir = BIT(IIO_CHAN_INFO_SAMP_FREQ), +		/* The ordering of elements in the buffer via an enum */ +		.scan_index = voltage0, +		.scan_type = { /* Description of storage in buffer */ +			.sign = 'u', /* unsigned */ +			.realbits = 13, /* 13 bits */ +			.storagebits = 16, /* 16 bits used for storage */ +			.shift = 0, /* zero shift */ +		}, +#ifdef CONFIG_IIO_SIMPLE_DUMMY_EVENTS +		.event_spec = &iio_dummy_event, +		.num_event_specs = 1, +#endif /* CONFIG_IIO_SIMPLE_DUMMY_EVENTS */ +	}, +	/* Differential ADC channel in_voltage1-voltage2_raw etc*/ +	{ +		.type = IIO_VOLTAGE, +		.differential = 1, +		/* +		 * Indexing for differential channels uses channel +		 * for the positive part, channel2 for the negative. +		 */ +		.indexed = 1, +		.channel = 1, +		.channel2 = 2, +		/* +		 * in_voltage1-voltage2_raw +		 * Raw (unscaled no bias removal etc) measurement +		 * from the device. +		 */ +		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), +		/* +		 * in_voltage-voltage_scale +		 * Shared version of scale - shared by differential +		 * input channels of type IIO_VOLTAGE. +		 */ +		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), +		/* +		 * sampling_frequency +		 * The frequency in Hz at which the channels are sampled +		 */ +		.scan_index = diffvoltage1m2, +		.scan_type = { /* Description of storage in buffer */ +			.sign = 's', /* signed */ +			.realbits = 12, /* 12 bits */ +			.storagebits = 16, /* 16 bits used for storage */ +			.shift = 0, /* zero shift */ +		}, +	}, +	/* Differential ADC channel in_voltage3-voltage4_raw etc*/ +	{ +		.type = IIO_VOLTAGE, +		.differential = 1, +		.indexed = 1, +		.channel = 3, +		.channel2 = 4, +		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), +		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), +		.info_mask_shared_by_dir = BIT(IIO_CHAN_INFO_SAMP_FREQ), +		.scan_index = diffvoltage3m4, +		.scan_type = { +			.sign = 's', +			.realbits = 11, +			.storagebits = 16, +			.shift = 0, +		}, +	}, +	/* +	 * 'modified' (i.e. axis specified) acceleration channel +	 * in_accel_z_raw +	 */ +	{ +		.type = IIO_ACCEL, +		.modified = 1, +		/* Channel 2 is use for modifiers */ +		.channel2 = IIO_MOD_X, +		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | +		/* +		 * Internal bias and gain correction values. Applied +		 * by the hardware or driver prior to userspace +		 * seeing the readings. Typically part of hardware +		 * calibration. +		 */ +		BIT(IIO_CHAN_INFO_CALIBSCALE) | +		BIT(IIO_CHAN_INFO_CALIBBIAS), +		.info_mask_shared_by_dir = BIT(IIO_CHAN_INFO_SAMP_FREQ), +		.scan_index = accelx, +		.scan_type = { /* Description of storage in buffer */ +			.sign = 's', /* signed */ +			.realbits = 16, /* 16 bits */ +			.storagebits = 16, /* 16 bits used for storage */ +			.shift = 0, /* zero shift */ +		}, +	}, +	/* +	 * Convenience macro for timestamps. 4 is the index in +	 * the buffer. +	 */ +	IIO_CHAN_SOFT_TIMESTAMP(4), +	/* DAC channel out_voltage0_raw */ +	{ +		.type = IIO_VOLTAGE, +		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), +		.output = 1, +		.indexed = 1, +		.channel = 0, +	}, +}; + +/** + * iio_dummy_read_raw() - data read function. + * @indio_dev:	the struct iio_dev associated with this device instance + * @chan:	the channel whose data is to be read + * @val:	first element of returned value (typically INT) + * @val2:	second element of returned value (typically MICRO) + * @mask:	what we actually want to read as per the info_mask_* + *		in iio_chan_spec. + */ +static int iio_dummy_read_raw(struct iio_dev *indio_dev, +			      struct iio_chan_spec const *chan, +			      int *val, +			      int *val2, +			      long mask) +{ +	struct iio_dummy_state *st = iio_priv(indio_dev); +	int ret = -EINVAL; + +	mutex_lock(&st->lock); +	switch (mask) { +	case IIO_CHAN_INFO_RAW: /* magic value - channel value read */ +		switch (chan->type) { +		case IIO_VOLTAGE: +			if (chan->output) { +				/* Set integer part to cached value */ +				*val = st->dac_val; +				ret = IIO_VAL_INT; +			} else if (chan->differential) { +				if (chan->channel == 1) +					*val = st->differential_adc_val[0]; +				else +					*val = st->differential_adc_val[1]; +				ret = IIO_VAL_INT; +			} else { +				*val = st->single_ended_adc_val; +				ret = IIO_VAL_INT; +			} +			break; +		case IIO_ACCEL: +			*val = st->accel_val; +			ret = IIO_VAL_INT; +			break; +		default: +			break; +		} +		break; +	case IIO_CHAN_INFO_OFFSET: +		/* only single ended adc -> 7 */ +		*val = 7; +		ret = IIO_VAL_INT; +		break; +	case IIO_CHAN_INFO_SCALE: +		switch (chan->differential) { +		case 0: +			/* only single ended adc -> 0.001333 */ +			*val = 0; +			*val2 = 1333; +			ret = IIO_VAL_INT_PLUS_MICRO; +			break; +		case 1: +			/* all differential adc channels -> 0.000001344 */ +			*val = 0; +			*val2 = 1344; +			ret = IIO_VAL_INT_PLUS_NANO; +		} +		break; +	case IIO_CHAN_INFO_CALIBBIAS: +		/* only the acceleration axis - read from cache */ +		*val = st->accel_calibbias; +		ret = IIO_VAL_INT; +		break; +	case IIO_CHAN_INFO_CALIBSCALE: +		*val = st->accel_calibscale->val; +		*val2 = st->accel_calibscale->val2; +		ret = IIO_VAL_INT_PLUS_MICRO; +		break; +	case IIO_CHAN_INFO_SAMP_FREQ: +		*val = 3; +		*val2 = 33; +		ret = IIO_VAL_INT_PLUS_NANO; +		break; +	default: +		break; +	} +	mutex_unlock(&st->lock); +	return ret; +} + +/** + * iio_dummy_write_raw() - data write function. + * @indio_dev:	the struct iio_dev associated with this device instance + * @chan:	the channel whose data is to be written + * @val:	first element of value to set (typically INT) + * @val2:	second element of value to set (typically MICRO) + * @mask:	what we actually want to write as per the info_mask_* + *		in iio_chan_spec. + * + * Note that all raw writes are assumed IIO_VAL_INT and info mask elements + * are assumed to be IIO_INT_PLUS_MICRO unless the callback write_raw_get_fmt + * in struct iio_info is provided by the driver. + */ +static int iio_dummy_write_raw(struct iio_dev *indio_dev, +			       struct iio_chan_spec const *chan, +			       int val, +			       int val2, +			       long mask) +{ +	int i; +	int ret = 0; +	struct iio_dummy_state *st = iio_priv(indio_dev); + +	switch (mask) { +	case IIO_CHAN_INFO_RAW: +		if (chan->output == 0) +			return -EINVAL; + +		/* Locking not required as writing single value */ +		mutex_lock(&st->lock); +		st->dac_val = val; +		mutex_unlock(&st->lock); +		return 0; +	case IIO_CHAN_INFO_CALIBSCALE: +		mutex_lock(&st->lock); +		/* Compare against table - hard matching here */ +		for (i = 0; i < ARRAY_SIZE(dummy_scales); i++) +			if (val == dummy_scales[i].val && +			    val2 == dummy_scales[i].val2) +				break; +		if (i == ARRAY_SIZE(dummy_scales)) +			ret = -EINVAL; +		else +			st->accel_calibscale = &dummy_scales[i]; +		mutex_unlock(&st->lock); +		return ret; +	case IIO_CHAN_INFO_CALIBBIAS: +		mutex_lock(&st->lock); +		st->accel_calibbias = val; +		mutex_unlock(&st->lock); +		return 0; + +	default: +		return -EINVAL; +	} +} + +/* + * Device type specific information. + */ +static const struct iio_info iio_dummy_info = { +	.driver_module = THIS_MODULE, +	.read_raw = &iio_dummy_read_raw, +	.write_raw = &iio_dummy_write_raw, +#ifdef CONFIG_IIO_SIMPLE_DUMMY_EVENTS +	.read_event_config = &iio_simple_dummy_read_event_config, +	.write_event_config = &iio_simple_dummy_write_event_config, +	.read_event_value = &iio_simple_dummy_read_event_value, +	.write_event_value = &iio_simple_dummy_write_event_value, +#endif /* CONFIG_IIO_SIMPLE_DUMMY_EVENTS */ +}; + +/** + * iio_dummy_init_device() - device instance specific init + * @indio_dev: the iio device structure + * + * Most drivers have one of these to set up default values, + * reset the device to known state etc. + */ +static int iio_dummy_init_device(struct iio_dev *indio_dev) +{ +	struct iio_dummy_state *st = iio_priv(indio_dev); + +	st->dac_val = 0; +	st->single_ended_adc_val = 73; +	st->differential_adc_val[0] = 33; +	st->differential_adc_val[1] = -34; +	st->accel_val = 34; +	st->accel_calibbias = -7; +	st->accel_calibscale = &dummy_scales[0]; + +	return 0; +} + +/** + * iio_dummy_probe() - device instance probe + * @index: an id number for this instance. + * + * Arguments are bus type specific. + * I2C: iio_dummy_probe(struct i2c_client *client, + *                      const struct i2c_device_id *id) + * SPI: iio_dummy_probe(struct spi_device *spi) + */ +static int iio_dummy_probe(int index) +{ +	int ret; +	struct iio_dev *indio_dev; +	struct iio_dummy_state *st; + +	/* +	 * Allocate an IIO device. +	 * +	 * This structure contains all generic state +	 * information about the device instance. +	 * It also has a region (accessed by iio_priv() +	 * for chip specific state information. +	 */ +	indio_dev = iio_device_alloc(sizeof(*st)); +	if (indio_dev == NULL) { +		ret = -ENOMEM; +		goto error_ret; +	} + +	st = iio_priv(indio_dev); +	mutex_init(&st->lock); + +	iio_dummy_init_device(indio_dev); +	/* +	 * With hardware: Set the parent device. +	 * indio_dev->dev.parent = &spi->dev; +	 * indio_dev->dev.parent = &client->dev; +	 */ + +	 /* +	 * Make the iio_dev struct available to remove function. +	 * Bus equivalents +	 * i2c_set_clientdata(client, indio_dev); +	 * spi_set_drvdata(spi, indio_dev); +	 */ +	iio_dummy_devs[index] = indio_dev; + + +	/* +	 * Set the device name. +	 * +	 * This is typically a part number and obtained from the module +	 * id table. +	 * e.g. for i2c and spi: +	 *    indio_dev->name = id->name; +	 *    indio_dev->name = spi_get_device_id(spi)->name; +	 */ +	indio_dev->name = iio_dummy_part_number; + +	/* Provide description of available channels */ +	indio_dev->channels = iio_dummy_channels; +	indio_dev->num_channels = ARRAY_SIZE(iio_dummy_channels); + +	/* +	 * Provide device type specific interface functions and +	 * constant data. +	 */ +	indio_dev->info = &iio_dummy_info; + +	/* Specify that device provides sysfs type interfaces */ +	indio_dev->modes = INDIO_DIRECT_MODE; + +	ret = iio_simple_dummy_events_register(indio_dev); +	if (ret < 0) +		goto error_free_device; + +	/* +	 * Configure buffered capture support and register the channels with the +	 * buffer, but avoid the output channel being registered by reducing the +	 * number of channels by 1. +	 */ +	ret = iio_simple_dummy_configure_buffer(indio_dev, +						iio_dummy_channels, 5); +	if (ret < 0) +		goto error_unregister_events; + +	ret = iio_device_register(indio_dev); +	if (ret < 0) +		goto error_unconfigure_buffer; + +	return 0; +error_unconfigure_buffer: +	iio_simple_dummy_unconfigure_buffer(indio_dev); +error_unregister_events: +	iio_simple_dummy_events_unregister(indio_dev); +error_free_device: +	iio_device_free(indio_dev); +error_ret: +	return ret; +} + +/** + * iio_dummy_remove() - device instance removal function + * @index: device index. + * + * Parameters follow those of iio_dummy_probe for buses. + */ +static int iio_dummy_remove(int index) +{ +	int ret; +	/* +	 * Get a pointer to the device instance iio_dev structure +	 * from the bus subsystem. E.g. +	 * struct iio_dev *indio_dev = i2c_get_clientdata(client); +	 * struct iio_dev *indio_dev = spi_get_drvdata(spi); +	 */ +	struct iio_dev *indio_dev = iio_dummy_devs[index]; + + +	/* Unregister the device */ +	iio_device_unregister(indio_dev); + +	/* Device specific code to power down etc */ + +	/* Buffered capture related cleanup */ +	iio_simple_dummy_unconfigure_buffer(indio_dev); + +	ret = iio_simple_dummy_events_unregister(indio_dev); +	if (ret) +		goto error_ret; + +	/* Free all structures */ +	iio_device_free(indio_dev); + +error_ret: +	return ret; +} + +/** + * iio_dummy_init() -  device driver registration + * + * Varies depending on bus type of the device. As there is no device + * here, call probe directly. For information on device registration + * i2c: + * Documentation/i2c/writing-clients + * spi: + * Documentation/spi/spi-summary + */ +static __init int iio_dummy_init(void) +{ +	int i, ret; +	if (instances > 10) { +		instances = 1; +		return -EINVAL; +	} + +	/* Fake a bus */ +	iio_dummy_devs = kcalloc(instances, sizeof(*iio_dummy_devs), +				 GFP_KERNEL); +	/* Here we have no actual device so call probe */ +	for (i = 0; i < instances; i++) { +		ret = iio_dummy_probe(i); +		if (ret < 0) +			return ret; +	} +	return 0; +} +module_init(iio_dummy_init); + +/** + * iio_dummy_exit() - device driver removal + * + * Varies depending on bus type of the device. + * As there is no device here, call remove directly. + */ +static __exit void iio_dummy_exit(void) +{ +	int i; +	for (i = 0; i < instances; i++) +		iio_dummy_remove(i); +	kfree(iio_dummy_devs); +} +module_exit(iio_dummy_exit); + +MODULE_AUTHOR("Jonathan Cameron <jic23@kernel.org>"); +MODULE_DESCRIPTION("IIO dummy driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/iio/iio_simple_dummy.h b/drivers/staging/iio/iio_simple_dummy.h new file mode 100644 index 00000000000..b126196cdf3 --- /dev/null +++ b/drivers/staging/iio/iio_simple_dummy.h @@ -0,0 +1,120 @@ +/** + * Copyright (c) 2011 Jonathan Cameron + * + * 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. + * + * Join together the various functionality of iio_simple_dummy driver + */ + +#include <linux/kernel.h> + +struct iio_dummy_accel_calibscale; + +/** + * struct iio_dummy_state - device instance specific state. + * @dac_val:			cache for dac value + * @single_ended_adc_val:	cache for single ended adc value + * @differential_adc_val:	cache for differential adc value + * @accel_val:			cache for acceleration value + * @accel_calibbias:		cache for acceleration calibbias + * @accel_calibscale:		cache for acceleration calibscale + * @lock:			lock to ensure state is consistent + * @event_irq:			irq number for event line (faked) + * @event_val:			cache for event theshold value + * @event_en:			cache of whether event is enabled + */ +struct iio_dummy_state { +	int dac_val; +	int single_ended_adc_val; +	int differential_adc_val[2]; +	int accel_val; +	int accel_calibbias; +	const struct iio_dummy_accel_calibscale *accel_calibscale; +	struct mutex lock; +#ifdef CONFIG_IIO_SIMPLE_DUMMY_EVENTS +	int event_irq; +	int event_val; +	bool event_en; +#endif /* CONFIG_IIO_SIMPLE_DUMMY_EVENTS */ +}; + +#ifdef CONFIG_IIO_SIMPLE_DUMMY_EVENTS + +struct iio_dev; + +int iio_simple_dummy_read_event_config(struct iio_dev *indio_dev, +				       const struct iio_chan_spec *chan, +				       enum iio_event_type type, +				       enum iio_event_direction dir); + +int iio_simple_dummy_write_event_config(struct iio_dev *indio_dev, +					const struct iio_chan_spec *chan, +					enum iio_event_type type, +					enum iio_event_direction dir, +					int state); + +int iio_simple_dummy_read_event_value(struct iio_dev *indio_dev, +				      const struct iio_chan_spec *chan, +				      enum iio_event_type type, +				      enum iio_event_direction dir, +				      enum iio_event_info info, int *val, +				      int *val2); + +int iio_simple_dummy_write_event_value(struct iio_dev *indio_dev, +				       const struct iio_chan_spec *chan, +				       enum iio_event_type type, +				       enum iio_event_direction dir, +				       enum iio_event_info info, int val, +				       int val2); + +int iio_simple_dummy_events_register(struct iio_dev *indio_dev); +int iio_simple_dummy_events_unregister(struct iio_dev *indio_dev); + +#else /* Stubs for when events are disabled at compile time */ + +static inline int +iio_simple_dummy_events_register(struct iio_dev *indio_dev) +{ +	return 0; +}; + +static inline int +iio_simple_dummy_events_unregister(struct iio_dev *indio_dev) +{ +	return 0; +}; + +#endif /* CONFIG_IIO_SIMPLE_DUMMY_EVENTS*/ + +/** + * enum iio_simple_dummy_scan_elements - scan index enum + * @voltage0:		the single ended voltage channel + * @diffvoltage1m2:	first differential channel + * @diffvoltage3m4:	second differenial channel + * @accelx:		acceleration channel + * + * Enum provides convenient numbering for the scan index. + */ +enum iio_simple_dummy_scan_elements { +	voltage0, +	diffvoltage1m2, +	diffvoltage3m4, +	accelx, +}; + +#ifdef CONFIG_IIO_SIMPLE_DUMMY_BUFFER +int iio_simple_dummy_configure_buffer(struct iio_dev *indio_dev, +	const struct iio_chan_spec *channels, unsigned int num_channels); +void iio_simple_dummy_unconfigure_buffer(struct iio_dev *indio_dev); +#else +static inline int iio_simple_dummy_configure_buffer(struct iio_dev *indio_dev, +	const struct iio_chan_spec *channels, unsigned int num_channels) +{ +	return 0; +}; +static inline +void iio_simple_dummy_unconfigure_buffer(struct iio_dev *indio_dev) +{}; +#endif /* CONFIG_IIO_SIMPLE_DUMMY_BUFFER */ diff --git a/drivers/staging/iio/iio_simple_dummy_buffer.c b/drivers/staging/iio/iio_simple_dummy_buffer.c new file mode 100644 index 00000000000..46c134b2a5d --- /dev/null +++ b/drivers/staging/iio/iio_simple_dummy_buffer.c @@ -0,0 +1,199 @@ +/** + * Copyright (c) 2011 Jonathan Cameron + * + * 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. + * + * Buffer handling elements of industrial I/O reference driver. + * Uses the kfifo buffer. + * + * To test without hardware use the sysfs trigger. + */ + +#include <linux/kernel.h> +#include <linux/export.h> +#include <linux/slab.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/bitmap.h> + +#include <linux/iio/iio.h> +#include <linux/iio/trigger_consumer.h> +#include <linux/iio/kfifo_buf.h> + +#include "iio_simple_dummy.h" + +/* Some fake data */ + +static const s16 fakedata[] = { +	[voltage0] = 7, +	[diffvoltage1m2] = -33, +	[diffvoltage3m4] = -2, +	[accelx] = 344, +}; +/** + * iio_simple_dummy_trigger_h() - the trigger handler function + * @irq: the interrupt number + * @p: private data - always a pointer to the poll func. + * + * This is the guts of buffered capture. On a trigger event occurring, + * if the pollfunc is attached then this handler is called as a threaded + * interrupt (and hence may sleep). It is responsible for grabbing data + * from the device and pushing it into the associated buffer. + */ +static irqreturn_t iio_simple_dummy_trigger_h(int irq, void *p) +{ +	struct iio_poll_func *pf = p; +	struct iio_dev *indio_dev = pf->indio_dev; +	int len = 0; +	u16 *data; + +	data = kmalloc(indio_dev->scan_bytes, GFP_KERNEL); +	if (data == NULL) +		goto done; + +	if (!bitmap_empty(indio_dev->active_scan_mask, indio_dev->masklength)) { +		/* +		 * Three common options here: +		 * hardware scans: certain combinations of channels make +		 *   up a fast read.  The capture will consist of all of them. +		 *   Hence we just call the grab data function and fill the +		 *   buffer without processing. +		 * software scans: can be considered to be random access +		 *   so efficient reading is just a case of minimal bus +		 *   transactions. +		 * software culled hardware scans: +		 *   occasionally a driver may process the nearest hardware +		 *   scan to avoid storing elements that are not desired. This +		 *   is the fiddliest option by far. +		 * Here let's pretend we have random access. And the values are +		 * in the constant table fakedata. +		 */ +		int i, j; +		for (i = 0, j = 0; +		     i < bitmap_weight(indio_dev->active_scan_mask, +				       indio_dev->masklength); +		     i++, j++) { +			j = find_next_bit(indio_dev->active_scan_mask, +					  indio_dev->masklength, j); +			/* random access read from the 'device' */ +			data[i] = fakedata[j]; +			len += 2; +		} +	} + +	iio_push_to_buffers_with_timestamp(indio_dev, data, iio_get_time_ns()); + +	kfree(data); + +done: +	/* +	 * Tell the core we are done with this trigger and ready for the +	 * next one. +	 */ +	iio_trigger_notify_done(indio_dev->trig); + +	return IRQ_HANDLED; +} + +static const struct iio_buffer_setup_ops iio_simple_dummy_buffer_setup_ops = { +	/* +	 * iio_triggered_buffer_postenable: +	 * Generic function that simply attaches the pollfunc to the trigger. +	 * Replace this to mess with hardware state before we attach the +	 * trigger. +	 */ +	.postenable = &iio_triggered_buffer_postenable, +	/* +	 * iio_triggered_buffer_predisable: +	 * Generic function that simple detaches the pollfunc from the trigger. +	 * Replace this to put hardware state back again after the trigger is +	 * detached but before userspace knows we have disabled the ring. +	 */ +	.predisable = &iio_triggered_buffer_predisable, +}; + +int iio_simple_dummy_configure_buffer(struct iio_dev *indio_dev, +	const struct iio_chan_spec *channels, unsigned int num_channels) +{ +	int ret; +	struct iio_buffer *buffer; + +	/* Allocate a buffer to use - here a kfifo */ +	buffer = iio_kfifo_allocate(indio_dev); +	if (buffer == NULL) { +		ret = -ENOMEM; +		goto error_ret; +	} + +	iio_device_attach_buffer(indio_dev, buffer); + +	/* Enable timestamps by default */ +	buffer->scan_timestamp = true; + +	/* +	 * Tell the core what device type specific functions should +	 * be run on either side of buffer capture enable / disable. +	 */ +	indio_dev->setup_ops = &iio_simple_dummy_buffer_setup_ops; + +	/* +	 * Configure a polling function. +	 * When a trigger event with this polling function connected +	 * occurs, this function is run. Typically this grabs data +	 * from the device. +	 * +	 * NULL for the bottom half. This is normally implemented only if we +	 * either want to ping a capture now pin (no sleeping) or grab +	 * a timestamp as close as possible to a data ready trigger firing. +	 * +	 * IRQF_ONESHOT ensures irqs are masked such that only one instance +	 * of the handler can run at a time. +	 * +	 * "iio_simple_dummy_consumer%d" formatting string for the irq 'name' +	 * as seen under /proc/interrupts. Remaining parameters as per printk. +	 */ +	indio_dev->pollfunc = iio_alloc_pollfunc(NULL, +						 &iio_simple_dummy_trigger_h, +						 IRQF_ONESHOT, +						 indio_dev, +						 "iio_simple_dummy_consumer%d", +						 indio_dev->id); + +	if (indio_dev->pollfunc == NULL) { +		ret = -ENOMEM; +		goto error_free_buffer; +	} + +	/* +	 * Notify the core that this device is capable of buffered capture +	 * driven by a trigger. +	 */ +	indio_dev->modes |= INDIO_BUFFER_TRIGGERED; + +	ret = iio_buffer_register(indio_dev, channels, num_channels); +	if (ret) +		goto error_dealloc_pollfunc; + +	return 0; + +error_dealloc_pollfunc: +	iio_dealloc_pollfunc(indio_dev->pollfunc); +error_free_buffer: +	iio_kfifo_free(indio_dev->buffer); +error_ret: +	return ret; + +} + +/** + * iio_simple_dummy_unconfigure_buffer() - release buffer resources + * @indo_dev: device instance state + */ +void iio_simple_dummy_unconfigure_buffer(struct iio_dev *indio_dev) +{ +	iio_buffer_unregister(indio_dev); +	iio_dealloc_pollfunc(indio_dev->pollfunc); +	iio_kfifo_free(indio_dev->buffer); +} diff --git a/drivers/staging/iio/iio_simple_dummy_events.c b/drivers/staging/iio/iio_simple_dummy_events.c new file mode 100644 index 00000000000..812ebd05a7f --- /dev/null +++ b/drivers/staging/iio/iio_simple_dummy_events.c @@ -0,0 +1,210 @@ +/** + * Copyright (c) 2011 Jonathan Cameron + * + * 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. + * + * Event handling elements of industrial I/O reference driver. + */ +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/interrupt.h> +#include <linux/irq.h> + +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> +#include <linux/iio/events.h> +#include "iio_simple_dummy.h" + +/* Evgen 'fakes' interrupt events for this example */ +#include "iio_dummy_evgen.h" + +/** + * iio_simple_dummy_read_event_config() - is event enabled? + * @indio_dev: the device instance data + * @chan: channel for the event whose state is being queried + * @type: type of the event whose state is being queried + * @dir: direction of the vent whose state is being queried + * + * This function would normally query the relevant registers or a cache to + * discover if the event generation is enabled on the device. + */ +int iio_simple_dummy_read_event_config(struct iio_dev *indio_dev, +				       const struct iio_chan_spec *chan, +				       enum iio_event_type type, +				       enum iio_event_direction dir) +{ +	struct iio_dummy_state *st = iio_priv(indio_dev); + +	return st->event_en; +} + +/** + * iio_simple_dummy_write_event_config() - set whether event is enabled + * @indio_dev: the device instance data + * @chan: channel for the event whose state is being set + * @type: type of the event whose state is being set + * @dir: direction of the vent whose state is being set + * @state: whether to enable or disable the device. + * + * This function would normally set the relevant registers on the devices + * so that it generates the specified event. Here it just sets up a cached + * value. + */ +int iio_simple_dummy_write_event_config(struct iio_dev *indio_dev, +					const struct iio_chan_spec *chan, +					enum iio_event_type type, +					enum iio_event_direction dir, +					int state) +{ +	struct iio_dummy_state *st = iio_priv(indio_dev); + +	/* +	 *  Deliberately over the top code splitting to illustrate +	 * how this is done when multiple events exist. +	 */ +	switch (chan->type) { +	case IIO_VOLTAGE: +		switch (type) { +		case IIO_EV_TYPE_THRESH: +			if (dir == IIO_EV_DIR_RISING) +				st->event_en = state; +			else +				return -EINVAL; +			break; +		default: +			return -EINVAL; +		} +	default: +		return -EINVAL; +	} + +	return 0; +} + +/** + * iio_simple_dummy_read_event_value() - get value associated with event + * @indio_dev: device instance specific data + * @chan: channel for the event whose value is being read + * @type: type of the event whose value is being read + * @dir: direction of the vent whose value is being read + * @info: info type of the event whose value is being read + * @val: value for the event code. + * + * Many devices provide a large set of events of which only a subset may + * be enabled at a time, with value registers whose meaning changes depending + * on the event enabled. This often means that the driver must cache the values + * associated with each possible events so that the right value is in place when + * the enabled event is changed. + */ +int iio_simple_dummy_read_event_value(struct iio_dev *indio_dev, +				      const struct iio_chan_spec *chan, +				      enum iio_event_type type, +				      enum iio_event_direction dir, +					  enum iio_event_info info, +				      int *val, int *val2) +{ +	struct iio_dummy_state *st = iio_priv(indio_dev); + +	*val = st->event_val; + +	return IIO_VAL_INT; +} + +/** + * iio_simple_dummy_write_event_value() - set value associate with event + * @indio_dev: device instance specific data + * @chan: channel for the event whose value is being set + * @type: type of the event whose value is being set + * @dir: direction of the vent whose value is being set + * @info: info type of the event whose value is being set + * @val: the value to be set. + */ +int iio_simple_dummy_write_event_value(struct iio_dev *indio_dev, +				       const struct iio_chan_spec *chan, +				       enum iio_event_type type, +				       enum iio_event_direction dir, +					   enum iio_event_info info, +				       int val, int val2) +{ +	struct iio_dummy_state *st = iio_priv(indio_dev); + +	st->event_val = val; + +	return 0; +} + +/** + * iio_simple_dummy_event_handler() - identify and pass on event + * @irq: irq of event line + * @private: pointer to device instance state. + * + * This handler is responsible for querying the device to find out what + * event occurred and for then pushing that event towards userspace. + * Here only one event occurs so we push that directly on with locally + * grabbed timestamp. + */ +static irqreturn_t iio_simple_dummy_event_handler(int irq, void *private) +{ +	struct iio_dev *indio_dev = private; +	iio_push_event(indio_dev, +		       IIO_EVENT_CODE(IIO_VOLTAGE, 0, 0, +				      IIO_EV_DIR_RISING, +				      IIO_EV_TYPE_THRESH, 0, 0, 0), +		       iio_get_time_ns()); +	return IRQ_HANDLED; +} + +/** + * iio_simple_dummy_events_register() - setup interrupt handling for events + * @indio_dev: device instance data + * + * This function requests the threaded interrupt to handle the events. + * Normally the irq is a hardware interrupt and the number comes + * from board configuration files.  Here we get it from a companion + * module that fakes the interrupt for us. Note that module in + * no way forms part of this example. Just assume that events magically + * appear via the provided interrupt. + */ +int iio_simple_dummy_events_register(struct iio_dev *indio_dev) +{ +	struct iio_dummy_state *st = iio_priv(indio_dev); +	int ret; + +	/* Fire up event source - normally not present */ +	st->event_irq = iio_dummy_evgen_get_irq(); +	if (st->event_irq < 0) { +		ret = st->event_irq; +		goto error_ret; +	} +	ret = request_threaded_irq(st->event_irq, +				   NULL, +				   &iio_simple_dummy_event_handler, +				   IRQF_ONESHOT, +				   "iio_simple_event", +				   indio_dev); +	if (ret < 0) +		goto error_free_evgen; +	return 0; + +error_free_evgen: +	iio_dummy_evgen_release_irq(st->event_irq); +error_ret: +	return ret; +} + +/** + * iio_simple_dummy_events_unregister() - tidy up interrupt handling on remove + * @indio_dev: device instance data + */ +int iio_simple_dummy_events_unregister(struct iio_dev *indio_dev) +{ +	struct iio_dummy_state *st = iio_priv(indio_dev); + +	free_irq(st->event_irq, indio_dev); +	/* Not part of normal driver */ +	iio_dummy_evgen_release_irq(st->event_irq); + +	return 0; +} diff --git a/drivers/staging/iio/impedance-analyzer/Kconfig b/drivers/staging/iio/impedance-analyzer/Kconfig new file mode 100644 index 00000000000..dd97b6bb3fd --- /dev/null +++ b/drivers/staging/iio/impedance-analyzer/Kconfig @@ -0,0 +1,18 @@ +# +# Impedance Converter, Network Analyzer drivers +# +menu "Network Analyzer, Impedance Converters" + +config AD5933 +	tristate "Analog Devices AD5933, AD5934 driver" +	depends on I2C +	select IIO_BUFFER +	select IIO_KFIFO_BUF +	help +	  Say yes here to build support for Analog Devices Impedance Converter, +	  Network Analyzer, AD5933/4, provides direct access via sysfs. + +	  To compile this driver as a module, choose M here: the +	  module will be called ad5933. + +endmenu diff --git a/drivers/staging/iio/impedance-analyzer/Makefile b/drivers/staging/iio/impedance-analyzer/Makefile new file mode 100644 index 00000000000..7604d786583 --- /dev/null +++ b/drivers/staging/iio/impedance-analyzer/Makefile @@ -0,0 +1,5 @@ +# +# Makefile for Impedance Converter, Network Analyzer drivers +# + +obj-$(CONFIG_AD5933) += ad5933.o diff --git a/drivers/staging/iio/impedance-analyzer/ad5933.c b/drivers/staging/iio/impedance-analyzer/ad5933.c new file mode 100644 index 00000000000..2b96665da8a --- /dev/null +++ b/drivers/staging/iio/impedance-analyzer/ad5933.c @@ -0,0 +1,821 @@ +/* + * AD5933 AD5934 Impedance Converter, Network Analyzer + * + * Copyright 2011 Analog Devices Inc. + * + * Licensed under the GPL-2. + */ + +#include <linux/interrupt.h> +#include <linux/device.h> +#include <linux/kernel.h> +#include <linux/sysfs.h> +#include <linux/i2c.h> +#include <linux/regulator/consumer.h> +#include <linux/slab.h> +#include <linux/types.h> +#include <linux/err.h> +#include <linux/delay.h> +#include <linux/module.h> +#include <asm/div64.h> + +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> +#include <linux/iio/buffer.h> +#include <linux/iio/kfifo_buf.h> + +#include "ad5933.h" + +/* AD5933/AD5934 Registers */ +#define AD5933_REG_CONTROL_HB		0x80	/* R/W, 2 bytes */ +#define AD5933_REG_CONTROL_LB		0x81	/* R/W, 2 bytes */ +#define AD5933_REG_FREQ_START		0x82	/* R/W, 3 bytes */ +#define AD5933_REG_FREQ_INC		0x85	/* R/W, 3 bytes */ +#define AD5933_REG_INC_NUM		0x88	/* R/W, 2 bytes, 9 bit */ +#define AD5933_REG_SETTLING_CYCLES	0x8A	/* R/W, 2 bytes */ +#define AD5933_REG_STATUS		0x8F	/* R, 1 byte */ +#define AD5933_REG_TEMP_DATA		0x92	/* R, 2 bytes*/ +#define AD5933_REG_REAL_DATA		0x94	/* R, 2 bytes*/ +#define AD5933_REG_IMAG_DATA		0x96	/* R, 2 bytes*/ + +/* AD5933_REG_CONTROL_HB Bits */ +#define AD5933_CTRL_INIT_START_FREQ	(0x1 << 4) +#define AD5933_CTRL_START_SWEEP		(0x2 << 4) +#define AD5933_CTRL_INC_FREQ		(0x3 << 4) +#define AD5933_CTRL_REPEAT_FREQ		(0x4 << 4) +#define AD5933_CTRL_MEASURE_TEMP	(0x9 << 4) +#define AD5933_CTRL_POWER_DOWN		(0xA << 4) +#define AD5933_CTRL_STANDBY		(0xB << 4) + +#define AD5933_CTRL_RANGE_2000mVpp	(0x0 << 1) +#define AD5933_CTRL_RANGE_200mVpp	(0x1 << 1) +#define AD5933_CTRL_RANGE_400mVpp	(0x2 << 1) +#define AD5933_CTRL_RANGE_1000mVpp	(0x3 << 1) +#define AD5933_CTRL_RANGE(x)		((x) << 1) + +#define AD5933_CTRL_PGA_GAIN_1		(0x1 << 0) +#define AD5933_CTRL_PGA_GAIN_5		(0x0 << 0) + +/* AD5933_REG_CONTROL_LB Bits */ +#define AD5933_CTRL_RESET		(0x1 << 4) +#define AD5933_CTRL_INT_SYSCLK		(0x0 << 3) +#define AD5933_CTRL_EXT_SYSCLK		(0x1 << 3) + +/* AD5933_REG_STATUS Bits */ +#define AD5933_STAT_TEMP_VALID		(0x1 << 0) +#define AD5933_STAT_DATA_VALID		(0x1 << 1) +#define AD5933_STAT_SWEEP_DONE		(0x1 << 2) + +/* I2C Block Commands */ +#define AD5933_I2C_BLOCK_WRITE		0xA0 +#define AD5933_I2C_BLOCK_READ		0xA1 +#define AD5933_I2C_ADDR_POINTER		0xB0 + +/* Device Specs */ +#define AD5933_INT_OSC_FREQ_Hz		16776000 +#define AD5933_MAX_OUTPUT_FREQ_Hz	100000 +#define AD5933_MAX_RETRIES		100 + +#define AD5933_OUT_RANGE		1 +#define AD5933_OUT_RANGE_AVAIL		2 +#define AD5933_OUT_SETTLING_CYCLES	3 +#define AD5933_IN_PGA_GAIN		4 +#define AD5933_IN_PGA_GAIN_AVAIL	5 +#define AD5933_FREQ_POINTS		6 + +#define AD5933_POLL_TIME_ms		10 +#define AD5933_INIT_EXCITATION_TIME_ms	100 + +struct ad5933_state { +	struct i2c_client		*client; +	struct regulator		*reg; +	struct ad5933_platform_data	*pdata; +	struct delayed_work		work; +	unsigned long			mclk_hz; +	unsigned char			ctrl_hb; +	unsigned char			ctrl_lb; +	unsigned			range_avail[4]; +	unsigned short			vref_mv; +	unsigned short			settling_cycles; +	unsigned short			freq_points; +	unsigned			freq_start; +	unsigned			freq_inc; +	unsigned			state; +	unsigned			poll_time_jiffies; +}; + +static struct ad5933_platform_data ad5933_default_pdata  = { +	.vref_mv = 3300, +}; + +static const struct iio_chan_spec ad5933_channels[] = { +	{ +		.type = IIO_TEMP, +		.indexed = 1, +		.channel = 0, +		.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), +		.address = AD5933_REG_TEMP_DATA, +		.scan_type = { +			.sign = 's', +			.realbits = 14, +			.storagebits = 16, +		}, +	}, { /* Ring Channels */ +		.type = IIO_VOLTAGE, +		.indexed = 1, +		.channel = 0, +		.extend_name = "real_raw", +		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | +		BIT(IIO_CHAN_INFO_SCALE), +		.address = AD5933_REG_REAL_DATA, +		.scan_index = 0, +		.scan_type = { +			.sign = 's', +			.realbits = 16, +			.storagebits = 16, +		}, +	}, { +		.type = IIO_VOLTAGE, +		.indexed = 1, +		.channel = 0, +		.extend_name = "imag_raw", +		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | +		BIT(IIO_CHAN_INFO_SCALE), +		.address = AD5933_REG_IMAG_DATA, +		.scan_index = 1, +		.scan_type = { +			.sign = 's', +			.realbits = 16, +			.storagebits = 16, +		}, +	}, +}; + +static int ad5933_i2c_write(struct i2c_client *client, +			      u8 reg, u8 len, u8 *data) +{ +	int ret; + +	while (len--) { +		ret = i2c_smbus_write_byte_data(client, reg++, *data++); +		if (ret < 0) { +			dev_err(&client->dev, "I2C write error\n"); +			return ret; +		} +	} +	return 0; +} + +static int ad5933_i2c_read(struct i2c_client *client, +			      u8 reg, u8 len, u8 *data) +{ +	int ret; + +	while (len--) { +		ret = i2c_smbus_read_byte_data(client, reg++); +		if (ret < 0) { +			dev_err(&client->dev, "I2C read error\n"); +			return ret; +		} +		*data++ = ret; +	} +	return 0; +} + +static int ad5933_cmd(struct ad5933_state *st, unsigned char cmd) +{ +	unsigned char dat = st->ctrl_hb | cmd; + +	return ad5933_i2c_write(st->client, +			AD5933_REG_CONTROL_HB, 1, &dat); +} + +static int ad5933_reset(struct ad5933_state *st) +{ +	unsigned char dat = st->ctrl_lb | AD5933_CTRL_RESET; +	return ad5933_i2c_write(st->client, +			AD5933_REG_CONTROL_LB, 1, &dat); +} + +static int ad5933_wait_busy(struct ad5933_state *st, unsigned char event) +{ +	unsigned char val, timeout = AD5933_MAX_RETRIES; +	int ret; + +	while (timeout--) { +		ret =  ad5933_i2c_read(st->client, AD5933_REG_STATUS, 1, &val); +		if (ret < 0) +			return ret; +		if (val & event) +			return val; +		cpu_relax(); +		mdelay(1); +	} + +	return -EAGAIN; +} + +static int ad5933_set_freq(struct ad5933_state *st, +			   unsigned reg, unsigned long freq) +{ +	unsigned long long freqreg; +	union { +		u32 d32; +		u8 d8[4]; +	} dat; + +	freqreg = (u64) freq * (u64) (1 << 27); +	do_div(freqreg, st->mclk_hz / 4); + +	switch (reg) { +	case AD5933_REG_FREQ_START: +		st->freq_start = freq; +		break; +	case AD5933_REG_FREQ_INC: +		st->freq_inc = freq; +		break; +	default: +		return -EINVAL; +	} + +	dat.d32 = cpu_to_be32(freqreg); +	return ad5933_i2c_write(st->client, reg, 3, &dat.d8[1]); +} + +static int ad5933_setup(struct ad5933_state *st) +{ +	unsigned short dat; +	int ret; + +	ret = ad5933_reset(st); +	if (ret < 0) +		return ret; + +	ret = ad5933_set_freq(st, AD5933_REG_FREQ_START, 10000); +	if (ret < 0) +		return ret; + +	ret = ad5933_set_freq(st, AD5933_REG_FREQ_INC, 200); +	if (ret < 0) +		return ret; + +	st->settling_cycles = 10; +	dat = cpu_to_be16(st->settling_cycles); + +	ret = ad5933_i2c_write(st->client, +			AD5933_REG_SETTLING_CYCLES, 2, (u8 *)&dat); +	if (ret < 0) +		return ret; + +	st->freq_points = 100; +	dat = cpu_to_be16(st->freq_points); + +	return ad5933_i2c_write(st->client, AD5933_REG_INC_NUM, 2, (u8 *)&dat); +} + +static void ad5933_calc_out_ranges(struct ad5933_state *st) +{ +	int i; +	unsigned normalized_3v3[4] = {1980, 198, 383, 970}; + +	for (i = 0; i < 4; i++) +		st->range_avail[i] = normalized_3v3[i] * st->vref_mv / 3300; + +} + +/* + * handles: AD5933_REG_FREQ_START and AD5933_REG_FREQ_INC + */ + +static ssize_t ad5933_show_frequency(struct device *dev, +					struct device_attribute *attr, +					char *buf) +{ +	struct iio_dev *indio_dev = dev_to_iio_dev(dev); +	struct ad5933_state *st = iio_priv(indio_dev); +	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); +	int ret; +	unsigned long long freqreg; +	union { +		u32 d32; +		u8 d8[4]; +	} dat; + +	mutex_lock(&indio_dev->mlock); +	ret = ad5933_i2c_read(st->client, this_attr->address, 3, &dat.d8[1]); +	mutex_unlock(&indio_dev->mlock); +	if (ret < 0) +		return ret; + +	freqreg = be32_to_cpu(dat.d32) & 0xFFFFFF; + +	freqreg = (u64) freqreg * (u64) (st->mclk_hz / 4); +	do_div(freqreg, 1 << 27); + +	return sprintf(buf, "%d\n", (int) freqreg); +} + +static ssize_t ad5933_store_frequency(struct device *dev, +					 struct device_attribute *attr, +					 const char *buf, +					 size_t len) +{ +	struct iio_dev *indio_dev = dev_to_iio_dev(dev); +	struct ad5933_state *st = iio_priv(indio_dev); +	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); +	unsigned long val; +	int ret; + +	ret = kstrtoul(buf, 10, &val); +	if (ret) +		return ret; + +	if (val > AD5933_MAX_OUTPUT_FREQ_Hz) +		return -EINVAL; + +	mutex_lock(&indio_dev->mlock); +	ret = ad5933_set_freq(st, this_attr->address, val); +	mutex_unlock(&indio_dev->mlock); + +	return ret ? ret : len; +} + +static IIO_DEVICE_ATTR(out_voltage0_freq_start, S_IRUGO | S_IWUSR, +			ad5933_show_frequency, +			ad5933_store_frequency, +			AD5933_REG_FREQ_START); + +static IIO_DEVICE_ATTR(out_voltage0_freq_increment, S_IRUGO | S_IWUSR, +			ad5933_show_frequency, +			ad5933_store_frequency, +			AD5933_REG_FREQ_INC); + +static ssize_t ad5933_show(struct device *dev, +					struct device_attribute *attr, +					char *buf) +{ +	struct iio_dev *indio_dev = dev_to_iio_dev(dev); +	struct ad5933_state *st = iio_priv(indio_dev); +	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); +	int ret = 0, len = 0; + +	mutex_lock(&indio_dev->mlock); +	switch ((u32) this_attr->address) { +	case AD5933_OUT_RANGE: +		len = sprintf(buf, "%d\n", +			      st->range_avail[(st->ctrl_hb >> 1) & 0x3]); +		break; +	case AD5933_OUT_RANGE_AVAIL: +		len = sprintf(buf, "%d %d %d %d\n", st->range_avail[0], +			      st->range_avail[3], st->range_avail[2], +			      st->range_avail[1]); +		break; +	case AD5933_OUT_SETTLING_CYCLES: +		len = sprintf(buf, "%d\n", st->settling_cycles); +		break; +	case AD5933_IN_PGA_GAIN: +		len = sprintf(buf, "%s\n", +			      (st->ctrl_hb & AD5933_CTRL_PGA_GAIN_1) ? +			      "1" : "0.2"); +		break; +	case AD5933_IN_PGA_GAIN_AVAIL: +		len = sprintf(buf, "1 0.2\n"); +		break; +	case AD5933_FREQ_POINTS: +		len = sprintf(buf, "%d\n", st->freq_points); +		break; +	default: +		ret = -EINVAL; +	} + +	mutex_unlock(&indio_dev->mlock); +	return ret ? ret : len; +} + +static ssize_t ad5933_store(struct device *dev, +					 struct device_attribute *attr, +					 const char *buf, +					 size_t len) +{ +	struct iio_dev *indio_dev = dev_to_iio_dev(dev); +	struct ad5933_state *st = iio_priv(indio_dev); +	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); +	u16 val; +	int i, ret = 0; +	unsigned short dat; + +	if (this_attr->address != AD5933_IN_PGA_GAIN) { +		ret = kstrtou16(buf, 10, &val); +		if (ret) +			return ret; +	} + +	mutex_lock(&indio_dev->mlock); +	switch ((u32) this_attr->address) { +	case AD5933_OUT_RANGE: +		for (i = 0; i < 4; i++) +			if (val == st->range_avail[i]) { +				st->ctrl_hb &= ~AD5933_CTRL_RANGE(0x3); +				st->ctrl_hb |= AD5933_CTRL_RANGE(i); +				ret = ad5933_cmd(st, 0); +				break; +			} +		ret = -EINVAL; +		break; +	case AD5933_IN_PGA_GAIN: +		if (sysfs_streq(buf, "1")) { +			st->ctrl_hb |= AD5933_CTRL_PGA_GAIN_1; +		} else if (sysfs_streq(buf, "0.2")) { +			st->ctrl_hb &= ~AD5933_CTRL_PGA_GAIN_1; +		} else { +			ret = -EINVAL; +			break; +		} +		ret = ad5933_cmd(st, 0); +		break; +	case AD5933_OUT_SETTLING_CYCLES: +		val = clamp(val, (u16)0, (u16)0x7FF); +		st->settling_cycles = val; + +		/* 2x, 4x handling, see datasheet */ +		if (val > 511) +			val = (val >> 1) | (1 << 9); +		else if (val > 1022) +			val = (val >> 2) | (3 << 9); + +		dat = cpu_to_be16(val); +		ret = ad5933_i2c_write(st->client, +				AD5933_REG_SETTLING_CYCLES, 2, (u8 *)&dat); +		break; +	case AD5933_FREQ_POINTS: +		val = clamp(val, (u16)0, (u16)511); +		st->freq_points = val; + +		dat = cpu_to_be16(val); +		ret = ad5933_i2c_write(st->client, AD5933_REG_INC_NUM, 2, +				       (u8 *)&dat); +		break; +	default: +		ret = -EINVAL; +	} + +	mutex_unlock(&indio_dev->mlock); +	return ret ? ret : len; +} + +static IIO_DEVICE_ATTR(out_voltage0_scale, S_IRUGO | S_IWUSR, +			ad5933_show, +			ad5933_store, +			AD5933_OUT_RANGE); + +static IIO_DEVICE_ATTR(out_voltage0_scale_available, S_IRUGO, +			ad5933_show, +			NULL, +			AD5933_OUT_RANGE_AVAIL); + +static IIO_DEVICE_ATTR(in_voltage0_scale, S_IRUGO | S_IWUSR, +			ad5933_show, +			ad5933_store, +			AD5933_IN_PGA_GAIN); + +static IIO_DEVICE_ATTR(in_voltage0_scale_available, S_IRUGO, +			ad5933_show, +			NULL, +			AD5933_IN_PGA_GAIN_AVAIL); + +static IIO_DEVICE_ATTR(out_voltage0_freq_points, S_IRUGO | S_IWUSR, +			ad5933_show, +			ad5933_store, +			AD5933_FREQ_POINTS); + +static IIO_DEVICE_ATTR(out_voltage0_settling_cycles, S_IRUGO | S_IWUSR, +			ad5933_show, +			ad5933_store, +			AD5933_OUT_SETTLING_CYCLES); + +/* note: + * ideally we would handle the scale attributes via the iio_info + * (read|write)_raw methods, however this part is a untypical since we + * don't create dedicated sysfs channel attributes for out0 and in0. + */ +static struct attribute *ad5933_attributes[] = { +	&iio_dev_attr_out_voltage0_scale.dev_attr.attr, +	&iio_dev_attr_out_voltage0_scale_available.dev_attr.attr, +	&iio_dev_attr_out_voltage0_freq_start.dev_attr.attr, +	&iio_dev_attr_out_voltage0_freq_increment.dev_attr.attr, +	&iio_dev_attr_out_voltage0_freq_points.dev_attr.attr, +	&iio_dev_attr_out_voltage0_settling_cycles.dev_attr.attr, +	&iio_dev_attr_in_voltage0_scale.dev_attr.attr, +	&iio_dev_attr_in_voltage0_scale_available.dev_attr.attr, +	NULL +}; + +static const struct attribute_group ad5933_attribute_group = { +	.attrs = ad5933_attributes, +}; + +static int ad5933_read_raw(struct iio_dev *indio_dev, +			   struct iio_chan_spec const *chan, +			   int *val, +			   int *val2, +			   long m) +{ +	struct ad5933_state *st = iio_priv(indio_dev); +	unsigned short dat; +	int ret = -EINVAL; + +	mutex_lock(&indio_dev->mlock); +	switch (m) { +	case IIO_CHAN_INFO_RAW: +	case IIO_CHAN_INFO_PROCESSED: +		if (iio_buffer_enabled(indio_dev)) { +			ret = -EBUSY; +			goto out; +		} +		ret = ad5933_cmd(st, AD5933_CTRL_MEASURE_TEMP); +		if (ret < 0) +			goto out; +		ret = ad5933_wait_busy(st, AD5933_STAT_TEMP_VALID); +		if (ret < 0) +			goto out; + +		ret = ad5933_i2c_read(st->client, +				AD5933_REG_TEMP_DATA, 2, +				(u8 *)&dat); +		if (ret < 0) +			goto out; +		mutex_unlock(&indio_dev->mlock); +		ret = be16_to_cpu(dat); +		/* Temp in Milli degrees Celsius */ +		if (ret < 8192) +			*val = ret * 1000 / 32; +		else +			*val = (ret - 16384) * 1000 / 32; + +		return IIO_VAL_INT; +	} + +out: +	mutex_unlock(&indio_dev->mlock); +	return ret; +} + +static const struct iio_info ad5933_info = { +	.read_raw = &ad5933_read_raw, +	.attrs = &ad5933_attribute_group, +	.driver_module = THIS_MODULE, +}; + +static int ad5933_ring_preenable(struct iio_dev *indio_dev) +{ +	struct ad5933_state *st = iio_priv(indio_dev); +	int ret; + +	if (bitmap_empty(indio_dev->active_scan_mask, indio_dev->masklength)) +		return -EINVAL; + +	ret = ad5933_reset(st); +	if (ret < 0) +		return ret; + +	ret = ad5933_cmd(st, AD5933_CTRL_STANDBY); +	if (ret < 0) +		return ret; + +	ret = ad5933_cmd(st, AD5933_CTRL_INIT_START_FREQ); +	if (ret < 0) +		return ret; + +	st->state = AD5933_CTRL_INIT_START_FREQ; + +	return 0; +} + +static int ad5933_ring_postenable(struct iio_dev *indio_dev) +{ +	struct ad5933_state *st = iio_priv(indio_dev); + +	/* AD5933_CTRL_INIT_START_FREQ: +	 * High Q complex circuits require a long time to reach steady state. +	 * To facilitate the measurement of such impedances, this mode allows +	 * the user full control of the settling time requirement before +	 * entering start frequency sweep mode where the impedance measurement +	 * takes place. In this mode the impedance is excited with the +	 * programmed start frequency (ad5933_ring_preenable), +	 * but no measurement takes place. +	 */ + +	schedule_delayed_work(&st->work, +			      msecs_to_jiffies(AD5933_INIT_EXCITATION_TIME_ms)); +	return 0; +} + +static int ad5933_ring_postdisable(struct iio_dev *indio_dev) +{ +	struct ad5933_state *st = iio_priv(indio_dev); + +	cancel_delayed_work_sync(&st->work); +	return ad5933_cmd(st, AD5933_CTRL_POWER_DOWN); +} + +static const struct iio_buffer_setup_ops ad5933_ring_setup_ops = { +	.preenable = &ad5933_ring_preenable, +	.postenable = &ad5933_ring_postenable, +	.postdisable = &ad5933_ring_postdisable, +}; + +static int ad5933_register_ring_funcs_and_init(struct iio_dev *indio_dev) +{ +	struct iio_buffer *buffer; + +	buffer = iio_kfifo_allocate(indio_dev); +	if (!buffer) +		return -ENOMEM; + +	iio_device_attach_buffer(indio_dev, buffer); + +	/* Ring buffer functions - here trigger setup related */ +	indio_dev->setup_ops = &ad5933_ring_setup_ops; + +	indio_dev->modes |= INDIO_BUFFER_HARDWARE; + +	return 0; +} + +static void ad5933_work(struct work_struct *work) +{ +	struct ad5933_state *st = container_of(work, +		struct ad5933_state, work.work); +	struct iio_dev *indio_dev = i2c_get_clientdata(st->client); +	signed short buf[2]; +	unsigned char status; + +	mutex_lock(&indio_dev->mlock); +	if (st->state == AD5933_CTRL_INIT_START_FREQ) { +		/* start sweep */ +		ad5933_cmd(st, AD5933_CTRL_START_SWEEP); +		st->state = AD5933_CTRL_START_SWEEP; +		schedule_delayed_work(&st->work, st->poll_time_jiffies); +		mutex_unlock(&indio_dev->mlock); +		return; +	} + +	ad5933_i2c_read(st->client, AD5933_REG_STATUS, 1, &status); + +	if (status & AD5933_STAT_DATA_VALID) { +		int scan_count = bitmap_weight(indio_dev->active_scan_mask, +					       indio_dev->masklength); +		ad5933_i2c_read(st->client, +				test_bit(1, indio_dev->active_scan_mask) ? +				AD5933_REG_REAL_DATA : AD5933_REG_IMAG_DATA, +				scan_count * 2, (u8 *)buf); + +		if (scan_count == 2) { +			buf[0] = be16_to_cpu(buf[0]); +			buf[1] = be16_to_cpu(buf[1]); +		} else { +			buf[0] = be16_to_cpu(buf[0]); +		} +		iio_push_to_buffers(indio_dev, buf); +	} else { +		/* no data available - try again later */ +		schedule_delayed_work(&st->work, st->poll_time_jiffies); +		mutex_unlock(&indio_dev->mlock); +		return; +	} + +	if (status & AD5933_STAT_SWEEP_DONE) { +		/* last sample received - power down do nothing until +		 * the ring enable is toggled */ +		ad5933_cmd(st, AD5933_CTRL_POWER_DOWN); +	} else { +		/* we just received a valid datum, move on to the next */ +		ad5933_cmd(st, AD5933_CTRL_INC_FREQ); +		schedule_delayed_work(&st->work, st->poll_time_jiffies); +	} + +	mutex_unlock(&indio_dev->mlock); +} + +static int ad5933_probe(struct i2c_client *client, +				   const struct i2c_device_id *id) +{ +	int ret, voltage_uv = 0; +	struct ad5933_platform_data *pdata = client->dev.platform_data; +	struct ad5933_state *st; +	struct iio_dev *indio_dev; + +	indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*st)); +	if (indio_dev == NULL) +		return -ENOMEM; + +	st = iio_priv(indio_dev); +	i2c_set_clientdata(client, indio_dev); +	st->client = client; + +	if (!pdata) +		st->pdata = &ad5933_default_pdata; +	else +		st->pdata = pdata; + +	st->reg = devm_regulator_get(&client->dev, "vcc"); +	if (!IS_ERR(st->reg)) { +		ret = regulator_enable(st->reg); +		if (ret) +			return ret; +		voltage_uv = regulator_get_voltage(st->reg); +	} + +	if (voltage_uv) +		st->vref_mv = voltage_uv / 1000; +	else +		st->vref_mv = st->pdata->vref_mv; + +	if (st->pdata->ext_clk_Hz) { +		st->mclk_hz = st->pdata->ext_clk_Hz; +		st->ctrl_lb = AD5933_CTRL_EXT_SYSCLK; +	} else { +		st->mclk_hz = AD5933_INT_OSC_FREQ_Hz; +		st->ctrl_lb = AD5933_CTRL_INT_SYSCLK; +	} + +	ad5933_calc_out_ranges(st); +	INIT_DELAYED_WORK(&st->work, ad5933_work); +	st->poll_time_jiffies = msecs_to_jiffies(AD5933_POLL_TIME_ms); + +	indio_dev->dev.parent = &client->dev; +	indio_dev->info = &ad5933_info; +	indio_dev->name = id->name; +	indio_dev->modes = INDIO_DIRECT_MODE; +	indio_dev->channels = ad5933_channels; +	indio_dev->num_channels = 1; /* only register temp0_input */ + +	ret = ad5933_register_ring_funcs_and_init(indio_dev); +	if (ret) +		goto error_disable_reg; + +	/* skip temp0_input, register in0_(real|imag)_raw */ +	ret = iio_buffer_register(indio_dev, &ad5933_channels[1], 2); +	if (ret) +		goto error_unreg_ring; + +	/* enable both REAL and IMAG channels by default */ +	iio_scan_mask_set(indio_dev, indio_dev->buffer, 0); +	iio_scan_mask_set(indio_dev, indio_dev->buffer, 1); + +	ret = ad5933_setup(st); +	if (ret) +		goto error_uninitialize_ring; + +	ret = iio_device_register(indio_dev); +	if (ret) +		goto error_uninitialize_ring; + +	return 0; + +error_uninitialize_ring: +	iio_buffer_unregister(indio_dev); +error_unreg_ring: +	iio_kfifo_free(indio_dev->buffer); +error_disable_reg: +	if (!IS_ERR(st->reg)) +		regulator_disable(st->reg); + +	return ret; +} + +static int ad5933_remove(struct i2c_client *client) +{ +	struct iio_dev *indio_dev = i2c_get_clientdata(client); +	struct ad5933_state *st = iio_priv(indio_dev); + +	iio_device_unregister(indio_dev); +	iio_buffer_unregister(indio_dev); +	iio_kfifo_free(indio_dev->buffer); +	if (!IS_ERR(st->reg)) +		regulator_disable(st->reg); + +	return 0; +} + +static const struct i2c_device_id ad5933_id[] = { +	{ "ad5933", 0 }, +	{ "ad5934", 0 }, +	{} +}; + +MODULE_DEVICE_TABLE(i2c, ad5933_id); + +static struct i2c_driver ad5933_driver = { +	.driver = { +		.name = "ad5933", +	}, +	.probe = ad5933_probe, +	.remove = ad5933_remove, +	.id_table = ad5933_id, +}; +module_i2c_driver(ad5933_driver); + +MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>"); +MODULE_DESCRIPTION("Analog Devices AD5933 Impedance Conv. Network Analyzer"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/iio/impedance-analyzer/ad5933.h b/drivers/staging/iio/impedance-analyzer/ad5933.h new file mode 100644 index 00000000000..b140e42d67c --- /dev/null +++ b/drivers/staging/iio/impedance-analyzer/ad5933.h @@ -0,0 +1,28 @@ +/* + * AD5933 AD5934 Impedance Converter, Network Analyzer + * + * Copyright 2011 Analog Devices Inc. + * + * Licensed under the GPL-2. + */ + +#ifndef IIO_ADC_AD5933_H_ +#define IIO_ADC_AD5933_H_ + +/* + * TODO: struct ad5933_platform_data needs to go into include/linux/iio + */ + +/** + * struct ad5933_platform_data - platform specific data + * @ext_clk_Hz:		the external clock frequency in Hz, if not set + *			the driver uses the internal clock (16.776 MHz) + * @vref_mv:		the external reference voltage in millivolt + */ + +struct ad5933_platform_data { +	unsigned long			ext_clk_Hz; +	unsigned short			vref_mv; +}; + +#endif /* IIO_ADC_AD5933_H_ */ diff --git a/drivers/staging/iio/imu/Kconfig b/drivers/staging/iio/imu/Kconfig deleted file mode 100644 index 31a6233a206..00000000000 --- a/drivers/staging/iio/imu/Kconfig +++ /dev/null @@ -1,31 +0,0 @@ -# -# IIO imu drivers configuration -# -comment "Inertial measurement units" - -config ADIS16300 -	tristate "Analog Devices ADIS16300 IMU SPI driver" -	depends on SPI -	select IIO_SW_RING if IIO_RING_BUFFER -	select IIO_TRIGGER if IIO_RING_BUFFER -	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 if IIO_RING_BUFFER -	select IIO_TRIGGER if IIO_RING_BUFFER -	help -	  Say yes here to build support for Analog Devices adis16400/5 triaxial -	  inertial sensor with Magnetometer. diff --git a/drivers/staging/iio/imu/Makefile b/drivers/staging/iio/imu/Makefile deleted file mode 100644 index f3b450b6611..00000000000 --- a/drivers/staging/iio/imu/Makefile +++ /dev/null @@ -1,15 +0,0 @@ -# -# 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 diff --git a/drivers/staging/iio/imu/adis16300.h b/drivers/staging/iio/imu/adis16300.h deleted file mode 100644 index 1f25d68064a..00000000000 --- a/drivers/staging/iio/imu/adis16300.h +++ /dev/null @@ -1,184 +0,0 @@ -#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 - * @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; -	s64				last_timestamp; -	struct iio_dev			*indio_dev; -	struct iio_trigger		*trig; -	u8				*tx; -	u8				*rx; -	struct mutex			buf_lock; -}; - -int adis16300_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! - */ - -#define ADIS16300_SCAN_SUPPLY	0 -#define ADIS16300_SCAN_GYRO_X	1 -#define ADIS16300_SCAN_ACC_X	2 -#define ADIS16300_SCAN_ACC_Y	3 -#define ADIS16300_SCAN_ACC_Z	4 -#define ADIS16300_SCAN_TEMP	5 -#define ADIS16300_SCAN_ADC_0	6 -#define ADIS16300_SCAN_INCLI_X	7 -#define ADIS16300_SCAN_INCLI_Y	8 - -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 deleted file mode 100644 index 7ad13f4d3d7..00000000000 --- a/drivers/staging/iio/imu/adis16300_core.c +++ /dev/null @@ -1,756 +0,0 @@ -/* - * 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/slab.h> -#include <linux/sysfs.h> -#include <linux/list.h> - -#include "../iio.h" -#include "../sysfs.h" -#include "../ring_generic.h" -#include "../accel/accel.h" -#include "../accel/inclinometer.h" -#include "../gyro/gyro.h" -#include "../adc/adc.h" - -#include "adis16300.h" - -#define DRIVER_NAME		"adis16300" - -static int adis16300_check_status(struct device *dev); - -/** - * 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, -			.delay_usecs = 75, -		}, { -			.tx_buf = st->tx + 2, -			.bits_per_word = 8, -			.len = 2, -			.cs_change = 1, -			.delay_usecs = 75, -		}, -	}; - -	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 = 1, -			.delay_usecs = 75, -		}, { -			.rx_buf = st->rx, -			.bits_per_word = 8, -			.len = 2, -			.cs_change = 1, -			.delay_usecs = 75, -		}, -	}; - -	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; -} - -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_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 & 0x3FFF); -} - -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 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; -} - -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; -} - -/* 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; -} - -static int adis16300_self_test(struct device *dev) -{ -	int ret; -	ret = adis16300_spi_write_reg_16(dev, -			ADIS16300_MSC_CTRL, -			ADIS16300_MSC_CTRL_MEM_TEST); -	if (ret) { -		dev_err(dev, "problem starting self test"); -		goto err_ret; -	} - -	adis16300_check_status(dev); - -err_ret: -	return ret; -} - -static 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 */ -	ret = adis16300_self_test(dev); -	if (ret) { -		dev_err(dev, "self test failure"); -		goto err_ret; -	} - -	/* 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_GYRO_X_CALIBBIAS(S_IWUSR | S_IRUGO, -		adis16300_read_12bit_signed, -		adis16300_write_16bit, -		ADIS16300_XGYRO_OFF); - -static IIO_DEV_ATTR_ACCEL_X_CALIBBIAS(S_IWUSR | S_IRUGO, -		adis16300_read_12bit_signed, -		adis16300_write_16bit, -		ADIS16300_XACCL_OFF); - -static IIO_DEV_ATTR_ACCEL_Y_CALIBBIAS(S_IWUSR | S_IRUGO, -		adis16300_read_12bit_signed, -		adis16300_write_16bit, -		ADIS16300_YACCL_OFF); - -static IIO_DEV_ATTR_ACCEL_Z_CALIBBIAS(S_IWUSR | S_IRUGO, -		adis16300_read_12bit_signed, -		adis16300_write_16bit, -		ADIS16300_ZACCL_OFF); - -static IIO_DEV_ATTR_IN_NAMED_RAW(0, supply, adis16300_read_14bit_unsigned, -			   ADIS16300_SUPPLY_OUT); -static IIO_CONST_ATTR_IN_NAMED_SCALE(0, supply, "0.00242"); - -static IIO_DEV_ATTR_GYRO_X(adis16300_read_14bit_signed, -		ADIS16300_XGYRO_OUT); -static IIO_CONST_ATTR_GYRO_SCALE("0.000872664"); - -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.00588399"); - -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.00076794487"); - -static IIO_DEV_ATTR_TEMP_RAW(adis16300_read_12bit_unsigned); -static IIO_CONST_ATTR_TEMP_OFFSET("198.16"); -static IIO_CONST_ATTR_TEMP_SCALE("0.14"); - -static IIO_DEV_ATTR_IN_RAW(1, adis16300_read_12bit_unsigned, -		ADIS16300_AUX_ADC); -static IIO_CONST_ATTR(in1_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_SAMP_FREQ_AVAIL("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_gyro_x_calibbias.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_in0_supply_raw.dev_attr.attr, -	&iio_const_attr_in0_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_in1_raw.dev_attr.attr, -	&iio_const_attr_in1_scale.dev_attr.attr, -	&iio_dev_attr_sampling_frequency.dev_attr.attr, -	&iio_const_attr_sampling_frequency_available.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 = iio_ring_buffer_register(st->indio_dev->ring, 0); -	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, -				"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: -	adis16300_remove_trigger(st->indio_dev); -error_unregister_line: -	if (spi->irq) -		iio_unregister_interrupt_line(st->indio_dev, 0); -error_uninitialize_ring: -	iio_ring_buffer_unregister(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; -} - -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) -		iio_unregister_interrupt_line(indio_dev, 0); - -	iio_ring_buffer_unregister(indio_dev->ring); -	iio_device_unregister(indio_dev); -	adis16300_unconfigure_ring(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 deleted file mode 100644 index 114fdf4fd47..00000000000 --- a/drivers/staging/iio/imu/adis16300_ring.c +++ /dev/null @@ -1,238 +0,0 @@ -#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/slab.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" - -static IIO_SCAN_EL_C(in0_supply, ADIS16300_SCAN_SUPPLY, -		     ADIS16300_SUPPLY_OUT, NULL); -static IIO_CONST_ATTR_SCAN_EL_TYPE(in0_supply, u, 12, 16); -static IIO_SCAN_EL_C(gyro_x, ADIS16300_SCAN_GYRO_X, ADIS16300_XGYRO_OUT, NULL); -static IIO_CONST_ATTR_SCAN_EL_TYPE(gyro, s, 14, 16); - -static IIO_SCAN_EL_C(accel_x, ADIS16300_SCAN_ACC_X, ADIS16300_XACCL_OUT, NULL); -static IIO_SCAN_EL_C(accel_y, ADIS16300_SCAN_ACC_Y, ADIS16300_YACCL_OUT, NULL); -static IIO_SCAN_EL_C(accel_z, ADIS16300_SCAN_ACC_Z, ADIS16300_ZACCL_OUT, NULL); -static IIO_CONST_ATTR_SCAN_EL_TYPE(accel, s, 14, 16); - -static IIO_SCAN_EL_C(temp, ADIS16300_SCAN_TEMP, ADIS16300_TEMP_OUT, NULL); -static IIO_CONST_ATTR_SCAN_EL_TYPE(temp, s, 12, 16); - -static IIO_SCAN_EL_C(in1, ADIS16300_SCAN_ADC_0, ADIS16300_AUX_ADC, NULL); -static IIO_CONST_ATTR_SCAN_EL_TYPE(in1, u, 12, 16); - -static IIO_SCAN_EL_C(incli_x, ADIS16300_SCAN_INCLI_X, -		     ADIS16300_XINCLI_OUT, NULL); -static IIO_SCAN_EL_C(incli_y, ADIS16300_SCAN_INCLI_Y, -		     ADIS16300_YINCLI_OUT, NULL); -static IIO_CONST_ATTR_SCAN_EL_TYPE(incli, s, 13, 16); - -static IIO_SCAN_EL_TIMESTAMP(9); -static IIO_CONST_ATTR_SCAN_EL_TYPE(timestamp, s, 64, 64); - -static struct attribute *adis16300_scan_el_attrs[] = { -	&iio_scan_el_in0_supply.dev_attr.attr, -	&iio_const_attr_in0_supply_index.dev_attr.attr, -	&iio_const_attr_in0_supply_type.dev_attr.attr, -	&iio_scan_el_gyro_x.dev_attr.attr, -	&iio_const_attr_gyro_x_index.dev_attr.attr, -	&iio_const_attr_gyro_type.dev_attr.attr, -	&iio_scan_el_temp.dev_attr.attr, -	&iio_const_attr_temp_index.dev_attr.attr, -	&iio_const_attr_temp_type.dev_attr.attr, -	&iio_scan_el_accel_x.dev_attr.attr, -	&iio_const_attr_accel_x_index.dev_attr.attr, -	&iio_scan_el_accel_y.dev_attr.attr, -	&iio_const_attr_accel_y_index.dev_attr.attr, -	&iio_scan_el_accel_z.dev_attr.attr, -	&iio_const_attr_accel_z_index.dev_attr.attr, -	&iio_const_attr_accel_type.dev_attr.attr, -	&iio_scan_el_incli_x.dev_attr.attr, -	&iio_const_attr_incli_x_index.dev_attr.attr, -	&iio_scan_el_incli_y.dev_attr.attr, -	&iio_const_attr_incli_y_index.dev_attr.attr, -	&iio_const_attr_incli_type.dev_attr.attr, -	&iio_scan_el_in1.dev_attr.attr, -	&iio_const_attr_in1_index.dev_attr.attr, -	&iio_const_attr_in1_type.dev_attr.attr, -	&iio_scan_el_timestamp.dev_attr.attr, -	&iio_const_attr_timestamp_index.dev_attr.attr, -	&iio_const_attr_timestamp_type.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, s64 time) -{ -	struct adis16300_state *st = iio_dev_get_devdata(indio_dev); -	st->last_timestamp = time; -	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. -	 */ -} - -/** - * 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) - **/ -static 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 = ADIS16300_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 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); -	struct iio_ring_buffer *ring = st->indio_dev->ring; - -	int i = 0; -	s16 *data; -	size_t datasize = ring->access.get_bytes_per_datum(ring); - -	data = kmalloc(datasize , GFP_KERNEL); -	if (data == NULL) { -		dev_err(&st->us->dev, "memory alloc failed in ring bh"); -		return; -	} - -	if (ring->scan_count) -		if (adis16300_spi_read_burst(&st->indio_dev->dev, st->rx) >= 0) -			for (; i < ring->scan_count; i++) -				data[i] = be16_to_cpup( -					(__be16 *)&(st->rx[i*2])); - -	/* Guaranteed to be aligned with 8 byte boundary */ -	if (ring->scan_timestamp) -		*((s64 *)(data + ((i + 3)/4)*4)) = st->last_timestamp; - -	ring->access.store_to(ring, -			(u8 *)data, -			st->last_timestamp); - -	iio_trigger_notify_done(st->indio_dev->trig); -	kfree(data); - -	return; -} - -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); - -	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->bpe = 2; -	ring->scan_el_attrs = &adis16300_scan_el_group; -	ring->scan_timestamp = true; -	ring->preenable = &iio_sw_ring_preenable; -	ring->postenable = &iio_triggered_ring_postenable; -	ring->predisable = &iio_triggered_ring_predisable; -	ring->owner = THIS_MODULE; - -	/* Set default scan mode */ -	iio_scan_mask_set(ring, iio_scan_el_in0_supply.number); -	iio_scan_mask_set(ring, iio_scan_el_gyro_x.number); -	iio_scan_mask_set(ring, iio_scan_el_accel_x.number); -	iio_scan_mask_set(ring, iio_scan_el_accel_y.number); -	iio_scan_mask_set(ring, iio_scan_el_accel_z.number); -	iio_scan_mask_set(ring, iio_scan_el_temp.number); -	iio_scan_mask_set(ring, iio_scan_el_in1.number); -	iio_scan_mask_set(ring, iio_scan_el_incli_x.number); -	iio_scan_mask_set(ring, iio_scan_el_incli_y.number); - -	ret = iio_alloc_pollfunc(indio_dev, NULL, &adis16300_poll_func_th); -	if (ret) -		goto error_iio_sw_rb_free; - -	indio_dev->modes |= INDIO_RING_TRIGGERED; -	return 0; - -error_iio_sw_rb_free: -	iio_sw_rb_free(indio_dev->ring); -	return ret; -} - diff --git a/drivers/staging/iio/imu/adis16300_trigger.c b/drivers/staging/iio/imu/adis16300_trigger.c deleted file mode 100644 index d6677b64edb..00000000000 --- a/drivers/staging/iio/imu/adis16300_trigger.c +++ /dev/null @@ -1,125 +0,0 @@ -#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; - -	iio_trigger_poll(trig, timestamp); - -	return IRQ_HANDLED; -} - -IIO_EVENT_SH(data_rdy_trig, &adis16300_data_rdy_trig_poll); - -static IIO_TRIGGER_NAME_ATTR; - -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 = kasprintf(GFP_KERNEL, -				   "adis16300-dev%d", -				   indio_dev->id); -	if (!st->trig->name) { -		ret = -ENOMEM; -		goto error_free_trig; -	} -	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 deleted file mode 100644 index b00001e3edd..00000000000 --- a/drivers/staging/iio/imu/adis16350.h +++ /dev/null @@ -1,177 +0,0 @@ -#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 - * @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; -	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 - -#define ADIS16350_SCAN_SUPPLY	0 -#define ADIS16350_SCAN_GYRO_X	1 -#define ADIS16350_SCAN_GYRO_Y	2 -#define ADIS16350_SCAN_GYRO_Z	3 -#define ADIS16350_SCAN_ACC_X	4 -#define ADIS16350_SCAN_ACC_Y	5 -#define ADIS16350_SCAN_ACC_Z	6 -#define ADIS16350_SCAN_TEMP_X	7 -#define ADIS16350_SCAN_TEMP_Y	8 -#define ADIS16350_SCAN_TEMP_Z	9 -#define ADIS16350_SCAN_ADC_0	10 - -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); - -#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 inline int adis16350_configure_ring(struct iio_dev *indio_dev) -{ -	return 0; -} - -static inline void adis16350_unconfigure_ring(struct iio_dev *indio_dev) -{ -} -#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 deleted file mode 100644 index 97c1ec8594c..00000000000 --- a/drivers/staging/iio/imu/adis16350_core.c +++ /dev/null @@ -1,756 +0,0 @@ -/* - * 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/slab.h> -#include <linux/sysfs.h> -#include <linux/list.h> - -#include "../iio.h" -#include "../sysfs.h" -#include "../ring_generic.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 = 35, -		}, { -			.tx_buf = st->tx + 2, -			.bits_per_word = 8, -			.len = 2, -			.cs_change = 1, -			.delay_usecs = 35, -		}, -	}; - -	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 = 35, -		}, { -			.rx_buf = st->rx, -			.bits_per_word = 8, -			.len = 2, -			.cs_change = 1, -			.delay_usecs = 35, -		}, -	}; - -	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_GYRO_X_CALIBBIAS(S_IWUSR | S_IRUGO, -		adis16350_read_12bit_signed, -		adis16350_write_16bit, -		ADIS16350_XGYRO_OFF); - -static IIO_DEV_ATTR_GYRO_Y_CALIBBIAS(S_IWUSR | S_IRUGO, -		adis16350_read_12bit_signed, -		adis16350_write_16bit, -		ADIS16350_YGYRO_OFF); - -static IIO_DEV_ATTR_GYRO_Z_CALIBBIAS(S_IWUSR | S_IRUGO, -		adis16350_read_12bit_signed, -		adis16350_write_16bit, -		ADIS16350_ZGYRO_OFF); - -static IIO_DEV_ATTR_ACCEL_X_CALIBBIAS(S_IWUSR | S_IRUGO, -		adis16350_read_12bit_signed, -		adis16350_write_16bit, -		ADIS16350_XACCL_OFF); - -static IIO_DEV_ATTR_ACCEL_Y_CALIBBIAS(S_IWUSR | S_IRUGO, -		adis16350_read_12bit_signed, -		adis16350_write_16bit, -		ADIS16350_YACCL_OFF); - -static IIO_DEV_ATTR_ACCEL_Z_CALIBBIAS(S_IWUSR | S_IRUGO, -		adis16350_read_12bit_signed, -		adis16350_write_16bit, -		ADIS16350_ZACCL_OFF); - -static IIO_DEV_ATTR_IN_NAMED_RAW(0, supply, adis16350_read_12bit_unsigned, -		ADIS16350_SUPPLY_OUT); -static IIO_CONST_ATTR_IN_NAMED_SCALE(0, supply, "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.00127862821"); - -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.0247323713"); - -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.14534"); -static IIO_CONST_ATTR_TEMP_OFFSET("198.16"); - -static IIO_DEV_ATTR_IN_RAW(1, adis16350_read_12bit_unsigned, -		ADIS16350_AUX_ADC); -static IIO_CONST_ATTR(in1_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_SAMP_FREQ_AVAIL("409 546 819 1638"); - -static IIO_CONST_ATTR_NAME("adis16350"); - -static struct attribute *adis16350_attributes[] = { -	&iio_dev_attr_gyro_x_calibbias.dev_attr.attr, -	&iio_dev_attr_gyro_y_calibbias.dev_attr.attr, -	&iio_dev_attr_gyro_z_calibbias.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_in0_supply_raw.dev_attr.attr, -	&iio_const_attr_in0_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_in1_raw.dev_attr.attr, -	&iio_const_attr_in1_scale.dev_attr.attr, -	&iio_dev_attr_sampling_frequency.dev_attr.attr, -	&iio_const_attr_sampling_frequency_available.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 = iio_ring_buffer_register(st->indio_dev->ring, 0); -	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: -	iio_ring_buffer_unregister(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); - -	iio_ring_buffer_unregister(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 deleted file mode 100644 index 56b70cfb582..00000000000 --- a/drivers/staging/iio/imu/adis16350_ring.c +++ /dev/null @@ -1,236 +0,0 @@ -#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/slab.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" - -static IIO_SCAN_EL_C(in0_supply, ADIS16350_SCAN_SUPPLY, -		ADIS16350_SUPPLY_OUT, NULL); -static IIO_CONST_ATTR_SCAN_EL_TYPE(in0_supply, u, 12, 16); - -static IIO_SCAN_EL_C(gyro_x, ADIS16350_SCAN_GYRO_X, ADIS16350_XGYRO_OUT, NULL); -static IIO_SCAN_EL_C(gyro_y, ADIS16350_SCAN_GYRO_Y, ADIS16350_YGYRO_OUT, NULL); -static IIO_SCAN_EL_C(gyro_z, ADIS16350_SCAN_GYRO_Z, ADIS16350_ZGYRO_OUT, NULL); -static IIO_CONST_ATTR_SCAN_EL_TYPE(gyro, s, 14, 16); - -static IIO_SCAN_EL_C(accel_x, ADIS16350_SCAN_ACC_X, ADIS16350_XACCL_OUT, NULL); -static IIO_SCAN_EL_C(accel_y, ADIS16350_SCAN_ACC_Y, ADIS16350_YACCL_OUT, NULL); -static IIO_SCAN_EL_C(accel_z, ADIS16350_SCAN_ACC_Z, ADIS16350_ZACCL_OUT, NULL); -static IIO_CONST_ATTR_SCAN_EL_TYPE(accel, s, 14, 16); - -static IIO_SCAN_EL_C(temp_x, ADIS16350_SCAN_TEMP_X, ADIS16350_XTEMP_OUT, NULL); -static IIO_SCAN_EL_C(temp_y, ADIS16350_SCAN_TEMP_Y, ADIS16350_YTEMP_OUT, NULL); -static IIO_SCAN_EL_C(temp_z, ADIS16350_SCAN_TEMP_Z, ADIS16350_ZTEMP_OUT, NULL); -static IIO_CONST_ATTR_SCAN_EL_TYPE(temp, s, 12, 16); - -static IIO_SCAN_EL_C(in1, ADIS16350_SCAN_ADC_0, ADIS16350_AUX_ADC, NULL); -static IIO_CONST_ATTR_SCAN_EL_TYPE(in1, u, 12, 16); - -static IIO_SCAN_EL_TIMESTAMP(11); -static IIO_CONST_ATTR_SCAN_EL_TYPE(timestamp, s, 64, 64); - -static struct attribute *adis16350_scan_el_attrs[] = { -	&iio_scan_el_in0_supply.dev_attr.attr, -	&iio_const_attr_in0_supply_index.dev_attr.attr, -	&iio_const_attr_in0_supply_type.dev_attr.attr, -	&iio_scan_el_gyro_x.dev_attr.attr, -	&iio_const_attr_gyro_x_index.dev_attr.attr, -	&iio_scan_el_gyro_y.dev_attr.attr, -	&iio_const_attr_gyro_y_index.dev_attr.attr, -	&iio_scan_el_gyro_z.dev_attr.attr, -	&iio_const_attr_gyro_z_index.dev_attr.attr, -	&iio_const_attr_gyro_type.dev_attr.attr, -	&iio_scan_el_accel_x.dev_attr.attr, -	&iio_const_attr_accel_x_index.dev_attr.attr, -	&iio_scan_el_accel_y.dev_attr.attr, -	&iio_const_attr_accel_y_index.dev_attr.attr, -	&iio_scan_el_accel_z.dev_attr.attr, -	&iio_const_attr_accel_z_index.dev_attr.attr, -	&iio_const_attr_accel_type.dev_attr.attr, -	&iio_scan_el_temp_x.dev_attr.attr, -	&iio_const_attr_temp_x_index.dev_attr.attr, -	&iio_scan_el_temp_y.dev_attr.attr, -	&iio_const_attr_temp_y_index.dev_attr.attr, -	&iio_scan_el_temp_z.dev_attr.attr, -	&iio_const_attr_temp_z_index.dev_attr.attr, -	&iio_const_attr_temp_type.dev_attr.attr, -	&iio_scan_el_in1.dev_attr.attr, -	&iio_const_attr_in1_index.dev_attr.attr, -	&iio_const_attr_in1_type.dev_attr.attr, -	&iio_scan_el_timestamp.dev_attr.attr, -	&iio_const_attr_timestamp_index.dev_attr.attr, -	&iio_const_attr_timestamp_type.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, s64 time) -{ -	struct adis16350_state *st = iio_dev_get_devdata(indio_dev); -	st->last_timestamp = time; -	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); -	struct iio_ring_buffer *ring = st->indio_dev->ring; - -	int i = 0; -	s16 *data; -	size_t datasize = ring->access.get_bytes_per_datum(ring); - -	data = kmalloc(datasize , GFP_KERNEL); -	if (data == NULL) { -		dev_err(&st->us->dev, "memory alloc failed in ring bh"); -		return; -	} - -	if (ring->scan_count) -		if (adis16350_spi_read_burst(&st->indio_dev->dev, st->rx) >= 0) -			for (; i < ring->scan_count; i++) -				data[i] = be16_to_cpup( -					(__be16 *)&(st->rx[i*2])); - -	/* Guaranteed to be aligned with 8 byte boundary */ -	if (ring->scan_timestamp) -		*((s64 *)(data + ((i + 3)/4)*4)) = st->last_timestamp; - -	ring->access.store_to(ring, -			(u8 *)data, -			st->last_timestamp); - -	iio_trigger_notify_done(st->indio_dev->trig); -	kfree(data); - -	return; -} - -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); - -	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->bpe = 2; -	ring->scan_el_attrs = &adis16350_scan_el_group; -	ring->scan_timestamp = true; -	ring->preenable = &iio_sw_ring_preenable; -	ring->postenable = &iio_triggered_ring_postenable; -	ring->predisable = &iio_triggered_ring_predisable; -	ring->owner = THIS_MODULE; - -	/* Set default scan mode */ -	iio_scan_mask_set(ring, iio_scan_el_in0_supply.number); -	iio_scan_mask_set(ring, iio_scan_el_gyro_x.number); -	iio_scan_mask_set(ring, iio_scan_el_gyro_y.number); -	iio_scan_mask_set(ring, iio_scan_el_gyro_z.number); -	iio_scan_mask_set(ring, iio_scan_el_accel_x.number); -	iio_scan_mask_set(ring, iio_scan_el_accel_y.number); -	iio_scan_mask_set(ring, iio_scan_el_accel_z.number); -	iio_scan_mask_set(ring, iio_scan_el_temp_x.number); -	iio_scan_mask_set(ring, iio_scan_el_temp_y.number); -	iio_scan_mask_set(ring, iio_scan_el_temp_z.number); -	iio_scan_mask_set(ring, iio_scan_el_in1.number); - -	ret = iio_alloc_pollfunc(indio_dev, NULL, &adis16350_poll_func_th); -	if (ret) -		goto error_iio_sw_rb_free; - -	indio_dev->modes |= INDIO_RING_TRIGGERED; -	return 0; - -error_iio_sw_rb_free: -	iio_sw_rb_free(indio_dev->ring); -	return ret; -} - diff --git a/drivers/staging/iio/imu/adis16350_trigger.c b/drivers/staging/iio/imu/adis16350_trigger.c deleted file mode 100644 index 739b7ecb2e7..00000000000 --- a/drivers/staging/iio/imu/adis16350_trigger.c +++ /dev/null @@ -1,125 +0,0 @@ -#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; - -	iio_trigger_poll(trig, timestamp); - -	return IRQ_HANDLED; -} - -IIO_EVENT_SH(data_rdy_trig, &adis16350_data_rdy_trig_poll); - -static IIO_TRIGGER_NAME_ATTR; - -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 = kasprintf(GFP_KERNEL, -				   "adis16350-dev%d", -				   indio_dev->id); -	if (!st->trig->name) { -		ret = -ENOMEM; -		goto error_free_trig; -	} -	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 deleted file mode 100644 index 6ff33e1ad8c..00000000000 --- a/drivers/staging/iio/imu/adis16400.h +++ /dev/null @@ -1,208 +0,0 @@ -/* - * 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 - * @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; -	s64				last_timestamp; -	struct iio_dev			*indio_dev; -	struct iio_trigger		*trig; -	u8				*tx; -	u8				*rx; -	struct mutex			buf_lock; -}; - -int adis16400_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! - */ - -#define ADIS16400_SCAN_SUPPLY	0 -#define ADIS16400_SCAN_GYRO_X	1 -#define ADIS16400_SCAN_GYRO_Y	2 -#define ADIS16400_SCAN_GYRO_Z	3 -#define ADIS16400_SCAN_ACC_X	4 -#define ADIS16400_SCAN_ACC_Y	5 -#define ADIS16400_SCAN_ACC_Z	6 -#define ADIS16400_SCAN_MAGN_X	7 -#define ADIS16400_SCAN_MAGN_Y	8 -#define ADIS16400_SCAN_MAGN_Z	9 -#define ADIS16400_SCAN_TEMP	10 -#define ADIS16400_SCAN_ADC_0	11 - -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); - -#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) -{ -} - -#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 deleted file mode 100644 index cfb108a1545..00000000000 --- a/drivers/staging/iio/imu/adis16400_core.c +++ /dev/null @@ -1,752 +0,0 @@ -/* - * 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/slab.h> -#include <linux/sysfs.h> -#include <linux/list.h> - -#include "../iio.h" -#include "../sysfs.h" -#include "../ring_generic.h" -#include "../accel/accel.h" -#include "../adc/adc.h" -#include "../gyro/gyro.h" -#include "../magnetometer/magnet.h" - -#include "adis16400.h" - -#define DRIVER_NAME		"adis16400" - -static int adis16400_check_status(struct device *dev); - -/* 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; -} - -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 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; -} - -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; -} - -/* 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; -} - -static 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 */ -	ret = adis16400_self_test(dev); -	if (ret) { -		dev_err(dev, "self test failure"); -		goto err_ret; -	} - -	/* 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; -} - -#define ADIS16400_DEV_ATTR_CALIBBIAS(_channel, _reg)		\ -	IIO_DEV_ATTR_##_channel##_CALIBBIAS(S_IWUSR | S_IRUGO,	\ -			adis16400_read_12bit_signed,		\ -			adis16400_write_16bit,			\ -			_reg) - -static ADIS16400_DEV_ATTR_CALIBBIAS(GYRO_X, ADIS16400_XGYRO_OFF); -static ADIS16400_DEV_ATTR_CALIBBIAS(GYRO_Y, ADIS16400_XGYRO_OFF); -static ADIS16400_DEV_ATTR_CALIBBIAS(GYRO_Z, ADIS16400_XGYRO_OFF); - -static ADIS16400_DEV_ATTR_CALIBBIAS(ACCEL_X, ADIS16400_XACCL_OFF); -static ADIS16400_DEV_ATTR_CALIBBIAS(ACCEL_Y, ADIS16400_XACCL_OFF); -static ADIS16400_DEV_ATTR_CALIBBIAS(ACCEL_Z, ADIS16400_XACCL_OFF); - - -static IIO_DEV_ATTR_IN_NAMED_RAW(0, supply, adis16400_read_14bit_signed, -		ADIS16400_SUPPLY_OUT); -static IIO_CONST_ATTR_IN_NAMED_SCALE(0, supply, "0.002418 V"); - -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.0008726646"); - -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.0326561445"); - -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(1, adis16400_read_12bit_unsigned, -		ADIS16400_AUX_ADC); -static IIO_CONST_ATTR(in1_scale, "0.000806 V"); - -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_SAMP_FREQ_AVAIL("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_gyro_x_calibbias.dev_attr.attr, -	&iio_dev_attr_gyro_y_calibbias.dev_attr.attr, -	&iio_dev_attr_gyro_z_calibbias.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_in0_supply_raw.dev_attr.attr, -	&iio_const_attr_in0_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_in1_raw.dev_attr.attr, -	&iio_const_attr_in1_scale.dev_attr.attr, -	&iio_dev_attr_sampling_frequency.dev_attr.attr, -	&iio_const_attr_sampling_frequency_available.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 = iio_ring_buffer_register(st->indio_dev->ring, 0); -	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) { -		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: -	iio_ring_buffer_unregister(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); - -	iio_ring_buffer_unregister(st->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 deleted file mode 100644 index 33293fba9bc..00000000000 --- a/drivers/staging/iio/imu/adis16400_ring.c +++ /dev/null @@ -1,248 +0,0 @@ -#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/slab.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" - -static IIO_SCAN_EL_C(in0_supply, ADIS16400_SCAN_SUPPLY, -		     ADIS16400_SUPPLY_OUT, NULL); -static IIO_CONST_ATTR_SCAN_EL_TYPE(in0_supply, u, 14, 16); - -static IIO_SCAN_EL_C(gyro_x, ADIS16400_SCAN_GYRO_X, ADIS16400_XGYRO_OUT, NULL); -static IIO_SCAN_EL_C(gyro_y, ADIS16400_SCAN_GYRO_Y, ADIS16400_YGYRO_OUT, NULL); -static IIO_SCAN_EL_C(gyro_z, ADIS16400_SCAN_GYRO_Z, ADIS16400_ZGYRO_OUT, NULL); -static IIO_CONST_ATTR_SCAN_EL_TYPE(gyro, s, 14, 16); - -static IIO_SCAN_EL_C(accel_x, ADIS16400_SCAN_ACC_X, ADIS16400_XACCL_OUT, NULL); -static IIO_SCAN_EL_C(accel_y, ADIS16400_SCAN_ACC_Y, ADIS16400_YACCL_OUT, NULL); -static IIO_SCAN_EL_C(accel_z, ADIS16400_SCAN_ACC_Z, ADIS16400_ZACCL_OUT, NULL); -static IIO_CONST_ATTR_SCAN_EL_TYPE(accel, s, 14, 16); - -static IIO_SCAN_EL_C(magn_x, ADIS16400_SCAN_MAGN_X, ADIS16400_XMAGN_OUT, NULL); -static IIO_SCAN_EL_C(magn_y, ADIS16400_SCAN_MAGN_Y, ADIS16400_YMAGN_OUT, NULL); -static IIO_SCAN_EL_C(magn_z, ADIS16400_SCAN_MAGN_Z, ADIS16400_ZMAGN_OUT, NULL); -static IIO_CONST_ATTR_SCAN_EL_TYPE(magn, s, 14, 16); - -static IIO_SCAN_EL_C(temp, ADIS16400_SCAN_TEMP, ADIS16400_TEMP_OUT, NULL); -static IIO_CONST_ATTR_SCAN_EL_TYPE(temp, s, 12, 16); - -static IIO_SCAN_EL_C(in1, ADIS16400_SCAN_ADC_0, ADIS16400_AUX_ADC, NULL); -static IIO_CONST_ATTR_SCAN_EL_TYPE(in1, u, 12, 16); - -static IIO_SCAN_EL_TIMESTAMP(12); -static IIO_CONST_ATTR_SCAN_EL_TYPE(timestamp, s, 64, 64); - -static struct attribute *adis16400_scan_el_attrs[] = { -	&iio_scan_el_in0_supply.dev_attr.attr, -	&iio_const_attr_in0_supply_index.dev_attr.attr, -	&iio_const_attr_in0_supply_type.dev_attr.attr, -	&iio_scan_el_gyro_x.dev_attr.attr, -	&iio_const_attr_gyro_x_index.dev_attr.attr, -	&iio_scan_el_gyro_y.dev_attr.attr, -	&iio_const_attr_gyro_y_index.dev_attr.attr, -	&iio_scan_el_gyro_z.dev_attr.attr, -	&iio_const_attr_gyro_z_index.dev_attr.attr, -	&iio_const_attr_gyro_type.dev_attr.attr, -	&iio_scan_el_accel_x.dev_attr.attr, -	&iio_const_attr_accel_x_index.dev_attr.attr, -	&iio_scan_el_accel_y.dev_attr.attr, -	&iio_const_attr_accel_y_index.dev_attr.attr, -	&iio_scan_el_accel_z.dev_attr.attr, -	&iio_const_attr_accel_z_index.dev_attr.attr, -	&iio_const_attr_accel_type.dev_attr.attr, -	&iio_scan_el_magn_x.dev_attr.attr, -	&iio_const_attr_magn_x_index.dev_attr.attr, -	&iio_scan_el_magn_y.dev_attr.attr, -	&iio_const_attr_magn_y_index.dev_attr.attr, -	&iio_scan_el_magn_z.dev_attr.attr, -	&iio_const_attr_magn_z_index.dev_attr.attr, -	&iio_const_attr_magn_type.dev_attr.attr, -	&iio_scan_el_temp.dev_attr.attr, -	&iio_const_attr_temp_index.dev_attr.attr, -	&iio_const_attr_temp_type.dev_attr.attr, -	&iio_scan_el_in1.dev_attr.attr, -	&iio_const_attr_in1_index.dev_attr.attr, -	&iio_const_attr_in1_type.dev_attr.attr, -	&iio_scan_el_timestamp.dev_attr.attr, -	&iio_const_attr_timestamp_index.dev_attr.attr, -	&iio_const_attr_timestamp_type.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, s64 time) -{ -	struct adis16400_state *st = iio_dev_get_devdata(indio_dev); -	st->last_timestamp = time; -	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. -	 */ -} - -/** - * 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) - **/ -static 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; -} - -/* 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); -	struct iio_ring_buffer *ring = st->indio_dev->ring; - -	int i = 0; -	s16 *data; -	size_t datasize = ring->access.get_bytes_per_datum(ring); - -	data = kmalloc(datasize , GFP_KERNEL); -	if (data == NULL) { -		dev_err(&st->us->dev, "memory alloc failed in ring bh"); -		return; -	} - -	if (ring->scan_count) -		if (adis16400_spi_read_burst(&st->indio_dev->dev, st->rx) >= 0) -			for (; i < ring->scan_count; i++) -				data[i]	= be16_to_cpup( -					(__be16 *)&(st->rx[i*2])); - -	/* Guaranteed to be aligned with 8 byte boundary */ -	if (ring->scan_timestamp) -		*((s64 *)(data + ((i + 3)/4)*4)) = st->last_timestamp; - -	ring->access.store_to(ring, -			(u8 *) data, -			st->last_timestamp); - -	iio_trigger_notify_done(st->indio_dev->trig); -	kfree(data); - -	return; -} - -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); - -	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->bpe = 2; -	ring->scan_el_attrs = &adis16400_scan_el_group; -	ring->scan_timestamp = true; -	ring->preenable = &iio_sw_ring_preenable; -	ring->postenable = &iio_triggered_ring_postenable; -	ring->predisable = &iio_triggered_ring_predisable; -	ring->owner = THIS_MODULE; - -	/* Set default scan mode */ -	iio_scan_mask_set(ring, iio_scan_el_in0_supply.number); -	iio_scan_mask_set(ring, iio_scan_el_gyro_x.number); -	iio_scan_mask_set(ring, iio_scan_el_gyro_y.number); -	iio_scan_mask_set(ring, iio_scan_el_gyro_z.number); -	iio_scan_mask_set(ring, iio_scan_el_accel_x.number); -	iio_scan_mask_set(ring, iio_scan_el_accel_y.number); -	iio_scan_mask_set(ring, iio_scan_el_accel_z.number); -	iio_scan_mask_set(ring, iio_scan_el_magn_x.number); -	iio_scan_mask_set(ring, iio_scan_el_magn_y.number); -	iio_scan_mask_set(ring, iio_scan_el_magn_z.number); -	iio_scan_mask_set(ring, iio_scan_el_temp.number); -	iio_scan_mask_set(ring, iio_scan_el_in1.number); - -	ret = iio_alloc_pollfunc(indio_dev, NULL, &adis16400_poll_func_th); -	if (ret) -		goto error_iio_sw_rb_free; - -	indio_dev->modes |= INDIO_RING_TRIGGERED; -	return 0; - -error_iio_sw_rb_free: -	iio_sw_rb_free(indio_dev->ring); -	return ret; -} diff --git a/drivers/staging/iio/imu/adis16400_trigger.c b/drivers/staging/iio/imu/adis16400_trigger.c deleted file mode 100644 index 36b5ff5be98..00000000000 --- a/drivers/staging/iio/imu/adis16400_trigger.c +++ /dev/null @@ -1,125 +0,0 @@ -#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; - -	iio_trigger_poll(trig, timestamp); - -	return IRQ_HANDLED; -} - -IIO_EVENT_SH(data_rdy_trig, &adis16400_data_rdy_trig_poll); - -static IIO_TRIGGER_NAME_ATTR; - -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 = kasprintf(GFP_KERNEL, -				   "adis16400-dev%d", -				   indio_dev->id); -	if (!st->trig->name) { -		ret = -ENOMEM; -		goto error_free_trig; -	} -	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 deleted file mode 100644 index f3bf111f354..00000000000 --- a/drivers/staging/iio/industrialio-core.c +++ /dev/null @@ -1,815 +0,0 @@ -/* The industrial I/O core - * - * Copyright (c) 2008 Jonathan Cameron - * - * 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. - * - * Based on elements of hwmon and input subsystems. - */ - -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/idr.h> -#include <linux/kdev_t.h> -#include <linux/err.h> -#include <linux/device.h> -#include <linux/fs.h> -#include <linux/interrupt.h> -#include <linux/poll.h> -#include <linux/sched.h> -#include <linux/wait.h> -#include <linux/cdev.h> -#include <linux/slab.h> -#include "iio.h" -#include "trigger_consumer.h" - -#define IIO_ID_PREFIX "device" -#define IIO_ID_FORMAT IIO_ID_PREFIX "%d" - -/* IDR to assign each registered device a unique id*/ -static DEFINE_IDA(iio_ida); -/* IDR to allocate character device minor numbers */ -static DEFINE_IDA(iio_chrdev_ida); -/* Lock used to protect both of the above */ -static DEFINE_SPINLOCK(iio_ida_lock); - -dev_t iio_devt; -EXPORT_SYMBOL(iio_devt); - -#define IIO_DEV_MAX 256 -struct bus_type iio_bus_type = { -	.name = "iio", -}; -EXPORT_SYMBOL(iio_bus_type); - -void __iio_change_event(struct iio_detected_event_list *ev, -			int ev_code, -			s64 timestamp) -{ -	ev->ev.id = ev_code; -	ev->ev.timestamp = timestamp; -} -EXPORT_SYMBOL(__iio_change_event); - -/* Used both in the interrupt line put events and the ring buffer ones */ - -/* Note that in it's current form someone has to be listening before events - * are queued. Hence a client MUST open the chrdev before the ring buffer is - * switched on. - */ -int __iio_push_event(struct iio_event_interface *ev_int, -		     int ev_code, -		     s64 timestamp, -		     struct iio_shared_ev_pointer * -		     shared_pointer_p) -{ -	struct iio_detected_event_list *ev; -	int ret = 0; - -	/* Does anyone care? */ -	mutex_lock(&ev_int->event_list_lock); -	if (test_bit(IIO_BUSY_BIT_POS, &ev_int->handler.flags)) { -		if (ev_int->current_events == ev_int->max_events) { -			mutex_unlock(&ev_int->event_list_lock); -			return 0; -		} -		ev = kmalloc(sizeof(*ev), GFP_KERNEL); -		if (ev == NULL) { -			ret = -ENOMEM; -			mutex_unlock(&ev_int->event_list_lock); -			goto error_ret; -		} -		ev->ev.id = ev_code; -		ev->ev.timestamp = timestamp; -		ev->shared_pointer = shared_pointer_p; -		if (ev->shared_pointer) -			shared_pointer_p->ev_p = ev; - -		list_add_tail(&ev->list, &ev_int->det_events.list); -		ev_int->current_events++; -		mutex_unlock(&ev_int->event_list_lock); -		wake_up_interruptible(&ev_int->wait); -	} else -		mutex_unlock(&ev_int->event_list_lock); - -error_ret: -	return ret; -} -EXPORT_SYMBOL(__iio_push_event); - -int iio_push_event(struct iio_dev *dev_info, -		   int ev_line, -		   int ev_code, -		   s64 timestamp) -{ -	return __iio_push_event(&dev_info->event_interfaces[ev_line], -				ev_code, timestamp, NULL); -} -EXPORT_SYMBOL(iio_push_event); - -/* Generic interrupt line interrupt handler */ -static irqreturn_t iio_interrupt_handler(int irq, void *_int_info) -{ -	struct iio_interrupt *int_info = _int_info; -	struct iio_dev *dev_info = int_info->dev_info; -	struct iio_event_handler_list *p; -	s64 time_ns; -	unsigned long flags; - -	spin_lock_irqsave(&int_info->ev_list_lock, flags); -	if (list_empty(&int_info->ev_list)) { -		spin_unlock_irqrestore(&int_info->ev_list_lock, flags); -		return IRQ_NONE; -	} - -	time_ns = iio_get_time_ns(); -	list_for_each_entry(p, &int_info->ev_list, list) { -		disable_irq_nosync(irq); -		p->handler(dev_info, 1, time_ns, !(p->refcount > 1)); -	} -	spin_unlock_irqrestore(&int_info->ev_list_lock, flags); - -	return IRQ_HANDLED; -} - -static struct iio_interrupt *iio_allocate_interrupt(void) -{ -	struct iio_interrupt *i = kmalloc(sizeof *i, GFP_KERNEL); -	if (i) { -		spin_lock_init(&i->ev_list_lock); -		INIT_LIST_HEAD(&i->ev_list); -	} -	return i; -} - -/* Confirming the validity of supplied irq is left to drivers.*/ -int iio_register_interrupt_line(unsigned int irq, -				struct iio_dev *dev_info, -				int line_number, -				unsigned long type, -				const char *name) -{ -	int ret; - -	dev_info->interrupts[line_number] = iio_allocate_interrupt(); -	if (dev_info->interrupts[line_number] == NULL) { -		ret = -ENOMEM; -		goto error_ret; -	} -	dev_info->interrupts[line_number]->line_number = line_number; -	dev_info->interrupts[line_number]->irq = irq; -	dev_info->interrupts[line_number]->dev_info = dev_info; - -	/* Possibly only request on demand? -	 * Can see this may complicate the handling of interrupts. -	 * However, with this approach we might end up handling lots of -	 * events no-one cares about.*/ -	ret = request_irq(irq, -			  &iio_interrupt_handler, -			  type, -			  name, -			  dev_info->interrupts[line_number]); - -error_ret: -	return ret; -} -EXPORT_SYMBOL(iio_register_interrupt_line); - -/* This turns up an awful lot */ -ssize_t iio_read_const_attr(struct device *dev, -			    struct device_attribute *attr, -			    char *buf) -{ -	return sprintf(buf, "%s\n", to_iio_const_attr(attr)->string); -} -EXPORT_SYMBOL(iio_read_const_attr); - -/* Before this runs the interrupt generator must have been disabled */ -void iio_unregister_interrupt_line(struct iio_dev *dev_info, int line_number) -{ -	/* make sure the interrupt handlers are all done */ -	flush_scheduled_work(); -	free_irq(dev_info->interrupts[line_number]->irq, -		 dev_info->interrupts[line_number]); -	kfree(dev_info->interrupts[line_number]); -} -EXPORT_SYMBOL(iio_unregister_interrupt_line); - -/* Reference counted add and remove */ -void iio_add_event_to_list(struct iio_event_handler_list *el, -			  struct list_head *head) -{ -	unsigned long flags; -	struct iio_interrupt *inter = to_iio_interrupt(head); - -	/* take mutex to protect this element */ -	mutex_lock(&el->exist_lock); -	if (el->refcount == 0) { -		/* Take the event list spin lock */ -		spin_lock_irqsave(&inter->ev_list_lock, flags); -		list_add(&el->list, head); -		spin_unlock_irqrestore(&inter->ev_list_lock, flags); -	} -	el->refcount++; -	mutex_unlock(&el->exist_lock); -} -EXPORT_SYMBOL(iio_add_event_to_list); - -void iio_remove_event_from_list(struct iio_event_handler_list *el, -			       struct list_head *head) -{ -	unsigned long flags; -	struct iio_interrupt *inter = to_iio_interrupt(head); - -	mutex_lock(&el->exist_lock); -	el->refcount--; -	if (el->refcount == 0) { -		/* Take the event list spin lock */ -		spin_lock_irqsave(&inter->ev_list_lock, flags); -		list_del_init(&el->list); -		spin_unlock_irqrestore(&inter->ev_list_lock, flags); -	} -	mutex_unlock(&el->exist_lock); -} -EXPORT_SYMBOL(iio_remove_event_from_list); - -static ssize_t iio_event_chrdev_read(struct file *filep, -				     char __user *buf, -				     size_t count, -				     loff_t *f_ps) -{ -	struct iio_event_interface *ev_int = filep->private_data; -	struct iio_detected_event_list *el; -	int ret; -	size_t len; - -	mutex_lock(&ev_int->event_list_lock); -	if (list_empty(&ev_int->det_events.list)) { -		if (filep->f_flags & O_NONBLOCK) { -			ret = -EAGAIN; -			goto error_mutex_unlock; -		} -		mutex_unlock(&ev_int->event_list_lock); -		/* Blocking on device; waiting for something to be there */ -		ret = wait_event_interruptible(ev_int->wait, -					       !list_empty(&ev_int -							   ->det_events.list)); -		if (ret) -			goto error_ret; -		/* Single access device so noone else can get the data */ -		mutex_lock(&ev_int->event_list_lock); -	} - -	el = list_first_entry(&ev_int->det_events.list, -			      struct iio_detected_event_list, -			      list); -	len = sizeof el->ev; -	if (copy_to_user(buf, &(el->ev), len)) { -		ret = -EFAULT; -		goto error_mutex_unlock; -	} -	list_del(&el->list); -	ev_int->current_events--; -	mutex_unlock(&ev_int->event_list_lock); -	/* -	 * Possible concurency issue if an update of this event is on its way -	 * through. May lead to new event being removed whilst the reported -	 * event was the unescalated event. In typical use case this is not a -	 * problem as userspace will say read half the buffer due to a 50% -	 * full event which would make the correct 100% full incorrect anyway. -	 */ -	if (el->shared_pointer) { -		spin_lock(&el->shared_pointer->lock); -		(el->shared_pointer->ev_p) = NULL; -		spin_unlock(&el->shared_pointer->lock); -	} -	kfree(el); - -	return len; - -error_mutex_unlock: -	mutex_unlock(&ev_int->event_list_lock); -error_ret: - -	return ret; -} - -static int iio_event_chrdev_release(struct inode *inode, struct file *filep) -{ -	struct iio_handler *hand = iio_cdev_to_handler(inode->i_cdev); -	struct iio_event_interface *ev_int = hand->private; -	struct iio_detected_event_list *el, *t; - -	mutex_lock(&ev_int->event_list_lock); -	clear_bit(IIO_BUSY_BIT_POS, &ev_int->handler.flags); -	/* -	 * In order to maintain a clean state for reopening, -	 * clear out any awaiting events. The mask will prevent -	 * any new __iio_push_event calls running. -	 */ -	list_for_each_entry_safe(el, t, &ev_int->det_events.list, list) { -		list_del(&el->list); -		kfree(el); -	} -	mutex_unlock(&ev_int->event_list_lock); - -	return 0; -} - -static int iio_event_chrdev_open(struct inode *inode, struct file *filep) -{ -	struct iio_handler *hand = iio_cdev_to_handler(inode->i_cdev); -	struct iio_event_interface *ev_int = hand->private; - -	mutex_lock(&ev_int->event_list_lock); -	if (test_and_set_bit(IIO_BUSY_BIT_POS, &hand->flags)) { -		fops_put(filep->f_op); -		mutex_unlock(&ev_int->event_list_lock); -		return -EBUSY; -	} -	filep->private_data = hand->private; -	mutex_unlock(&ev_int->event_list_lock); - -	return 0; -} - -static const struct file_operations iio_event_chrdev_fileops = { -	.read =  iio_event_chrdev_read, -	.release = iio_event_chrdev_release, -	.open = iio_event_chrdev_open, -	.owner = THIS_MODULE, -	.llseek = noop_llseek, -}; - -static void iio_event_dev_release(struct device *dev) -{ -	struct iio_event_interface *ev_int -		= container_of(dev, struct iio_event_interface, dev); -	cdev_del(&ev_int->handler.chrdev); -	iio_device_free_chrdev_minor(MINOR(dev->devt)); -}; - -static struct device_type iio_event_type = { -	.release = iio_event_dev_release, -}; - -int iio_device_get_chrdev_minor(void) -{ -	int ret, val; - -ida_again: -	if (unlikely(ida_pre_get(&iio_chrdev_ida, GFP_KERNEL) == 0)) -		return -ENOMEM; -	spin_lock(&iio_ida_lock); -	ret = ida_get_new(&iio_chrdev_ida, &val); -	spin_unlock(&iio_ida_lock); -	if (unlikely(ret == -EAGAIN)) -		goto ida_again; -	else if (unlikely(ret)) -		return ret; -	if (val > IIO_DEV_MAX) -		return -ENOMEM; -	return val; -} - -void iio_device_free_chrdev_minor(int val) -{ -	spin_lock(&iio_ida_lock); -	ida_remove(&iio_chrdev_ida, val); -	spin_unlock(&iio_ida_lock); -} - -int iio_setup_ev_int(struct iio_event_interface *ev_int, -		     const char *name, -		     struct module *owner, -		     struct device *dev) -{ -	int ret, minor; - -	ev_int->dev.bus = &iio_bus_type; -	ev_int->dev.parent = dev; -	ev_int->dev.type = &iio_event_type; -	device_initialize(&ev_int->dev); - -	minor = iio_device_get_chrdev_minor(); -	if (minor < 0) { -		ret = minor; -		goto error_device_put; -	} -	ev_int->dev.devt = MKDEV(MAJOR(iio_devt), minor); -	dev_set_name(&ev_int->dev, "%s", name); - -	ret = device_add(&ev_int->dev); -	if (ret) -		goto error_free_minor; - -	cdev_init(&ev_int->handler.chrdev, &iio_event_chrdev_fileops); -	ev_int->handler.chrdev.owner = owner; - -	mutex_init(&ev_int->event_list_lock); -	/* discussion point - make this variable? */ -	ev_int->max_events = 10; -	ev_int->current_events = 0; -	INIT_LIST_HEAD(&ev_int->det_events.list); -	init_waitqueue_head(&ev_int->wait); -	ev_int->handler.private = ev_int; -	ev_int->handler.flags = 0; - -	ret = cdev_add(&ev_int->handler.chrdev, ev_int->dev.devt, 1); -	if (ret) -		goto error_unreg_device; - -	return 0; - -error_unreg_device: -	device_unregister(&ev_int->dev); -error_free_minor: -	iio_device_free_chrdev_minor(minor); -error_device_put: -	put_device(&ev_int->dev); - -	return ret; -} - -void iio_free_ev_int(struct iio_event_interface *ev_int) -{ -	device_unregister(&ev_int->dev); -	put_device(&ev_int->dev); -} - -static int __init iio_dev_init(void) -{ -	int err; - -	err = alloc_chrdev_region(&iio_devt, 0, IIO_DEV_MAX, "iio"); -	if (err < 0) -		printk(KERN_ERR "%s: failed to allocate char dev region\n", -		       __FILE__); - -	return err; -} - -static void __exit iio_dev_exit(void) -{ -	if (iio_devt) -		unregister_chrdev_region(iio_devt, IIO_DEV_MAX); -} - -static int __init iio_init(void) -{ -	int ret; - -	/* Register sysfs bus */ -	ret  = bus_register(&iio_bus_type); -	if (ret < 0) { -		printk(KERN_ERR -		       "%s could not register bus type\n", -			__FILE__); -		goto error_nothing; -	} - -	ret = iio_dev_init(); -	if (ret < 0) -		goto error_unregister_bus_type; - -	return 0; - -error_unregister_bus_type: -	bus_unregister(&iio_bus_type); -error_nothing: -	return ret; -} - -static void __exit iio_exit(void) -{ -	iio_dev_exit(); -	bus_unregister(&iio_bus_type); -} - -static int iio_device_register_sysfs(struct iio_dev *dev_info) -{ -	int ret = 0; - -	ret = sysfs_create_group(&dev_info->dev.kobj, dev_info->attrs); -	if (ret) { -		dev_err(dev_info->dev.parent, -			"Failed to register sysfs hooks\n"); -		goto error_ret; -	} - -error_ret: -	return ret; -} - -static void iio_device_unregister_sysfs(struct iio_dev *dev_info) -{ -	sysfs_remove_group(&dev_info->dev.kobj, dev_info->attrs); -} - -/* Return a negative errno on failure */ -int iio_get_new_ida_val(struct ida *this_ida) -{ -	int ret; -	int val; - -ida_again: -	if (unlikely(ida_pre_get(this_ida, GFP_KERNEL) == 0)) -		return -ENOMEM; - -	spin_lock(&iio_ida_lock); -	ret = ida_get_new(this_ida, &val); -	spin_unlock(&iio_ida_lock); -	if (unlikely(ret == -EAGAIN)) -		goto ida_again; -	else if (unlikely(ret)) -		return ret; - -	return val; -} -EXPORT_SYMBOL(iio_get_new_ida_val); - -void iio_free_ida_val(struct ida *this_ida, int id) -{ -	spin_lock(&iio_ida_lock); -	ida_remove(this_ida, id); -	spin_unlock(&iio_ida_lock); -} -EXPORT_SYMBOL(iio_free_ida_val); - -static int iio_device_register_id(struct iio_dev *dev_info, -				  struct ida *this_ida) -{ -	dev_info->id = iio_get_new_ida_val(&iio_ida); -	if (dev_info->id < 0) -		return dev_info->id; -	return 0; -} - -static void iio_device_unregister_id(struct iio_dev *dev_info) -{ -	iio_free_ida_val(&iio_ida, dev_info->id); -} - -static inline int __iio_add_event_config_attrs(struct iio_dev *dev_info, int i) -{ -	int ret; -	/*p for adding, q for removing */ -	struct attribute **attrp, **attrq; - -	if (dev_info->event_conf_attrs && dev_info->event_conf_attrs[i].attrs) { -		attrp = dev_info->event_conf_attrs[i].attrs; -		while (*attrp) { -			ret =  sysfs_add_file_to_group(&dev_info->dev.kobj, -						       *attrp, -						       dev_info -						       ->event_attrs[i].name); -			if (ret) -				goto error_ret; -			attrp++; -		} -	} -	return 0; - -error_ret: -	attrq = dev_info->event_conf_attrs[i].attrs; -	while (attrq != attrp) { -			sysfs_remove_file_from_group(&dev_info->dev.kobj, -					     *attrq, -					     dev_info->event_attrs[i].name); -		attrq++; -	} - -	return ret; -} - -static inline int __iio_remove_event_config_attrs(struct iio_dev *dev_info, -						  int i) -{ -	struct attribute **attrq; - -	if (dev_info->event_conf_attrs -		&& dev_info->event_conf_attrs[i].attrs) { -		attrq = dev_info->event_conf_attrs[i].attrs; -		while (*attrq) { -			sysfs_remove_file_from_group(&dev_info->dev.kobj, -						     *attrq, -						     dev_info -						     ->event_attrs[i].name); -			attrq++; -		} -	} - -	return 0; -} - -static int iio_device_register_eventset(struct iio_dev *dev_info) -{ -	int ret = 0, i, j; - -	if (dev_info->num_interrupt_lines == 0) -		return 0; - -	dev_info->event_interfaces = -		kzalloc(sizeof(struct iio_event_interface) -			*dev_info->num_interrupt_lines, -			GFP_KERNEL); -	if (dev_info->event_interfaces == NULL) { -		ret = -ENOMEM; -		goto error_ret; -	} - -	dev_info->interrupts = kzalloc(sizeof(struct iio_interrupt *) -				       *dev_info->num_interrupt_lines, -				       GFP_KERNEL); -	if (dev_info->interrupts == NULL) { -		ret = -ENOMEM; -		goto error_free_event_interfaces; -	} - -	for (i = 0; i < dev_info->num_interrupt_lines; i++) { -		dev_info->event_interfaces[i].owner = dev_info->driver_module; - -		snprintf(dev_info->event_interfaces[i]._name, 20, -			 "%s:event%d", -			 dev_name(&dev_info->dev), -			 i); - -		ret = iio_setup_ev_int(&dev_info->event_interfaces[i], -				       (const char *)(dev_info -						      ->event_interfaces[i] -						      ._name), -				       dev_info->driver_module, -				       &dev_info->dev); -		if (ret) { -			dev_err(&dev_info->dev, -				"Could not get chrdev interface\n"); -			goto error_free_setup_ev_ints; -		} - -		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"); -			goto error_remove_sysfs_interfaces; -		} -	} - -	for (i = 0; i < dev_info->num_interrupt_lines; i++) { -		ret = __iio_add_event_config_attrs(dev_info, i); -		if (ret) -			goto error_unregister_config_attrs; -	} - -	return 0; - -error_unregister_config_attrs: -	for (j = 0; j < i; j++) -		__iio_remove_event_config_attrs(dev_info, i); -	i = dev_info->num_interrupt_lines - 1; -error_remove_sysfs_interfaces: -	for (j = 0; j < i; j++) -		sysfs_remove_group(&dev_info -				   ->event_interfaces[j].dev.kobj, -				   &dev_info->event_attrs[j]); -error_free_setup_ev_ints: -	for (j = 0; j < i; j++) -		iio_free_ev_int(&dev_info->event_interfaces[j]); -	kfree(dev_info->interrupts); -error_free_event_interfaces: -	kfree(dev_info->event_interfaces); -error_ret: - -	return ret; -} - -static void iio_device_unregister_eventset(struct iio_dev *dev_info) -{ -	int i; - -	if (dev_info->num_interrupt_lines == 0) -		return; -	for (i = 0; i < dev_info->num_interrupt_lines; i++) -		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++) -		iio_free_ev_int(&dev_info->event_interfaces[i]); -	kfree(dev_info->interrupts); -	kfree(dev_info->event_interfaces); -} - -static void iio_dev_release(struct device *device) -{ -	struct iio_dev *dev = to_iio_dev(device); - -	iio_put(); -	kfree(dev); -} - -static struct device_type iio_dev_type = { -	.name = "iio_device", -	.release = iio_dev_release, -}; - -struct iio_dev *iio_allocate_device(void) -{ -	struct iio_dev *dev = kzalloc(sizeof *dev, GFP_KERNEL); - -	if (dev) { -		dev->dev.type = &iio_dev_type; -		dev->dev.bus = &iio_bus_type; -		device_initialize(&dev->dev); -		dev_set_drvdata(&dev->dev, (void *)dev); -		mutex_init(&dev->mlock); -		iio_get(); -	} - -	return dev; -} -EXPORT_SYMBOL(iio_allocate_device); - -void iio_free_device(struct iio_dev *dev) -{ -	if (dev) -		iio_put_device(dev); -} -EXPORT_SYMBOL(iio_free_device); - -int iio_device_register(struct iio_dev *dev_info) -{ -	int ret; - -	ret = iio_device_register_id(dev_info, &iio_ida); -	if (ret) { -		dev_err(&dev_info->dev, "Failed to get id\n"); -		goto error_ret; -	} -	dev_set_name(&dev_info->dev, "device%d", dev_info->id); - -	ret = device_add(&dev_info->dev); -	if (ret) -		goto error_free_ida; -	ret = iio_device_register_sysfs(dev_info); -	if (ret) { -		dev_err(dev_info->dev.parent, -			"Failed to register sysfs interfaces\n"); -		goto error_del_device; -	} -	ret = iio_device_register_eventset(dev_info); -	if (ret) { -		dev_err(dev_info->dev.parent, -			"Failed to register event set\n"); -		goto error_free_sysfs; -	} -	if (dev_info->modes & INDIO_RING_TRIGGERED) -		iio_device_register_trigger_consumer(dev_info); - -	return 0; - -error_free_sysfs: -	iio_device_unregister_sysfs(dev_info); -error_del_device: -	device_del(&dev_info->dev); -error_free_ida: -	iio_device_unregister_id(dev_info); -error_ret: -	return ret; -} -EXPORT_SYMBOL(iio_device_register); - -void iio_device_unregister(struct iio_dev *dev_info) -{ -	if (dev_info->modes & INDIO_RING_TRIGGERED) -		iio_device_unregister_trigger_consumer(dev_info); -	iio_device_unregister_eventset(dev_info); -	iio_device_unregister_sysfs(dev_info); -	iio_device_unregister_id(dev_info); -	device_unregister(&dev_info->dev); -} -EXPORT_SYMBOL(iio_device_unregister); - -void iio_put(void) -{ -	module_put(THIS_MODULE); -} - -void iio_get(void) -{ -	__module_get(THIS_MODULE); -} - -subsys_initcall(iio_init); -module_exit(iio_exit); - -MODULE_AUTHOR("Jonathan Cameron <jic23@cam.ac.uk>"); -MODULE_DESCRIPTION("Industrial I/O core"); -MODULE_LICENSE("GPL"); diff --git a/drivers/staging/iio/industrialio-ring.c b/drivers/staging/iio/industrialio-ring.c deleted file mode 100644 index 9a98fcdbe10..00000000000 --- a/drivers/staging/iio/industrialio-ring.c +++ /dev/null @@ -1,562 +0,0 @@ -/* The industrial I/O core - * - * Copyright (c) 2008 Jonathan Cameron - * - * 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. - * - * Handling of ring allocation / resizing. - * - * - * Things to look at here. - * - Better memory allocation techniques? - * - Alternative access techniques? - */ -#include <linux/kernel.h> -#include <linux/device.h> -#include <linux/fs.h> -#include <linux/poll.h> -#include <linux/cdev.h> -#include <linux/slab.h> - -#include "iio.h" -#include "ring_generic.h" - -int iio_push_ring_event(struct iio_ring_buffer *ring_buf, -		       int event_code, -		       s64 timestamp) -{ -	return __iio_push_event(&ring_buf->ev_int, -			       event_code, -			       timestamp, -			       &ring_buf->shared_ev_pointer); -} -EXPORT_SYMBOL(iio_push_ring_event); - -int iio_push_or_escallate_ring_event(struct iio_ring_buffer *ring_buf, -				    int event_code, -				    s64 timestamp) -{ -	if (ring_buf->shared_ev_pointer.ev_p) -		__iio_change_event(ring_buf->shared_ev_pointer.ev_p, -				   event_code, -				   timestamp); -	else -		return iio_push_ring_event(ring_buf, -					  event_code, -					  timestamp); -	return 0; -} -EXPORT_SYMBOL(iio_push_or_escallate_ring_event); - -/** - * iio_ring_open() - chrdev file open for ring buffer access - * - * This function relies on all ring buffer implementations having an - * iio_ring_buffer as their first element. - **/ -static int iio_ring_open(struct inode *inode, struct file *filp) -{ -	struct iio_handler *hand -		= container_of(inode->i_cdev, struct iio_handler, chrdev); -	struct iio_ring_buffer *rb = hand->private; - -	filp->private_data = hand->private; -	if (rb->access.mark_in_use) -		rb->access.mark_in_use(rb); - -	return 0; -} - -/** - * iio_ring_release() - chrdev file close ring buffer access - * - * This function relies on all ring buffer implementations having an - * iio_ring_buffer as their first element. - **/ -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); -	struct iio_ring_buffer *rb = hand->private; - -	clear_bit(IIO_BUSY_BIT_POS, &rb->access_handler.flags); -	if (rb->access.unmark_in_use) -		rb->access.unmark_in_use(rb); - -	return 0; -} - -/** - * iio_ring_rip_outer() - chrdev read for ring buffer access - * - * This function relies on all ring buffer implementations having an - * iio_ring _bufer as their first element. - **/ -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; -	u8 *data; -	/* rip lots must exist. */ -	if (!rb->access.rip_lots) -		return -EINVAL; -	copied = rb->access.rip_lots(rb, count, &data, &dead_offset); - -	if (copied <= 0) { -		ret = copied; -		goto error_ret; -	} -	if (copy_to_user(buf, data + dead_offset, copied))  { -		ret =  -EFAULT; -		goto error_free_data_cpy; -	} -	/* In clever ring buffer designs this may not need to be freed. -	 * When such a design exists I'll add this to ring access funcs. -	 */ -	kfree(data); - -	return copied; - -error_free_data_cpy: -	kfree(data); -error_ret: -	return ret; -} - -static const struct file_operations iio_ring_fileops = { -	.read = iio_ring_rip_outer, -	.release = iio_ring_release, -	.open = iio_ring_open, -	.owner = THIS_MODULE, -	.llseek = noop_llseek, -}; - -/** - * __iio_request_ring_buffer_event_chrdev() - allocate ring event chrdev - * @buf:	ring buffer whose event chrdev we are allocating - * @id:		id of this ring buffer (typically 0) - * @owner:	the module who owns the ring buffer (for ref counting) - * @dev:	device with which the chrdev is associated - **/ -static inline int -__iio_request_ring_buffer_event_chrdev(struct iio_ring_buffer *buf, -				       int id, -				       struct module *owner, -				       struct device *dev) -{ -	int ret; - -	snprintf(buf->ev_int._name, sizeof(buf->ev_int._name), -		 "%s:event%d", -		 dev_name(&buf->dev), -		 id); -	ret = iio_setup_ev_int(&(buf->ev_int), -			       buf->ev_int._name, -			       owner, -			       dev); -	if (ret) -		goto error_ret; -	return 0; - -error_ret: -	return ret; -} - -static inline void -__iio_free_ring_buffer_event_chrdev(struct iio_ring_buffer *buf) -{ -	iio_free_ev_int(&(buf->ev_int)); -} - -static void iio_ring_access_release(struct device *dev) -{ -	struct iio_ring_buffer *buf -		= access_dev_to_iio_ring_buffer(dev); -	cdev_del(&buf->access_handler.chrdev); -	iio_device_free_chrdev_minor(MINOR(dev->devt)); -} - -static struct device_type iio_ring_access_type = { -	.release = iio_ring_access_release, -}; - -static inline int -__iio_request_ring_buffer_access_chrdev(struct iio_ring_buffer *buf, -					int id, -					struct module *owner) -{ -	int ret, minor; - -	buf->access_handler.flags = 0; - -	buf->access_dev.parent = &buf->dev; -	buf->access_dev.bus = &iio_bus_type; -	buf->access_dev.type = &iio_ring_access_type; -	device_initialize(&buf->access_dev); - -	minor = iio_device_get_chrdev_minor(); -	if (minor < 0) { -		ret = minor; -		goto error_device_put; -	} -	buf->access_dev.devt = MKDEV(MAJOR(iio_devt), minor); - - -	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_device_put; -	} - -	cdev_init(&buf->access_handler.chrdev, &iio_ring_fileops); -	buf->access_handler.chrdev.owner = owner; - -	ret = cdev_add(&buf->access_handler.chrdev, buf->access_dev.devt, 1); -	if (ret) { -		printk(KERN_ERR "failed to allocate ring access chrdev\n"); -		goto error_device_unregister; -	} -	return 0; - -error_device_unregister: -	device_unregister(&buf->access_dev); -error_device_put: -	put_device(&buf->access_dev); - -	return ret; -} - -static void __iio_free_ring_buffer_access_chrdev(struct iio_ring_buffer *buf) -{ -	device_unregister(&buf->access_dev); -} - -void iio_ring_buffer_init(struct iio_ring_buffer *ring, -			  struct iio_dev *dev_info) -{ -	if (ring->access.mark_param_change) -		ring->access.mark_param_change(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 id) -{ -	int ret; - -	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_ret; - -	ret = __iio_request_ring_buffer_event_chrdev(ring, -						     0, -						     ring->owner, -						     &ring->dev); -	if (ret) -		goto error_remove_device; - -	ret = __iio_request_ring_buffer_access_chrdev(ring, -						      0, -						      ring->owner); - -	if (ret) -		goto error_free_ring_buffer_event_chrdev; - -	if (ring->scan_el_attrs) { -		ret = sysfs_create_group(&ring->dev.kobj, -					 ring->scan_el_attrs); -		if (ret) { -			dev_err(&ring->dev, -				"Failed to add sysfs scan elements\n"); -			goto error_free_ring_buffer_event_chrdev; -		} -	} - -	return ret; -error_free_ring_buffer_event_chrdev: -	__iio_free_ring_buffer_event_chrdev(ring); -error_remove_device: -	device_del(&ring->dev); -error_ret: -	return ret; -} -EXPORT_SYMBOL(iio_ring_buffer_register); - -void iio_ring_buffer_unregister(struct iio_ring_buffer *ring) -{ -	if (ring->scan_el_attrs) -		sysfs_remove_group(&ring->dev.kobj, -				   ring->scan_el_attrs); - -	__iio_free_ring_buffer_access_chrdev(ring); -	__iio_free_ring_buffer_event_chrdev(ring); -	device_del(&ring->dev); -} -EXPORT_SYMBOL(iio_ring_buffer_unregister); - -ssize_t iio_read_ring_length(struct device *dev, -			     struct device_attribute *attr, -			     char *buf) -{ -	int len = 0; -	struct iio_ring_buffer *ring = dev_get_drvdata(dev); - -	if (ring->access.get_length) -		len = sprintf(buf, "%d\n", -			      ring->access.get_length(ring)); - -	return len; -} -EXPORT_SYMBOL(iio_read_ring_length); - -ssize_t iio_write_ring_length(struct device *dev, -			       struct device_attribute *attr, -			       const char *buf, -			       size_t len) -{ -	int ret; -	ulong val; -	struct iio_ring_buffer *ring = dev_get_drvdata(dev); -	ret = strict_strtoul(buf, 10, &val); -	if (ret) -		return ret; - -	if (ring->access.get_length) -		if (val == ring->access.get_length(ring)) -			return len; - -	if (ring->access.set_length) { -		ring->access.set_length(ring, val); -		if (ring->access.mark_param_change) -			ring->access.mark_param_change(ring); -	} - -	return len; -} -EXPORT_SYMBOL(iio_write_ring_length); - -ssize_t iio_read_ring_bytes_per_datum(struct device *dev, -			  struct device_attribute *attr, -			  char *buf) -{ -	int len = 0; -	struct iio_ring_buffer *ring = dev_get_drvdata(dev); - -	if (ring->access.get_bytes_per_datum) -		len = sprintf(buf, "%d\n", -			      ring->access.get_bytes_per_datum(ring)); - -	return len; -} -EXPORT_SYMBOL(iio_read_ring_bytes_per_datum); - -ssize_t iio_store_ring_enable(struct device *dev, -			      struct device_attribute *attr, -			      const char *buf, -			      size_t len) -{ -	int ret; -	bool requested_state, current_state; -	int previous_mode; -	struct iio_ring_buffer *ring = dev_get_drvdata(dev); -	struct iio_dev *dev_info = ring->indio_dev; - -	mutex_lock(&dev_info->mlock); -	previous_mode = dev_info->currentmode; -	requested_state = !(buf[0] == '0'); -	current_state = !!(previous_mode & INDIO_ALL_RING_MODES); -	if (current_state == requested_state) { -		printk(KERN_INFO "iio-ring, current state requested again\n"); -		goto done; -	} -	if (requested_state) { -		if (ring->preenable) { -			ret = ring->preenable(dev_info); -			if (ret) { -				printk(KERN_ERR -				       "Buffer not started:" -				       "ring preenable failed\n"); -				goto error_ret; -			} -		} -		if (ring->access.request_update) { -			ret = ring->access.request_update(ring); -			if (ret) { -				printk(KERN_INFO -				       "Buffer not started:" -				       "ring parameter update failed\n"); -				goto error_ret; -			} -		} -		if (ring->access.mark_in_use) -			ring->access.mark_in_use(ring); -		/* Definitely possible for devices to support both of these.*/ -		if (dev_info->modes & INDIO_RING_TRIGGERED) { -			if (!dev_info->trig) { -				printk(KERN_INFO -				       "Buffer not started: no trigger\n"); -				ret = -EINVAL; -				if (ring->access.unmark_in_use) -					ring->access.unmark_in_use(ring); -				goto error_ret; -			} -			dev_info->currentmode = INDIO_RING_TRIGGERED; -		} else if (dev_info->modes & INDIO_RING_HARDWARE_BUFFER) -			dev_info->currentmode = INDIO_RING_HARDWARE_BUFFER; -		else { /* should never be reached */ -			ret = -EINVAL; -			goto error_ret; -		} - -		if (ring->postenable) { - -			ret = ring->postenable(dev_info); -			if (ret) { -				printk(KERN_INFO -				       "Buffer not started:" -				       "postenable failed\n"); -				if (ring->access.unmark_in_use) -					ring->access.unmark_in_use(ring); -				dev_info->currentmode = previous_mode; -				if (ring->postdisable) -					ring->postdisable(dev_info); -				goto error_ret; -			} -		} -	} else { -		if (ring->predisable) { -			ret = ring->predisable(dev_info); -			if (ret) -				goto error_ret; -		} -		if (ring->access.unmark_in_use) -			ring->access.unmark_in_use(ring); -		dev_info->currentmode = INDIO_DIRECT_MODE; -		if (ring->postdisable) { -			ret = ring->postdisable(dev_info); -			if (ret) -				goto error_ret; -		} -	} -done: -	mutex_unlock(&dev_info->mlock); -	return len; - -error_ret: -	mutex_unlock(&dev_info->mlock); -	return ret; -} -EXPORT_SYMBOL(iio_store_ring_enable); -ssize_t iio_show_ring_enable(struct device *dev, -				    struct device_attribute *attr, -				    char *buf) -{ -	struct iio_ring_buffer *ring = dev_get_drvdata(dev); -	return sprintf(buf, "%d\n", !!(ring->indio_dev->currentmode -				       & INDIO_ALL_RING_MODES)); -} -EXPORT_SYMBOL(iio_show_ring_enable); - -ssize_t iio_scan_el_show(struct device *dev, -			 struct device_attribute *attr, -			 char *buf) -{ -	int ret; -	struct iio_ring_buffer *ring = dev_get_drvdata(dev); -	struct iio_scan_el *this_el = to_iio_scan_el(attr); - -	ret = iio_scan_mask_query(ring, this_el->number); -	if (ret < 0) -		return ret; -	return sprintf(buf, "%d\n", ret); -} -EXPORT_SYMBOL(iio_scan_el_show); - -ssize_t iio_scan_el_store(struct device *dev, -			  struct device_attribute *attr, -			  const char *buf, -			  size_t len) -{ -	int ret = 0; -	bool state; -	struct iio_ring_buffer *ring = dev_get_drvdata(dev); -	struct iio_dev *indio_dev = ring->indio_dev; -	struct iio_scan_el *this_el = to_iio_scan_el(attr); - -	state = !(buf[0] == '0'); -	mutex_lock(&indio_dev->mlock); -	if (indio_dev->currentmode == INDIO_RING_TRIGGERED) { -		ret = -EBUSY; -		goto error_ret; -	} -	ret = iio_scan_mask_query(ring, this_el->number); -	if (ret < 0) -		goto error_ret; -	if (!state && ret) { -		ret = iio_scan_mask_clear(ring, this_el->number); -		if (ret) -			goto error_ret; -	} else if (state && !ret) { -		ret = iio_scan_mask_set(ring, this_el->number); -		if (ret) -			goto error_ret; -	} -	if (this_el->set_state) -		ret = this_el->set_state(this_el, indio_dev, state); -error_ret: -	mutex_unlock(&indio_dev->mlock); - -	return ret ? ret : len; - -} -EXPORT_SYMBOL(iio_scan_el_store); - -ssize_t iio_scan_el_ts_show(struct device *dev, -			    struct device_attribute *attr, -			    char *buf) -{ -	struct iio_ring_buffer *ring = dev_get_drvdata(dev); -	return sprintf(buf, "%d\n", ring->scan_timestamp); -} -EXPORT_SYMBOL(iio_scan_el_ts_show); - -ssize_t iio_scan_el_ts_store(struct device *dev, -			     struct device_attribute *attr, -			     const char *buf, -			     size_t len) -{ -	int ret = 0; -	struct iio_ring_buffer *ring = dev_get_drvdata(dev); -	struct iio_dev *indio_dev = ring->indio_dev; -	bool state; -	state = !(buf[0] == '0'); -	mutex_lock(&indio_dev->mlock); -	if (indio_dev->currentmode == INDIO_RING_TRIGGERED) { -		ret = -EBUSY; -		goto error_ret; -	} -	ring->scan_timestamp = state; -error_ret: -	mutex_unlock(&indio_dev->mlock); - -	return ret ? ret : len; -} -EXPORT_SYMBOL(iio_scan_el_ts_store); - diff --git a/drivers/staging/iio/industrialio-trigger.c b/drivers/staging/iio/industrialio-trigger.c deleted file mode 100644 index 57dd9232cf0..00000000000 --- a/drivers/staging/iio/industrialio-trigger.c +++ /dev/null @@ -1,431 +0,0 @@ -/* The industrial I/O core, trigger handling functions - * - * Copyright (c) 2008 Jonathan Cameron - * - * 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/kernel.h> -#include <linux/module.h> -#include <linux/idr.h> -#include <linux/err.h> -#include <linux/device.h> -#include <linux/interrupt.h> -#include <linux/list.h> -#include <linux/slab.h> - -#include "iio.h" -#include "trigger.h" -#include "trigger_consumer.h" - -/* RFC - Question of approach - * Make the common case (single sensor single trigger) - * simple by starting trigger capture from when first sensors - * is added. - * - * Complex simultaneous start requires use of 'hold' functionality - * of the trigger. (not implemented) - * - * Any other suggestions? - */ - -static DEFINE_IDR(iio_trigger_idr); -static DEFINE_SPINLOCK(iio_trigger_idr_lock); - -/* Single list of all available triggers */ -static LIST_HEAD(iio_trigger_list); -static DEFINE_MUTEX(iio_trigger_list_lock); - -/** - * iio_trigger_register_sysfs() - create a device for this trigger - * @trig_info:	the trigger - * - * Also adds any control attribute registered by the trigger driver - **/ -static int iio_trigger_register_sysfs(struct iio_trigger *trig_info) -{ -	int ret = 0; - -	if (trig_info->control_attrs) -		ret = sysfs_create_group(&trig_info->dev.kobj, -					 trig_info->control_attrs); - -	return ret; -} - -static void iio_trigger_unregister_sysfs(struct iio_trigger *trig_info) -{ -	if (trig_info->control_attrs) -		sysfs_remove_group(&trig_info->dev.kobj, -				   trig_info->control_attrs); -} - - -/** - * iio_trigger_register_id() - get a unique id for this trigger - * @trig_info:	the trigger - **/ -static int iio_trigger_register_id(struct iio_trigger *trig_info) -{ -	int ret = 0; - -idr_again: -	if (unlikely(idr_pre_get(&iio_trigger_idr, GFP_KERNEL) == 0)) -		return -ENOMEM; - -	spin_lock(&iio_trigger_idr_lock); -	ret = idr_get_new(&iio_trigger_idr, NULL, &trig_info->id); -	spin_unlock(&iio_trigger_idr_lock); -	if (unlikely(ret == -EAGAIN)) -		goto idr_again; -	else if (likely(!ret)) -		trig_info->id = trig_info->id & MAX_ID_MASK; - -	return ret; -} - -/** - * iio_trigger_unregister_id() - free up unique id for use by another trigger - * @trig_info: the trigger - **/ -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); -} - -int iio_trigger_register(struct iio_trigger *trig_info) -{ -	int ret; - -	ret = iio_trigger_register_id(trig_info); -	if (ret) -		goto error_ret; -	/* Set the name used for the sysfs directory etc */ -	dev_set_name(&trig_info->dev, "trigger%ld", -		     (unsigned long) trig_info->id); - -	ret = device_add(&trig_info->dev); -	if (ret) -		goto error_unregister_id; - -	ret = iio_trigger_register_sysfs(trig_info); -	if (ret) -		goto error_device_del; - -	/* Add to list of available triggers held by the IIO core */ -	mutex_lock(&iio_trigger_list_lock); -	list_add_tail(&trig_info->list, &iio_trigger_list); -	mutex_unlock(&iio_trigger_list_lock); - -	return 0; - -error_device_del: -	device_del(&trig_info->dev); -error_unregister_id: -	iio_trigger_unregister_id(trig_info); -error_ret: -	return ret; -} -EXPORT_SYMBOL(iio_trigger_register); - -void iio_trigger_unregister(struct iio_trigger *trig_info) -{ -	struct iio_trigger *cursor; - -	mutex_lock(&iio_trigger_list_lock); -	list_for_each_entry(cursor, &iio_trigger_list, list) -		if (cursor == trig_info) { -			list_del(&cursor->list); -			break; -		} -	mutex_unlock(&iio_trigger_list_lock); - -	iio_trigger_unregister_sysfs(trig_info); -	iio_trigger_unregister_id(trig_info); -	/* Possible issue in here */ -	device_unregister(&trig_info->dev); -} -EXPORT_SYMBOL(iio_trigger_unregister); - -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) { -			found = true; -			break; -		} -	} -	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, s64 time) -{ -	struct iio_poll_func *pf_cursor; - -	list_for_each_entry(pf_cursor, &trig->pollfunc_list, list) { -		if (pf_cursor->poll_func_immediate) { -			pf_cursor->poll_func_immediate(pf_cursor->private_data); -			trig->use_count++; -		} -	} -	list_for_each_entry(pf_cursor, &trig->pollfunc_list, list) { -		if (pf_cursor->poll_func_main) { -			pf_cursor->poll_func_main(pf_cursor->private_data, -						  time); -			trig->use_count++; -		} -	} -} -EXPORT_SYMBOL(iio_trigger_poll); - -void iio_trigger_notify_done(struct iio_trigger *trig) -{ -	trig->use_count--; -	if (trig->use_count == 0 && trig->try_reenable) -		if (trig->try_reenable(trig)) { -			/* Missed and interrupt so launch new poll now */ -			iio_trigger_poll(trig, 0); -		} -} -EXPORT_SYMBOL(iio_trigger_notify_done); - -/** - * iio_trigger_read_name() - retrieve useful identifying name - **/ -ssize_t iio_trigger_read_name(struct device *dev, -			      struct device_attribute *attr, -			      char *buf) -{ -	struct iio_trigger *trig = dev_get_drvdata(dev); -	return sprintf(buf, "%s\n", trig->name); -} -EXPORT_SYMBOL(iio_trigger_read_name); - -/* Trigger Consumer related functions */ - -/* Complexity in here.  With certain triggers (datardy) an acknowledgement - * may be needed if the pollfuncs do not include the data read for the - * triggering device. - * This is not currently handled.  Alternative of not enabling trigger unless - * the relevant function is in there may be the best option. - */ -/* Worth protecting against double additions?*/ -int iio_trigger_attach_poll_func(struct iio_trigger *trig, -				 struct iio_poll_func *pf) -{ -	int ret = 0; -	unsigned long flags; - -	spin_lock_irqsave(&trig->pollfunc_list_lock, flags); -	list_add_tail(&pf->list, &trig->pollfunc_list); -	spin_unlock_irqrestore(&trig->pollfunc_list_lock, flags); - -	if (trig->set_trigger_state) -		ret = trig->set_trigger_state(trig, true); -	if (ret) { -		printk(KERN_ERR "set trigger state failed\n"); -		list_del(&pf->list); -	} -	return ret; -} -EXPORT_SYMBOL(iio_trigger_attach_poll_func); - -int iio_trigger_dettach_poll_func(struct iio_trigger *trig, -				  struct iio_poll_func *pf) -{ -	struct iio_poll_func *pf_cursor; -	unsigned long flags; -	int ret = -EINVAL; - -	spin_lock_irqsave(&trig->pollfunc_list_lock, flags); -	list_for_each_entry(pf_cursor, &trig->pollfunc_list, list) -		if (pf_cursor == pf) { -			ret = 0; -			break; -		} -	if (!ret) { -		if (list_is_singular(&trig->pollfunc_list) -		    && trig->set_trigger_state) { -			spin_unlock_irqrestore(&trig->pollfunc_list_lock, -					       flags); -			/* May sleep hence cannot hold the spin lock */ -			ret = trig->set_trigger_state(trig, false); -			if (ret) -				goto error_ret; -			spin_lock_irqsave(&trig->pollfunc_list_lock, flags); -		} -		/* -		 * Now we can delete safe in the knowledge that, if this is -		 * the last pollfunc then we have disabled the trigger anyway -		 * and so nothing should be able to call the pollfunc. -		 */ -		list_del(&pf_cursor->list); -	} -	spin_unlock_irqrestore(&trig->pollfunc_list_lock, flags); - -error_ret: -	return ret; -} -EXPORT_SYMBOL(iio_trigger_dettach_poll_func); - -/** - * iio_trigger_read_currrent() - trigger consumer sysfs query which trigger - * - * For trigger consumers the current_trigger interface allows the trigger - * used by the device to be queried. - **/ -static ssize_t iio_trigger_read_current(struct device *dev, -					struct device_attribute *attr, -					char *buf) -{ -	struct iio_dev *dev_info = dev_get_drvdata(dev); -	int len = 0; -	if (dev_info->trig) -		len = sprintf(buf, -			      "%s\n", -			      dev_info->trig->name); -	return len; -} - -/** - * iio_trigger_write_current() trigger consumer sysfs set current trigger - * - * For trigger consumers the current_trigger interface allows the trigger - * used for this device to be specified at run time based on the triggers - * name. - **/ -static ssize_t iio_trigger_write_current(struct device *dev, -					 struct device_attribute *attr, -					 const char *buf, -					 size_t len) -{ -	struct iio_dev *dev_info = dev_get_drvdata(dev); -	struct iio_trigger *oldtrig = dev_info->trig; -	mutex_lock(&dev_info->mlock); -	if (dev_info->currentmode == INDIO_RING_TRIGGERED) { -		mutex_unlock(&dev_info->mlock); -		return -EBUSY; -	} -	mutex_unlock(&dev_info->mlock); - -	dev_info->trig = iio_trigger_find_by_name(buf, len); -	if (oldtrig && dev_info->trig != oldtrig) -		iio_put_trigger(oldtrig); -	if (dev_info->trig) -		iio_get_trigger(dev_info->trig); - -	return len; -} - -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, -	NULL, -}; - -static const struct attribute_group iio_trigger_consumer_attr_group = { -	.name = "trigger", -	.attrs = iio_trigger_consumer_attrs, -}; - -static void iio_trig_release(struct device *device) -{ -	struct iio_trigger *trig = to_iio_trigger(device); -	kfree(trig); -	iio_put(); -} - -static struct device_type iio_trig_type = { -	.release = iio_trig_release, -}; - -struct iio_trigger *iio_allocate_trigger(void) -{ -	struct iio_trigger *trig; -	trig = kzalloc(sizeof *trig, GFP_KERNEL); -	if (trig) { -		trig->dev.type = &iio_trig_type; -		trig->dev.bus = &iio_bus_type; -		device_initialize(&trig->dev); -		dev_set_drvdata(&trig->dev, (void *)trig); -		spin_lock_init(&trig->pollfunc_list_lock); -		INIT_LIST_HEAD(&trig->list); -		INIT_LIST_HEAD(&trig->pollfunc_list); -		iio_get(); -	} -	return trig; -} -EXPORT_SYMBOL(iio_allocate_trigger); - -void iio_free_trigger(struct iio_trigger *trig) -{ -	if (trig) -		put_device(&trig->dev); -} -EXPORT_SYMBOL(iio_free_trigger); - -int iio_device_register_trigger_consumer(struct iio_dev *dev_info) -{ -	int ret; -	ret = sysfs_create_group(&dev_info->dev.kobj, -				 &iio_trigger_consumer_attr_group); -	return ret; -} -EXPORT_SYMBOL(iio_device_register_trigger_consumer); - -int iio_device_unregister_trigger_consumer(struct iio_dev *dev_info) -{ -	sysfs_remove_group(&dev_info->dev.kobj, -			   &iio_trigger_consumer_attr_group); -	return 0; -} -EXPORT_SYMBOL(iio_device_unregister_trigger_consumer); - -int iio_alloc_pollfunc(struct iio_dev *indio_dev, -		       void (*immediate)(struct iio_dev *indio_dev), -		       void (*main)(struct iio_dev *private_data, s64 time)) -{ -	indio_dev->pollfunc = kzalloc(sizeof(*indio_dev->pollfunc), GFP_KERNEL); -	if (indio_dev->pollfunc == NULL) -		return -ENOMEM; -	indio_dev->pollfunc->poll_func_immediate = immediate; -	indio_dev->pollfunc->poll_func_main = main; -	indio_dev->pollfunc->private_data = indio_dev; -	return 0; -} -EXPORT_SYMBOL(iio_alloc_pollfunc); - -int iio_triggered_ring_postenable(struct iio_dev *indio_dev) -{ -	return indio_dev->trig -		? iio_trigger_attach_poll_func(indio_dev->trig, -					       indio_dev->pollfunc) -		: 0; -} -EXPORT_SYMBOL(iio_triggered_ring_postenable); - -int iio_triggered_ring_predisable(struct iio_dev *indio_dev) -{ -	return indio_dev->trig -		? iio_trigger_dettach_poll_func(indio_dev->trig, -						indio_dev->pollfunc) -		: 0; -} -EXPORT_SYMBOL(iio_triggered_ring_predisable); diff --git a/drivers/staging/iio/light/Kconfig b/drivers/staging/iio/light/Kconfig index 36d8bbe1a9c..ca8d6e66c89 100644 --- a/drivers/staging/iio/light/Kconfig +++ b/drivers/staging/iio/light/Kconfig @@ -1,26 +1,43 @@  #  # Light sensors  # -comment "Light sensors" +menu "Light sensors" -config SENSORS_TSL2563 -	tristate "TAOS TSL256[0-3] ambient light sensor" +config SENSORS_ISL29018 +	tristate "ISL 29018 light and proximity sensor"  	depends on I2C +	select REGMAP_I2C +	default n  	help -	 If you say yes here you get support for the Taos TSL2560, -	 TSL2561, TSL2562 and TSL2563 ambient light sensors. +	 If you say yes here you get support for ambient light sensing and +	 proximity infrared sensing from Intersil ISL29018. +	 This driver will provide the measurements of ambient light intensity +	 in lux, proximity infrared sensing and normal infrared sensing. +	 Data from sensor is accessible via sysfs. -	 This driver can also be built as a module.  If so, the module -	 will be called tsl2563. +config SENSORS_ISL29028 +	tristate "Intersil ISL29028 Concurrent Light and Proximity Sensor" +	depends on I2C +	select REGMAP_I2C +	help +	 Provides driver for the Intersil's ISL29028 device. +	 This driver supports the sysfs interface to get the ALS, IR intensity, +	 Proximity value via iio. The ISL29028 provides the concurrent sensing +	 of ambient light and proximity. -config SENSORS_ISL29018 -        tristate "ISL 29018 light and proximity sensor" -        depends on I2C -        default n -        help -         If you say yes here you get support for ambient light sensing and -         proximity infrared sensing from Intersil ISL29018. -         This driver will provide the measurements of ambient light intensity -         in lux, proximity infrared sensing and normal infrared sensing. -         Data from sensor is accessible via sysfs. +config TSL2583 +	tristate "TAOS TSL2580, TSL2581 and TSL2583 light-to-digital converters" +	depends on I2C +	help +	 Provides support for the TAOS tsl2580, tsl2581 and tsl2583 devices. +	 Access ALS data via iio, sysfs. + +config TSL2x7x +	tristate "TAOS TSL/TMD2x71 and TSL/TMD2x72 Family of light and proximity sensors" +	depends on I2C +	help +	 Support for: tsl2571, tsl2671, tmd2671, tsl2771, tmd2771, tsl2572, tsl2672, +	 tmd2672, tsl2772, tmd2772 devices. +	 Provides iio_events and direct access via sysfs. +endmenu diff --git a/drivers/staging/iio/light/Makefile b/drivers/staging/iio/light/Makefile index 9142c0e5a1b..9960fdf7c15 100644 --- a/drivers/staging/iio/light/Makefile +++ b/drivers/staging/iio/light/Makefile @@ -2,5 +2,7 @@  # Makefile for industrial I/O Light sensors  # -obj-$(CONFIG_SENSORS_TSL2563)	+= tsl2563.o  obj-$(CONFIG_SENSORS_ISL29018)	+= isl29018.o +obj-$(CONFIG_SENSORS_ISL29028)	+= isl29028.o +obj-$(CONFIG_TSL2583)	+= tsl2583.o +obj-$(CONFIG_TSL2x7x)	+= tsl2x7x_core.o diff --git a/drivers/staging/iio/light/isl29018.c b/drivers/staging/iio/light/isl29018.c index f919cc1d35e..3660a43b5f0 100644 --- a/drivers/staging/iio/light/isl29018.c +++ b/drivers/staging/iio/light/isl29018.c @@ -26,8 +26,10 @@  #include <linux/err.h>  #include <linux/mutex.h>  #include <linux/delay.h> +#include <linux/regmap.h>  #include <linux/slab.h> -#include "../iio.h" +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.h>  #define CONVERSION_TIME_MS		100 @@ -51,40 +53,24 @@  #define ISL29018_REG_ADD_DATA_LSB	0x02  #define ISL29018_REG_ADD_DATA_MSB	0x03 -#define ISL29018_MAX_REGS		ISL29018_REG_ADD_DATA_MSB + +#define ISL29018_REG_TEST		0x08 +#define ISL29018_TEST_SHIFT		0 +#define ISL29018_TEST_MASK		(0xFF << ISL29018_TEST_SHIFT)  struct isl29018_chip { -	struct iio_dev		*indio_dev; -	struct i2c_client	*client; +	struct device		*dev; +	struct regmap		*regmap;  	struct mutex		lock; +	unsigned int		lux_scale; +	unsigned int		lux_uscale;  	unsigned int		range;  	unsigned int		adc_bit;  	int			prox_scheme; -	u8			reg_cache[ISL29018_MAX_REGS]; +	bool			suspended;  }; -static int isl29018_write_data(struct i2c_client *client, u8 reg, -			u8 val, u8 mask, u8 shift) -{ -	u8 regval; -	int ret = 0; -	struct isl29018_chip *chip = i2c_get_clientdata(client); - -	regval = chip->reg_cache[reg]; -	regval &= ~mask; -	regval |= val << shift; - -	ret = i2c_smbus_write_byte_data(client, reg, regval); -	if (ret) { -		dev_err(&client->dev, "Write to device fails status %x\n", ret); -		return ret; -	} -	chip->reg_cache[reg] = regval; - -	return 0; -} - -static int isl29018_set_range(struct i2c_client *client, unsigned long range, +static int isl29018_set_range(struct isl29018_chip *chip, unsigned long range,  		unsigned int *new_range)  {  	static const unsigned long supp_ranges[] = {1000, 4000, 16000, 64000}; @@ -100,11 +86,11 @@ static int isl29018_set_range(struct i2c_client *client, unsigned long range,  	if (i >= ARRAY_SIZE(supp_ranges))  		return -EINVAL; -	return isl29018_write_data(client, ISL29018_REG_ADD_COMMANDII, -			i, COMMANDII_RANGE_MASK, COMMANDII_RANGE_SHIFT); +	return regmap_update_bits(chip->regmap, ISL29018_REG_ADD_COMMANDII, +			COMMANDII_RANGE_MASK, i << COMMANDII_RANGE_SHIFT);  } -static int isl29018_set_resolution(struct i2c_client *client, +static int isl29018_set_resolution(struct isl29018_chip *chip,  			unsigned long adcbit, unsigned int *conf_adc_bit)  {  	static const unsigned long supp_adcbit[] = {16, 12, 8, 4}; @@ -120,62 +106,72 @@ static int isl29018_set_resolution(struct i2c_client *client,  	if (i >= ARRAY_SIZE(supp_adcbit))  		return -EINVAL; -	return isl29018_write_data(client, ISL29018_REG_ADD_COMMANDII, -			i, COMMANDII_RESOLUTION_MASK, -			COMMANDII_RESOLUTION_SHIFT); +	return regmap_update_bits(chip->regmap, ISL29018_REG_ADD_COMMANDII, +			COMMANDII_RESOLUTION_MASK, +			i << COMMANDII_RESOLUTION_SHIFT);  } -static int isl29018_read_sensor_input(struct i2c_client *client, int mode) +static int isl29018_read_sensor_input(struct isl29018_chip *chip, int mode)  {  	int status; -	int lsb; -	int msb; +	unsigned int lsb; +	unsigned int msb;  	/* Set mode */ -	status = isl29018_write_data(client, ISL29018_REG_ADD_COMMAND1, -			mode, COMMMAND1_OPMODE_MASK, COMMMAND1_OPMODE_SHIFT); +	status = regmap_write(chip->regmap, ISL29018_REG_ADD_COMMAND1, +			mode << COMMMAND1_OPMODE_SHIFT);  	if (status) { -		dev_err(&client->dev, "Error in setting operating mode\n"); +		dev_err(chip->dev, +			"Error in setting operating mode err %d\n", status);  		return status;  	}  	msleep(CONVERSION_TIME_MS); -	lsb = i2c_smbus_read_byte_data(client, ISL29018_REG_ADD_DATA_LSB); -	if (lsb < 0) { -		dev_err(&client->dev, "Error in reading LSB DATA\n"); -		return lsb; +	status = regmap_read(chip->regmap, ISL29018_REG_ADD_DATA_LSB, &lsb); +	if (status < 0) { +		dev_err(chip->dev, +			"Error in reading LSB DATA with err %d\n", status); +		return status;  	} -	msb = i2c_smbus_read_byte_data(client, ISL29018_REG_ADD_DATA_MSB); -	if (msb < 0) { -		dev_err(&client->dev, "Error in reading MSB DATA\n"); -		return msb; +	status = regmap_read(chip->regmap, ISL29018_REG_ADD_DATA_MSB, &msb); +	if (status < 0) { +		dev_err(chip->dev, +			"Error in reading MSB DATA with error %d\n", status); +		return status;  	} -	dev_vdbg(&client->dev, "MSB 0x%x and LSB 0x%x\n", msb, lsb); +	dev_vdbg(chip->dev, "MSB 0x%x and LSB 0x%x\n", msb, lsb);  	return (msb << 8) | lsb;  } -static int isl29018_read_lux(struct i2c_client *client, int *lux) +static int isl29018_read_lux(struct isl29018_chip *chip, int *lux)  {  	int lux_data; -	struct isl29018_chip *chip = i2c_get_clientdata(client); +	unsigned int data_x_range, lux_unshifted; -	lux_data = isl29018_read_sensor_input(client, -				COMMMAND1_OPMODE_ALS_ONCE); +	lux_data = isl29018_read_sensor_input(chip, COMMMAND1_OPMODE_ALS_ONCE);  	if (lux_data < 0)  		return lux_data; -	*lux = (lux_data * chip->range) >> chip->adc_bit; +	/* To support fractional scaling, separate the unshifted lux +	 * into two calculations: int scaling and micro-scaling. +	 * lux_uscale ranges from 0-999999, so about 20 bits.  Split +	 * the /1,000,000 in two to reduce the risk of over/underflow. +	 */ +	data_x_range = lux_data * chip->range; +	lux_unshifted = data_x_range * chip->lux_scale; +	lux_unshifted += data_x_range / 1000 * chip->lux_uscale / 1000; +	*lux = lux_unshifted >> chip->adc_bit;  	return 0;  } -static int isl29018_read_ir(struct i2c_client *client, int *ir) +static int isl29018_read_ir(struct isl29018_chip *chip, int *ir)  {  	int ir_data; -	ir_data = isl29018_read_sensor_input(client, COMMMAND1_OPMODE_IR_ONCE); +	ir_data = isl29018_read_sensor_input(chip, COMMMAND1_OPMODE_IR_ONCE);  	if (ir_data < 0)  		return ir_data; @@ -185,7 +181,7 @@ static int isl29018_read_ir(struct i2c_client *client, int *ir)  	return 0;  } -static int isl29018_read_proximity_ir(struct i2c_client *client, int scheme, +static int isl29018_read_proximity_ir(struct isl29018_chip *chip, int scheme,  		int *near_ir)  {  	int status; @@ -193,14 +189,15 @@ static int isl29018_read_proximity_ir(struct i2c_client *client, int scheme,  	int ir_data = -1;  	/* Do proximity sensing with required scheme */ -	status = isl29018_write_data(client, ISL29018_REG_ADD_COMMANDII, -			scheme, COMMANDII_SCHEME_MASK, COMMANDII_SCHEME_SHIFT); +	status = regmap_update_bits(chip->regmap, ISL29018_REG_ADD_COMMANDII, +			COMMANDII_SCHEME_MASK, +			scheme << COMMANDII_SCHEME_SHIFT);  	if (status) { -		dev_err(&client->dev, "Error in setting operating mode\n"); +		dev_err(chip->dev, "Error in setting operating mode\n");  		return status;  	} -	prox_data = isl29018_read_sensor_input(client, +	prox_data = isl29018_read_sensor_input(chip,  					COMMMAND1_OPMODE_PROX_ONCE);  	if (prox_data < 0)  		return prox_data; @@ -210,8 +207,7 @@ static int isl29018_read_proximity_ir(struct i2c_client *client, int scheme,  		return 0;  	} -	ir_data = isl29018_read_sensor_input(client, -				COMMMAND1_OPMODE_IR_ONCE); +	ir_data = isl29018_read_sensor_input(chip, COMMMAND1_OPMODE_IR_ONCE);  	if (ir_data < 0)  		return ir_data; @@ -224,52 +220,13 @@ static int isl29018_read_proximity_ir(struct i2c_client *client, int scheme,  	return 0;  } -static ssize_t get_sensor_data(struct device *dev, char *buf, int mode) -{ -	struct iio_dev *indio_dev = dev_get_drvdata(dev); -	struct isl29018_chip *chip = indio_dev->dev_data; -	struct i2c_client *client = chip->client; -	int value = 0; -	int status; - -	mutex_lock(&chip->lock); -	switch (mode) { -	case COMMMAND1_OPMODE_PROX_ONCE: -		status = isl29018_read_proximity_ir(client, -				chip->prox_scheme, &value); -		break; - -	case COMMMAND1_OPMODE_ALS_ONCE: -		status = isl29018_read_lux(client, &value); -		break; - -	case COMMMAND1_OPMODE_IR_ONCE: -		status = isl29018_read_ir(client, &value); -		break; - -	default: -		dev_err(&client->dev, "Mode %d is not supported\n", mode); -		mutex_unlock(&chip->lock); -		return -EBUSY; -	} -	if (status < 0) { -		dev_err(&client->dev, "Error in Reading data"); -		mutex_unlock(&chip->lock); -		return status; -	} - -	mutex_unlock(&chip->lock); - -	return sprintf(buf, "%d\n", value); -} -  /* Sysfs interface */  /* range */  static ssize_t show_range(struct device *dev,  			struct device_attribute *attr, char *buf)  { -	struct iio_dev *indio_dev = dev_get_drvdata(dev); -	struct isl29018_chip *chip = indio_dev->dev_data; +	struct iio_dev *indio_dev = dev_to_iio_dev(dev); +	struct isl29018_chip *chip = iio_priv(indio_dev);  	return sprintf(buf, "%u\n", chip->range);  } @@ -277,14 +234,13 @@ static ssize_t show_range(struct device *dev,  static ssize_t store_range(struct device *dev,  		struct device_attribute *attr, const char *buf, size_t count)  { -	struct iio_dev *indio_dev = dev_get_drvdata(dev); -	struct isl29018_chip *chip = indio_dev->dev_data; -	struct i2c_client *client = chip->client; +	struct iio_dev *indio_dev = dev_to_iio_dev(dev); +	struct isl29018_chip *chip = iio_priv(indio_dev);  	int status;  	unsigned long lval;  	unsigned int new_range; -	if (strict_strtoul(buf, 10, &lval)) +	if (kstrtoul(buf, 10, &lval))  		return -EINVAL;  	if (!(lval == 1000UL || lval == 4000UL || @@ -294,10 +250,11 @@ static ssize_t store_range(struct device *dev,  	}  	mutex_lock(&chip->lock); -	status = isl29018_set_range(client, lval, &new_range); +	status = isl29018_set_range(chip, lval, &new_range);  	if (status < 0) {  		mutex_unlock(&chip->lock); -		dev_err(dev, "Error in setting max range\n"); +		dev_err(dev, +			"Error in setting max range with err %d\n", status);  		return status;  	}  	chip->range = new_range; @@ -310,8 +267,8 @@ static ssize_t store_range(struct device *dev,  static ssize_t show_resolution(struct device *dev,  			struct device_attribute *attr, char *buf)  { -	struct iio_dev *indio_dev = dev_get_drvdata(dev); -	struct isl29018_chip *chip = indio_dev->dev_data; +	struct iio_dev *indio_dev = dev_to_iio_dev(dev); +	struct isl29018_chip *chip = iio_priv(indio_dev);  	return sprintf(buf, "%u\n", chip->adc_bit);  } @@ -319,22 +276,21 @@ static ssize_t show_resolution(struct device *dev,  static ssize_t store_resolution(struct device *dev,  		struct device_attribute *attr, const char *buf, size_t count)  { -	struct iio_dev *indio_dev = dev_get_drvdata(dev); -	struct isl29018_chip *chip = indio_dev->dev_data; -	struct i2c_client *client = chip->client; +	struct iio_dev *indio_dev = dev_to_iio_dev(dev); +	struct isl29018_chip *chip = iio_priv(indio_dev);  	int status; -	unsigned long lval; +	unsigned int val;  	unsigned int new_adc_bit; -	if (strict_strtoul(buf, 10, &lval)) +	if (kstrtouint(buf, 10, &val))  		return -EINVAL; -	if (!(lval == 4 || lval == 8 || lval == 12 || lval == 16)) { +	if (!(val == 4 || val == 8 || val == 12 || val == 16)) {  		dev_err(dev, "The resolution is not supported\n");  		return -EINVAL;  	}  	mutex_lock(&chip->lock); -	status = isl29018_set_resolution(client, lval, &new_adc_bit); +	status = isl29018_set_resolution(chip, val, &new_adc_bit);  	if (status < 0) {  		mutex_unlock(&chip->lock);  		dev_err(dev, "Error in setting resolution\n"); @@ -347,97 +303,147 @@ static ssize_t store_resolution(struct device *dev,  }  /* proximity scheme */ -static ssize_t show_prox_infrared_supression(struct device *dev, +static ssize_t show_prox_infrared_suppression(struct device *dev,  			struct device_attribute *attr, char *buf)  { -	struct iio_dev *indio_dev = dev_get_drvdata(dev); -	struct isl29018_chip *chip = indio_dev->dev_data; +	struct iio_dev *indio_dev = dev_to_iio_dev(dev); +	struct isl29018_chip *chip = iio_priv(indio_dev);  	/* return the "proximity scheme" i.e. if the chip does on chip -	infrared supression (1 means perform on chip supression) */ +	infrared suppression (1 means perform on chip suppression) */  	return sprintf(buf, "%d\n", chip->prox_scheme);  } -static ssize_t store_prox_infrared_supression(struct device *dev, +static ssize_t store_prox_infrared_suppression(struct device *dev,  		struct device_attribute *attr, const char *buf, size_t count)  { -	struct iio_dev *indio_dev = dev_get_drvdata(dev); -	struct isl29018_chip *chip = indio_dev->dev_data; -	unsigned long lval; +	struct iio_dev *indio_dev = dev_to_iio_dev(dev); +	struct isl29018_chip *chip = iio_priv(indio_dev); +	int val; -	if (strict_strtoul(buf, 10, &lval)) +	if (kstrtoint(buf, 10, &val))  		return -EINVAL; -	if (!(lval == 0UL || lval == 1UL)) { +	if (!(val == 0 || val == 1)) {  		dev_err(dev, "The mode is not supported\n");  		return -EINVAL;  	}  	/* get the  "proximity scheme" i.e. if the chip does on chip -	infrared supression (1 means perform on chip supression) */ +	infrared suppression (1 means perform on chip suppression) */  	mutex_lock(&chip->lock); -	chip->prox_scheme = (int)lval; +	chip->prox_scheme = val;  	mutex_unlock(&chip->lock);  	return count;  } -/* Read lux */ -static ssize_t show_lux(struct device *dev, -		struct device_attribute *devattr, char *buf) +/* Channel IO */ +static int isl29018_write_raw(struct iio_dev *indio_dev, +			      struct iio_chan_spec const *chan, +			      int val, +			      int val2, +			      long mask)  { -	return get_sensor_data(dev, buf, COMMMAND1_OPMODE_ALS_ONCE); -} +	struct isl29018_chip *chip = iio_priv(indio_dev); +	int ret = -EINVAL; -/* Read ir */ -static ssize_t show_ir(struct device *dev, -		struct device_attribute *devattr, char *buf) -{ -	return get_sensor_data(dev, buf, COMMMAND1_OPMODE_IR_ONCE); -} +	mutex_lock(&chip->lock); +	if (mask == IIO_CHAN_INFO_CALIBSCALE && chan->type == IIO_LIGHT) { +		chip->lux_scale = val; +		/* With no write_raw_get_fmt(), val2 is a MICRO fraction. */ +		chip->lux_uscale = val2; +		ret = 0; +	} +	mutex_unlock(&chip->lock); -/* Read nearest ir */ -static ssize_t show_proxim_ir(struct device *dev, -		struct device_attribute *devattr, char *buf) -{ -	return get_sensor_data(dev, buf, COMMMAND1_OPMODE_PROX_ONCE); +	return ret;  } -/* Read name */ -static ssize_t show_name(struct device *dev, -		struct device_attribute *attr, char *buf) +static int isl29018_read_raw(struct iio_dev *indio_dev, +			     struct iio_chan_spec const *chan, +			     int *val, +			     int *val2, +			     long mask)  { -	struct iio_dev *indio_dev = dev_get_drvdata(dev); -	struct isl29018_chip *chip = indio_dev->dev_data; +	int ret = -EINVAL; +	struct isl29018_chip *chip = iio_priv(indio_dev); -	return sprintf(buf, "%s\n", chip->client->name); +	mutex_lock(&chip->lock); +	if (chip->suspended) { +		mutex_unlock(&chip->lock); +		return -EBUSY; +	} +	switch (mask) { +	case IIO_CHAN_INFO_RAW: +	case IIO_CHAN_INFO_PROCESSED: +		switch (chan->type) { +		case IIO_LIGHT: +			ret = isl29018_read_lux(chip, val); +			break; +		case IIO_INTENSITY: +			ret = isl29018_read_ir(chip, val); +			break; +		case IIO_PROXIMITY: +			ret = isl29018_read_proximity_ir(chip, +					chip->prox_scheme, val); +			break; +		default: +			break; +		} +		if (!ret) +			ret = IIO_VAL_INT; +		break; +	case IIO_CHAN_INFO_CALIBSCALE: +		if (chan->type == IIO_LIGHT) { +			*val = chip->lux_scale; +			*val2 = chip->lux_uscale; +			ret = IIO_VAL_INT_PLUS_MICRO; +		} +		break; +	default: +		break; +	} +	mutex_unlock(&chip->lock); +	return ret;  } +static const struct iio_chan_spec isl29018_channels[] = { +	{ +		.type = IIO_LIGHT, +		.indexed = 1, +		.channel = 0, +		.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED) | +		BIT(IIO_CHAN_INFO_CALIBSCALE), +	}, { +		.type = IIO_INTENSITY, +		.modified = 1, +		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), +		.channel2 = IIO_MOD_LIGHT_IR, +	}, { +		/* Unindexed in current ABI.  But perhaps it should be. */ +		.type = IIO_PROXIMITY, +		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), +	} +}; +  static IIO_DEVICE_ATTR(range, S_IRUGO | S_IWUSR, show_range, store_range, 0);  static IIO_CONST_ATTR(range_available, "1000 4000 16000 64000");  static IIO_CONST_ATTR(adc_resolution_available, "4 8 12 16");  static IIO_DEVICE_ATTR(adc_resolution, S_IRUGO | S_IWUSR,  					show_resolution, store_resolution, 0); -static IIO_DEVICE_ATTR(proximity_on_chip_ambient_infrared_supression, +static IIO_DEVICE_ATTR(proximity_on_chip_ambient_infrared_suppression,  					S_IRUGO | S_IWUSR, -					show_prox_infrared_supression, -					store_prox_infrared_supression, 0); -static IIO_DEVICE_ATTR(illuminance0_input, S_IRUGO, show_lux, NULL, 0); -static IIO_DEVICE_ATTR(intensity_infrared_raw, S_IRUGO, show_ir, NULL, 0); -static IIO_DEVICE_ATTR(proximity_raw, S_IRUGO, show_proxim_ir, NULL, 0); -static IIO_DEVICE_ATTR(name, S_IRUGO, show_name, NULL, 0); +					show_prox_infrared_suppression, +					store_prox_infrared_suppression, 0);  #define ISL29018_DEV_ATTR(name) (&iio_dev_attr_##name.dev_attr.attr)  #define ISL29018_CONST_ATTR(name) (&iio_const_attr_##name.dev_attr.attr)  static struct attribute *isl29018_attributes[] = { -	ISL29018_DEV_ATTR(name),  	ISL29018_DEV_ATTR(range),  	ISL29018_CONST_ATTR(range_available),  	ISL29018_DEV_ATTR(adc_resolution),  	ISL29018_CONST_ATTR(adc_resolution_available), -	ISL29018_DEV_ATTR(proximity_on_chip_ambient_infrared_supression), -	ISL29018_DEV_ATTR(illuminance0_input), -	ISL29018_DEV_ATTR(intensity_infrared_raw), -	ISL29018_DEV_ATTR(proximity_raw), +	ISL29018_DEV_ATTR(proximity_on_chip_ambient_infrared_suppression),  	NULL  }; @@ -445,89 +451,187 @@ static const struct attribute_group isl29108_group = {  	.attrs = isl29018_attributes,  }; -static int isl29018_chip_init(struct i2c_client *client) +static int isl29018_chip_init(struct isl29018_chip *chip)  { -	struct isl29018_chip *chip = i2c_get_clientdata(client);  	int status;  	int new_adc_bit;  	unsigned int new_range; -	memset(chip->reg_cache, 0, sizeof(chip->reg_cache)); +	/* Code added per Intersil Application Note 1534: +	 *     When VDD sinks to approximately 1.8V or below, some of +	 * the part's registers may change their state. When VDD +	 * recovers to 2.25V (or greater), the part may thus be in an +	 * unknown mode of operation. The user can return the part to +	 * a known mode of operation either by (a) setting VDD = 0V for +	 * 1 second or more and then powering back up with a slew rate +	 * of 0.5V/ms or greater, or (b) via I2C disable all ALS/PROX +	 * conversions, clear the test registers, and then rewrite all +	 * registers to the desired values. +	 * ... +	 * FOR ISL29011, ISL29018, ISL29021, ISL29023 +	 * 1. Write 0x00 to register 0x08 (TEST) +	 * 2. Write 0x00 to register 0x00 (CMD1) +	 * 3. Rewrite all registers to the desired values +	 * +	 * ISL29018 Data Sheet (FN6619.1, Feb 11, 2010) essentially says +	 * the same thing EXCEPT the data sheet asks for a 1ms delay after +	 * writing the CMD1 register. +	 */ +	status = regmap_write(chip->regmap, ISL29018_REG_TEST, 0x0); +	if (status < 0) { +		dev_err(chip->dev, "Failed to clear isl29018 TEST reg." +					"(%d)\n", status); +		return status; +	} + +	/* See Intersil AN1534 comments above. +	 * "Operating Mode" (COMMAND1) register is reprogrammed when +	 * data is read from the device. +	 */ +	status = regmap_write(chip->regmap, ISL29018_REG_ADD_COMMAND1, 0); +	if (status < 0) { +		dev_err(chip->dev, "Failed to clear isl29018 CMD1 reg." +					"(%d)\n", status); +		return status; +	} + +	msleep(1);	/* per data sheet, page 10 */  	/* set defaults */ -	status = isl29018_set_range(client, chip->range, &new_range); +	status = isl29018_set_range(chip, chip->range, &new_range);  	if (status < 0) { -		dev_err(&client->dev, "Init of isl29018 fails\n"); +		dev_err(chip->dev, "Init of isl29018 fails\n");  		return status;  	} -	status = isl29018_set_resolution(client, chip->adc_bit, +	status = isl29018_set_resolution(chip, chip->adc_bit,  						&new_adc_bit);  	return 0;  } -static int __devinit isl29018_probe(struct i2c_client *client, +static const struct iio_info isl29108_info = { +	.attrs = &isl29108_group, +	.driver_module = THIS_MODULE, +	.read_raw = &isl29018_read_raw, +	.write_raw = &isl29018_write_raw, +}; + +static bool is_volatile_reg(struct device *dev, unsigned int reg) +{ +	switch (reg) { +	case ISL29018_REG_ADD_DATA_LSB: +	case ISL29018_REG_ADD_DATA_MSB: +	case ISL29018_REG_ADD_COMMAND1: +	case ISL29018_REG_TEST: +		return true; +	default: +		return false; +	} +} + +/* + * isl29018_regmap_config: regmap configuration. + * Use RBTREE mechanism for caching. + */ +static const struct regmap_config isl29018_regmap_config = { +	.reg_bits = 8, +	.val_bits = 8, +	.volatile_reg = is_volatile_reg, +	.max_register = ISL29018_REG_TEST, +	.num_reg_defaults_raw = ISL29018_REG_TEST + 1, +	.cache_type = REGCACHE_RBTREE, +}; + +static int isl29018_probe(struct i2c_client *client,  			 const struct i2c_device_id *id)  {  	struct isl29018_chip *chip; +	struct iio_dev *indio_dev;  	int err; -	chip = kzalloc(sizeof(struct isl29018_chip), GFP_KERNEL); -	if (!chip) { -		dev_err(&client->dev, "Memory allocation fails\n"); -		err = -ENOMEM; -		goto exit; +	indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*chip)); +	if (indio_dev == NULL) { +		dev_err(&client->dev, "iio allocation fails\n"); +		return -ENOMEM;  	} +	chip = iio_priv(indio_dev); -	i2c_set_clientdata(client, chip); -	chip->client = client; +	i2c_set_clientdata(client, indio_dev); +	chip->dev = &client->dev;  	mutex_init(&chip->lock); +	chip->lux_scale = 1; +	chip->lux_uscale = 0;  	chip->range = 1000;  	chip->adc_bit = 16; +	chip->suspended = false; -	err = isl29018_chip_init(client); -	if (err) -		goto exit_free; - -	chip->indio_dev = iio_allocate_device(); -	if (!chip->indio_dev) { -		dev_err(&client->dev, "iio allocation fails\n"); -		goto exit_free; +	chip->regmap = devm_regmap_init_i2c(client, &isl29018_regmap_config); +	if (IS_ERR(chip->regmap)) { +		err = PTR_ERR(chip->regmap); +		dev_err(chip->dev, "regmap initialization failed: %d\n", err); +		return err;  	} -	chip->indio_dev->attrs = &isl29108_group; -	chip->indio_dev->dev.parent = &client->dev; -	chip->indio_dev->dev_data = (void *)(chip); -	chip->indio_dev->driver_module = THIS_MODULE; -	chip->indio_dev->modes = INDIO_DIRECT_MODE; -	err = iio_device_register(chip->indio_dev); + +	err = isl29018_chip_init(chip); +	if (err) +		return err; + +	indio_dev->info = &isl29108_info; +	indio_dev->channels = isl29018_channels; +	indio_dev->num_channels = ARRAY_SIZE(isl29018_channels); +	indio_dev->name = id->name; +	indio_dev->dev.parent = &client->dev; +	indio_dev->modes = INDIO_DIRECT_MODE; +	err = devm_iio_device_register(&client->dev, indio_dev);  	if (err) {  		dev_err(&client->dev, "iio registration fails\n"); -		goto exit_iio_free; +		return err;  	}  	return 0; -exit_iio_free: -	iio_free_device(chip->indio_dev); -exit_free: -	kfree(chip); -exit: -	return err;  } -static int __devexit isl29018_remove(struct i2c_client *client) +#ifdef CONFIG_PM_SLEEP +static int isl29018_suspend(struct device *dev)  { -	struct isl29018_chip *chip = i2c_get_clientdata(client); +	struct isl29018_chip *chip = iio_priv(dev_get_drvdata(dev)); -	dev_dbg(&client->dev, "%s()\n", __func__); -	iio_device_unregister(chip->indio_dev); -	kfree(chip); +	mutex_lock(&chip->lock); + +	/* Since this driver uses only polling commands, we are by default in +	 * auto shutdown (ie, power-down) mode. +	 * So we do not have much to do here. +	 */ +	chip->suspended = true; +	mutex_unlock(&chip->lock);  	return 0;  } +static int isl29018_resume(struct device *dev) +{ +	struct isl29018_chip *chip = iio_priv(dev_get_drvdata(dev)); +	int err; + +	mutex_lock(&chip->lock); + +	err = isl29018_chip_init(chip); +	if (!err) +		chip->suspended = false; + +	mutex_unlock(&chip->lock); +	return err; +} + +static SIMPLE_DEV_PM_OPS(isl29018_pm_ops, isl29018_suspend, isl29018_resume); +#define ISL29018_PM_OPS (&isl29018_pm_ops) +#else +#define ISL29018_PM_OPS NULL +#endif +  static const struct i2c_device_id isl29018_id[] = {  	{"isl29018", 0},  	{} @@ -535,29 +639,24 @@ static const struct i2c_device_id isl29018_id[] = {  MODULE_DEVICE_TABLE(i2c, isl29018_id); +static const struct of_device_id isl29018_of_match[] = { +	{ .compatible = "isil,isl29018", }, +	{ }, +}; +MODULE_DEVICE_TABLE(of, isl29018_of_match); +  static struct i2c_driver isl29018_driver = {  	.class	= I2C_CLASS_HWMON,  	.driver	 = {  			.name = "isl29018", +			.pm = ISL29018_PM_OPS,  			.owner = THIS_MODULE, +			.of_match_table = isl29018_of_match,  		    },  	.probe	 = isl29018_probe, -	.remove	 = __devexit_p(isl29018_remove),  	.id_table = isl29018_id,  }; - -static int __init isl29018_init(void) -{ -	return i2c_add_driver(&isl29018_driver); -} - -static void __exit isl29018_exit(void) -{ -	i2c_del_driver(&isl29018_driver); -} - -module_init(isl29018_init); -module_exit(isl29018_exit); +module_i2c_driver(isl29018_driver);  MODULE_DESCRIPTION("ISL29018 Ambient Light Sensor driver");  MODULE_LICENSE("GPL"); diff --git a/drivers/staging/iio/light/isl29028.c b/drivers/staging/iio/light/isl29028.c new file mode 100644 index 00000000000..6014625920b --- /dev/null +++ b/drivers/staging/iio/light/isl29028.c @@ -0,0 +1,561 @@ +/* + * IIO driver for the light sensor ISL29028. + * ISL29028 is Concurrent Ambient Light and Proximity Sensor + * + * Copyright (c) 2012, NVIDIA CORPORATION.  All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program.  If not, see <http://www.gnu.org/licenses/>. + */ + +#include <linux/module.h> +#include <linux/i2c.h> +#include <linux/err.h> +#include <linux/mutex.h> +#include <linux/delay.h> +#include <linux/slab.h> +#include <linux/regmap.h> +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> + +#define CONVERSION_TIME_MS		100 + +#define ISL29028_REG_CONFIGURE		0x01 + +#define CONFIGURE_ALS_IR_MODE_ALS	0 +#define CONFIGURE_ALS_IR_MODE_IR	BIT(0) +#define CONFIGURE_ALS_IR_MODE_MASK	BIT(0) + +#define CONFIGURE_ALS_RANGE_LOW_LUX	0 +#define CONFIGURE_ALS_RANGE_HIGH_LUX	BIT(1) +#define CONFIGURE_ALS_RANGE_MASK	BIT(1) + +#define CONFIGURE_ALS_DIS		0 +#define CONFIGURE_ALS_EN		BIT(2) +#define CONFIGURE_ALS_EN_MASK		BIT(2) + +#define CONFIGURE_PROX_DRIVE		BIT(3) + +#define CONFIGURE_PROX_SLP_SH		4 +#define CONFIGURE_PROX_SLP_MASK		(7 << CONFIGURE_PROX_SLP_SH) + +#define CONFIGURE_PROX_EN		BIT(7) +#define CONFIGURE_PROX_EN_MASK		BIT(7) + +#define ISL29028_REG_INTERRUPT		0x02 + +#define ISL29028_REG_PROX_DATA		0x08 +#define ISL29028_REG_ALSIR_L		0x09 +#define ISL29028_REG_ALSIR_U		0x0A + +#define ISL29028_REG_TEST1_MODE		0x0E +#define ISL29028_REG_TEST2_MODE		0x0F + +#define ISL29028_NUM_REGS		(ISL29028_REG_TEST2_MODE + 1) + +enum als_ir_mode { +	MODE_NONE = 0, +	MODE_ALS, +	MODE_IR +}; + +struct isl29028_chip { +	struct device		*dev; +	struct mutex		lock; +	struct regmap		*regmap; + +	unsigned int		prox_sampling; +	bool			enable_prox; + +	int			lux_scale; +	int			als_ir_mode; +}; + +static int isl29028_set_proxim_sampling(struct isl29028_chip *chip, +			unsigned int sampling) +{ +	static unsigned int prox_period[] = {800, 400, 200, 100, 75, 50, 12, 0}; +	int sel; +	unsigned int period = DIV_ROUND_UP(1000, sampling); + +	for (sel = 0; sel < ARRAY_SIZE(prox_period); ++sel) { +		if (period >= prox_period[sel]) +			break; +	} +	return regmap_update_bits(chip->regmap, ISL29028_REG_CONFIGURE, +			CONFIGURE_PROX_SLP_MASK, sel << CONFIGURE_PROX_SLP_SH); +} + +static int isl29028_enable_proximity(struct isl29028_chip *chip, bool enable) +{ +	int ret; +	int val = 0; + +	if (enable) +		val = CONFIGURE_PROX_EN; +	ret = regmap_update_bits(chip->regmap, ISL29028_REG_CONFIGURE, +			CONFIGURE_PROX_EN_MASK, val); +	if (ret < 0) +		return ret; + +	/* Wait for conversion to be complete for first sample */ +	mdelay(DIV_ROUND_UP(1000, chip->prox_sampling)); +	return 0; +} + +static int isl29028_set_als_scale(struct isl29028_chip *chip, int lux_scale) +{ +	int val = (lux_scale == 2000) ? CONFIGURE_ALS_RANGE_HIGH_LUX : +					CONFIGURE_ALS_RANGE_LOW_LUX; + +	return regmap_update_bits(chip->regmap, ISL29028_REG_CONFIGURE, +		CONFIGURE_ALS_RANGE_MASK, val); +} + +static int isl29028_set_als_ir_mode(struct isl29028_chip *chip, +	enum als_ir_mode mode) +{ +	int ret = 0; + +	switch (mode) { +	case MODE_ALS: +		ret = regmap_update_bits(chip->regmap, ISL29028_REG_CONFIGURE, +			CONFIGURE_ALS_IR_MODE_MASK, CONFIGURE_ALS_IR_MODE_ALS); +		if (ret < 0) +			return ret; + +		ret = regmap_update_bits(chip->regmap, ISL29028_REG_CONFIGURE, +			CONFIGURE_ALS_RANGE_MASK, CONFIGURE_ALS_RANGE_HIGH_LUX); +		break; + +	case MODE_IR: +		ret = regmap_update_bits(chip->regmap, ISL29028_REG_CONFIGURE, +			CONFIGURE_ALS_IR_MODE_MASK, CONFIGURE_ALS_IR_MODE_IR); +		break; + +	case MODE_NONE: +		return regmap_update_bits(chip->regmap, ISL29028_REG_CONFIGURE, +			CONFIGURE_ALS_EN_MASK, CONFIGURE_ALS_DIS); +	} + +	if (ret < 0) +		return ret; + +	/* Enable the ALS/IR */ +	ret = regmap_update_bits(chip->regmap, ISL29028_REG_CONFIGURE, +			CONFIGURE_ALS_EN_MASK, CONFIGURE_ALS_EN); +	if (ret < 0) +		return ret; + +	/* Need to wait for conversion time if ALS/IR mode enabled */ +	mdelay(CONVERSION_TIME_MS); +	return 0; +} + +static int isl29028_read_als_ir(struct isl29028_chip *chip, int *als_ir) +{ +	unsigned int lsb; +	unsigned int msb; +	int ret; + +	ret = regmap_read(chip->regmap, ISL29028_REG_ALSIR_L, &lsb); +	if (ret < 0) { +		dev_err(chip->dev, +			"Error in reading register ALSIR_L err %d\n", ret); +		return ret; +	} + +	ret = regmap_read(chip->regmap, ISL29028_REG_ALSIR_U, &msb); +	if (ret < 0) { +		dev_err(chip->dev, +			"Error in reading register ALSIR_U err %d\n", ret); +		return ret; +	} + +	*als_ir = ((msb & 0xF) << 8) | (lsb & 0xFF); +	return 0; +} + +static int isl29028_read_proxim(struct isl29028_chip *chip, int *prox) +{ +	unsigned int data; +	int ret; + +	ret = regmap_read(chip->regmap, ISL29028_REG_PROX_DATA, &data); +	if (ret < 0) { +		dev_err(chip->dev, "Error in reading register %d, error %d\n", +				ISL29028_REG_PROX_DATA, ret); +		return ret; +	} +	*prox = data; +	return 0; +} + +static int isl29028_proxim_get(struct isl29028_chip *chip, int *prox_data) +{ +	int ret; + +	if (!chip->enable_prox) { +		ret = isl29028_enable_proximity(chip, true); +		if (ret < 0) +			return ret; +		chip->enable_prox = true; +	} +	return isl29028_read_proxim(chip, prox_data); +} + +static int isl29028_als_get(struct isl29028_chip *chip, int *als_data) +{ +	int ret; +	int als_ir_data; + +	if (chip->als_ir_mode != MODE_ALS) { +		ret = isl29028_set_als_ir_mode(chip, MODE_ALS); +		if (ret < 0) { +			dev_err(chip->dev, +				"Error in enabling ALS mode err %d\n", ret); +			return ret; +		} +		chip->als_ir_mode = MODE_ALS; +	} + +	ret = isl29028_read_als_ir(chip, &als_ir_data); +	if (ret < 0) +		return ret; + +	/* +	 * convert als data count to lux. +	 * if lux_scale = 125,  lux = count * 0.031 +	 * if lux_scale = 2000, lux = count * 0.49 +	 */ +	if (chip->lux_scale == 125) +		als_ir_data = (als_ir_data * 31) / 1000; +	else +		als_ir_data = (als_ir_data * 49) / 100; + +	*als_data = als_ir_data; +	return 0; +} + +static int isl29028_ir_get(struct isl29028_chip *chip, int *ir_data) +{ +	int ret; + +	if (chip->als_ir_mode != MODE_IR) { +		ret = isl29028_set_als_ir_mode(chip, MODE_IR); +		if (ret < 0) { +			dev_err(chip->dev, +				"Error in enabling IR mode err %d\n", ret); +			return ret; +		} +		chip->als_ir_mode = MODE_IR; +	} +	return isl29028_read_als_ir(chip, ir_data); +} + +/* Channel IO */ +static int isl29028_write_raw(struct iio_dev *indio_dev, +	     struct iio_chan_spec const *chan, int val, int val2, long mask) +{ +	struct isl29028_chip *chip = iio_priv(indio_dev); +	int ret = -EINVAL; + +	mutex_lock(&chip->lock); +	switch (chan->type) { +	case IIO_PROXIMITY: +		if (mask != IIO_CHAN_INFO_SAMP_FREQ) { +			dev_err(chip->dev, +				"proximity: mask value 0x%08lx not supported\n", +				mask); +			break; +		} +		if (val < 1 || val > 100) { +			dev_err(chip->dev, +				"Samp_freq %d is not in range[1:100]\n", val); +			break; +		} +		ret = isl29028_set_proxim_sampling(chip, val); +		if (ret < 0) { +			dev_err(chip->dev, +				"Setting proximity samp_freq fail, err %d\n", +				ret); +			break; +		} +		chip->prox_sampling = val; +		break; + +	case IIO_LIGHT: +		if (mask != IIO_CHAN_INFO_SCALE) { +			dev_err(chip->dev, +				"light: mask value 0x%08lx not supported\n", +				mask); +			break; +		} +		if ((val != 125) && (val != 2000)) { +			dev_err(chip->dev, +				"lux scale %d is invalid [125, 2000]\n", val); +			break; +		} +		ret = isl29028_set_als_scale(chip, val); +		if (ret < 0) { +			dev_err(chip->dev, +				"Setting lux scale fail with error %d\n", ret); +			break; +		} +		chip->lux_scale = val; +		break; + +	default: +		dev_err(chip->dev, "Unsupported channel type\n"); +		break; +	} +	mutex_unlock(&chip->lock); +	return ret; +} + +static int isl29028_read_raw(struct iio_dev *indio_dev, +	     struct iio_chan_spec const *chan, int *val, int *val2, long mask) +{ +	struct isl29028_chip *chip = iio_priv(indio_dev); +	int ret = -EINVAL; + +	mutex_lock(&chip->lock); +	switch (mask) { +	case IIO_CHAN_INFO_RAW: +	case IIO_CHAN_INFO_PROCESSED: +		switch (chan->type) { +		case IIO_LIGHT: +			ret = isl29028_als_get(chip, val); +			break; +		case IIO_INTENSITY: +			ret = isl29028_ir_get(chip, val); +			break; +		case IIO_PROXIMITY: +			ret = isl29028_proxim_get(chip, val); +			break; +		default: +			break; +		} +		if (ret < 0) +			break; +		ret = IIO_VAL_INT; +		break; + +	case IIO_CHAN_INFO_SAMP_FREQ: +		if (chan->type != IIO_PROXIMITY) +			break; +		*val = chip->prox_sampling; +		ret = IIO_VAL_INT; +		break; + +	case IIO_CHAN_INFO_SCALE: +		if (chan->type != IIO_LIGHT) +			break; +		*val = chip->lux_scale; +		ret = IIO_VAL_INT; +		break; + +	default: +		dev_err(chip->dev, "mask value 0x%08lx not supported\n", mask); +		break; +	} +	mutex_unlock(&chip->lock); +	return ret; +} + +static IIO_CONST_ATTR(in_proximity_sampling_frequency_available, +				"1, 3, 5, 10, 13, 20, 83, 100"); +static IIO_CONST_ATTR(in_illuminance_scale_available, "125, 2000"); + +#define ISL29028_DEV_ATTR(name) (&iio_dev_attr_##name.dev_attr.attr) +#define ISL29028_CONST_ATTR(name) (&iio_const_attr_##name.dev_attr.attr) +static struct attribute *isl29028_attributes[] = { +	ISL29028_CONST_ATTR(in_proximity_sampling_frequency_available), +	ISL29028_CONST_ATTR(in_illuminance_scale_available), +	NULL, +}; + +static const struct attribute_group isl29108_group = { +	.attrs = isl29028_attributes, +}; + +static const struct iio_chan_spec isl29028_channels[] = { +	{ +		.type = IIO_LIGHT, +		.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED) | +		BIT(IIO_CHAN_INFO_SCALE), +	}, { +		.type = IIO_INTENSITY, +		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), +	}, { +		.type = IIO_PROXIMITY, +		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | +		BIT(IIO_CHAN_INFO_SAMP_FREQ), +	} +}; + +static const struct iio_info isl29028_info = { +	.attrs = &isl29108_group, +	.driver_module = THIS_MODULE, +	.read_raw = &isl29028_read_raw, +	.write_raw = &isl29028_write_raw, +}; + +static int isl29028_chip_init(struct isl29028_chip *chip) +{ +	int ret; + +	chip->enable_prox  = false; +	chip->prox_sampling = 20; +	chip->lux_scale = 2000; +	chip->als_ir_mode = MODE_NONE; + +	ret = regmap_write(chip->regmap, ISL29028_REG_TEST1_MODE, 0x0); +	if (ret < 0) { +		dev_err(chip->dev, "%s(): write to reg %d failed, err = %d\n", +			__func__, ISL29028_REG_TEST1_MODE, ret); +		return ret; +	} +	ret = regmap_write(chip->regmap, ISL29028_REG_TEST2_MODE, 0x0); +	if (ret < 0) { +		dev_err(chip->dev, "%s(): write to reg %d failed, err = %d\n", +			__func__, ISL29028_REG_TEST2_MODE, ret); +		return ret; +	} + +	ret = regmap_write(chip->regmap, ISL29028_REG_CONFIGURE, 0x0); +	if (ret < 0) { +		dev_err(chip->dev, "%s(): write to reg %d failed, err = %d\n", +			__func__, ISL29028_REG_CONFIGURE, ret); +		return ret; +	} + +	ret = isl29028_set_proxim_sampling(chip, chip->prox_sampling); +	if (ret < 0) { +		dev_err(chip->dev, "%s(): setting the proximity, err = %d\n", +			__func__, ret); +		return ret; +	} + +	ret = isl29028_set_als_scale(chip, chip->lux_scale); +	if (ret < 0) +		dev_err(chip->dev, "%s(): setting als scale failed, err = %d\n", +			__func__, ret); +	return ret; +} + +static bool is_volatile_reg(struct device *dev, unsigned int reg) +{ +	switch (reg) { +	case ISL29028_REG_INTERRUPT: +	case ISL29028_REG_PROX_DATA: +	case ISL29028_REG_ALSIR_L: +	case ISL29028_REG_ALSIR_U: +		return true; +	default: +		return false; +	} +} + +static const struct regmap_config isl29028_regmap_config = { +	.reg_bits = 8, +	.val_bits = 8, +	.volatile_reg = is_volatile_reg, +	.max_register = ISL29028_NUM_REGS - 1, +	.num_reg_defaults_raw = ISL29028_NUM_REGS, +	.cache_type = REGCACHE_RBTREE, +}; + +static int isl29028_probe(struct i2c_client *client, +	const struct i2c_device_id *id) +{ +	struct isl29028_chip *chip; +	struct iio_dev *indio_dev; +	int ret; + +	indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*chip)); +	if (!indio_dev) { +		dev_err(&client->dev, "iio allocation fails\n"); +		return -ENOMEM; +	} + +	chip = iio_priv(indio_dev); + +	i2c_set_clientdata(client, indio_dev); +	chip->dev = &client->dev; +	mutex_init(&chip->lock); + +	chip->regmap = devm_regmap_init_i2c(client, &isl29028_regmap_config); +	if (IS_ERR(chip->regmap)) { +		ret = PTR_ERR(chip->regmap); +		dev_err(chip->dev, "regmap initialization failed: %d\n", ret); +		return ret; +	} + +	ret = isl29028_chip_init(chip); +	if (ret < 0) { +		dev_err(chip->dev, "chip initialization failed: %d\n", ret); +		return ret; +	} + +	indio_dev->info = &isl29028_info; +	indio_dev->channels = isl29028_channels; +	indio_dev->num_channels = ARRAY_SIZE(isl29028_channels); +	indio_dev->name = id->name; +	indio_dev->dev.parent = &client->dev; +	indio_dev->modes = INDIO_DIRECT_MODE; +	ret = iio_device_register(indio_dev); +	if (ret < 0) { +		dev_err(chip->dev, "iio registration fails with error %d\n", +			ret); +		return ret; +	} +	return 0; +} + +static int isl29028_remove(struct i2c_client *client) +{ +	struct iio_dev *indio_dev = i2c_get_clientdata(client); + +	iio_device_unregister(indio_dev); +	return 0; +} + +static const struct i2c_device_id isl29028_id[] = { +	{"isl29028", 0}, +	{} +}; +MODULE_DEVICE_TABLE(i2c, isl29028_id); + +static const struct of_device_id isl29028_of_match[] = { +	{ .compatible = "isil,isl29028", }, +	{ }, +}; +MODULE_DEVICE_TABLE(of, isl29028_of_match); + +static struct i2c_driver isl29028_driver = { +	.class	= I2C_CLASS_HWMON, +	.driver  = { +		.name = "isl29028", +		.owner = THIS_MODULE, +		.of_match_table = isl29028_of_match, +	}, +	.probe	 = isl29028_probe, +	.remove  = isl29028_remove, +	.id_table = isl29028_id, +}; + +module_i2c_driver(isl29028_driver); + +MODULE_DESCRIPTION("ISL29028 Ambient Light and Proximity Sensor driver"); +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Laxman Dewangan <ldewangan@nvidia.com>"); diff --git a/drivers/staging/iio/light/tsl2563.c b/drivers/staging/iio/light/tsl2563.c deleted file mode 100644 index dadae7527d5..00000000000 --- a/drivers/staging/iio/light/tsl2563.c +++ /dev/null @@ -1,998 +0,0 @@ -/* - * drivers/i2c/chips/tsl2563.c - * - * Copyright (C) 2008 Nokia Corporation - * - * Written by Timo O. Karjalainen <timo.o.karjalainen@nokia.com> - * Contact: Amit Kucheria <amit.kucheria@verdurent.com> - * - * Converted to IIO driver - * Amit Kucheria <amit.kucheria@verdurent.com> - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA - */ - -#include <linux/module.h> -#include <linux/i2c.h> -#include <linux/interrupt.h> -#include <linux/irq.h> -#include <linux/sched.h> -#include <linux/mutex.h> -#include <linux/delay.h> -#include <linux/platform_device.h> -#include <linux/pm.h> -#include <linux/hwmon.h> -#include <linux/err.h> -#include <linux/slab.h> - -#include "../iio.h" -#include "tsl2563.h" - -/* Use this many bits for fraction part. */ -#define ADC_FRAC_BITS		(14) - -/* Given number of 1/10000's in ADC_FRAC_BITS precision. */ -#define FRAC10K(f)		(((f) * (1L << (ADC_FRAC_BITS))) / (10000)) - -/* Bits used for fraction in calibration coefficients.*/ -#define CALIB_FRAC_BITS		(10) -/* 0.5 in CALIB_FRAC_BITS precision */ -#define CALIB_FRAC_HALF		(1 << (CALIB_FRAC_BITS - 1)) -/* Make a fraction from a number n that was multiplied with b. */ -#define CALIB_FRAC(n, b)	(((n) << CALIB_FRAC_BITS) / (b)) -/* Decimal 10^(digits in sysfs presentation) */ -#define CALIB_BASE_SYSFS	(1000) - -#define TSL2563_CMD		(0x80) -#define TSL2563_CLEARINT	(0x40) - -#define TSL2563_REG_CTRL	(0x00) -#define TSL2563_REG_TIMING	(0x01) -#define TSL2563_REG_LOWLOW	(0x02) /* data0 low threshold, 2 bytes */ -#define TSL2563_REG_LOWHIGH	(0x03) -#define TSL2563_REG_HIGHLOW	(0x04) /* data0 high threshold, 2 bytes */ -#define TSL2563_REG_HIGHHIGH	(0x05) -#define TSL2563_REG_INT		(0x06) -#define TSL2563_REG_ID		(0x0a) -#define TSL2563_REG_DATA0LOW	(0x0c) /* broadband sensor value, 2 bytes */ -#define TSL2563_REG_DATA0HIGH	(0x0d) -#define TSL2563_REG_DATA1LOW	(0x0e) /* infrared sensor value, 2 bytes */ -#define TSL2563_REG_DATA1HIGH	(0x0f) - -#define TSL2563_CMD_POWER_ON	(0x03) -#define TSL2563_CMD_POWER_OFF	(0x00) -#define TSL2563_CTRL_POWER_MASK	(0x03) - -#define TSL2563_TIMING_13MS	(0x00) -#define TSL2563_TIMING_100MS	(0x01) -#define TSL2563_TIMING_400MS	(0x02) -#define TSL2563_TIMING_MASK	(0x03) -#define TSL2563_TIMING_GAIN16	(0x10) -#define TSL2563_TIMING_GAIN1	(0x00) - -#define TSL2563_INT_DISBLED	(0x00) -#define TSL2563_INT_LEVEL	(0x10) -#define TSL2563_INT_PERSIST(n)	((n) & 0x0F) - -struct tsl2563_gainlevel_coeff { -	u8 gaintime; -	u16 min; -	u16 max; -}; - -static struct tsl2563_gainlevel_coeff tsl2563_gainlevel_table[] = { -	{ -		.gaintime	= TSL2563_TIMING_400MS | TSL2563_TIMING_GAIN16, -		.min		= 0, -		.max		= 65534, -	}, { -		.gaintime	= TSL2563_TIMING_400MS | TSL2563_TIMING_GAIN1, -		.min		= 2048, -		.max		= 65534, -	}, { -		.gaintime	= TSL2563_TIMING_100MS | TSL2563_TIMING_GAIN1, -		.min		= 4095, -		.max		= 37177, -	}, { -		.gaintime	= TSL2563_TIMING_13MS | TSL2563_TIMING_GAIN1, -		.min		= 3000, -		.max		= 65535, -	}, -}; - -struct tsl2563_chip { -	struct mutex		lock; -	struct i2c_client	*client; -	struct iio_dev		*indio_dev; -	struct delayed_work	poweroff_work; - -	struct work_struct	work_thresh; -	s64			event_timestamp; -	/* Remember state for suspend and resume functions */ -	pm_message_t		state; - -	struct tsl2563_gainlevel_coeff *gainlevel; - -	u16			low_thres; -	u16			high_thres; -	u8			intr; -	bool			int_enabled; - -	/* Calibration coefficients */ -	u32			calib0; -	u32			calib1; -	int			cover_comp_gain; - -	/* Cache current values, to be returned while suspended */ -	u32			data0; -	u32			data1; -}; - -static int tsl2563_write(struct i2c_client *client, u8 reg, u8 value) -{ -	int ret; -	u8 buf[2]; - -	buf[0] = TSL2563_CMD | reg; -	buf[1] = value; - -	ret = i2c_master_send(client, buf, sizeof(buf)); -	return (ret == sizeof(buf)) ? 0 : ret; -} - -static int tsl2563_read(struct i2c_client *client, u8 reg, void *buf, int len) -{ -	int ret; -	u8 cmd = TSL2563_CMD | reg; - -	ret = i2c_master_send(client, &cmd, sizeof(cmd)); -	if (ret != sizeof(cmd)) -		return ret; - -	return i2c_master_recv(client, buf, len); -} - -static int tsl2563_set_power(struct tsl2563_chip *chip, int on) -{ -	struct i2c_client *client = chip->client; -	u8 cmd; - -	cmd = on ? TSL2563_CMD_POWER_ON : TSL2563_CMD_POWER_OFF; -	return tsl2563_write(client, TSL2563_REG_CTRL, cmd); -} - -/* - * Return value is 0 for off, 1 for on, or a negative error - * code if reading failed. - */ -static int tsl2563_get_power(struct tsl2563_chip *chip) -{ -	struct i2c_client *client = chip->client; -	int ret; -	u8 val; - -	ret = tsl2563_read(client, TSL2563_REG_CTRL, &val, sizeof(val)); -	if (ret != sizeof(val)) -		return ret; - -	return (val & TSL2563_CTRL_POWER_MASK) == TSL2563_CMD_POWER_ON; -} - -static int tsl2563_configure(struct tsl2563_chip *chip) -{ -	int ret; - -	ret = tsl2563_write(chip->client, TSL2563_REG_TIMING, -			chip->gainlevel->gaintime); -	if (ret) -		goto error_ret; -	ret = tsl2563_write(chip->client, TSL2563_REG_HIGHLOW, -			chip->high_thres & 0xFF); -	if (ret) -		goto error_ret; -	ret = tsl2563_write(chip->client, TSL2563_REG_HIGHHIGH, -			(chip->high_thres >> 8) & 0xFF); -	if (ret) -		goto error_ret; -	ret = tsl2563_write(chip->client, TSL2563_REG_LOWLOW, -			chip->low_thres & 0xFF); -	if (ret) -		goto error_ret; -	ret = tsl2563_write(chip->client, TSL2563_REG_LOWHIGH, -			(chip->low_thres >> 8) & 0xFF); -/* Interrupt register is automatically written anyway if it is relevant -   so is not here */ -error_ret: -	return ret; -} - -static void tsl2563_poweroff_work(struct work_struct *work) -{ -	struct tsl2563_chip *chip = -		container_of(work, struct tsl2563_chip, poweroff_work.work); -	tsl2563_set_power(chip, 0); -} - -static int tsl2563_detect(struct tsl2563_chip *chip) -{ -	int ret; - -	ret = tsl2563_set_power(chip, 1); -	if (ret) -		return ret; - -	ret = tsl2563_get_power(chip); -	if (ret < 0) -		return ret; - -	return ret ? 0 : -ENODEV; -} - -static int tsl2563_read_id(struct tsl2563_chip *chip, u8 *id) -{ -	struct i2c_client *client = chip->client; -	int ret; - -	ret = tsl2563_read(client, TSL2563_REG_ID, id, sizeof(*id)); -	if (ret != sizeof(*id)) -		return ret; - -	return 0; -} - -/* - * "Normalized" ADC value is one obtained with 400ms of integration time and - * 16x gain. This function returns the number of bits of shift needed to - * convert between normalized values and HW values obtained using given - * timing and gain settings. - */ -static int adc_shiftbits(u8 timing) -{ -	int shift = 0; - -	switch (timing & TSL2563_TIMING_MASK) { -	case TSL2563_TIMING_13MS: -		shift += 5; -		break; -	case TSL2563_TIMING_100MS: -		shift += 2; -		break; -	case TSL2563_TIMING_400MS: -		/* no-op */ -		break; -	} - -	if (!(timing & TSL2563_TIMING_GAIN16)) -		shift += 4; - -	return shift; -} - -/* Convert a HW ADC value to normalized scale. */ -static u32 normalize_adc(u16 adc, u8 timing) -{ -	return adc << adc_shiftbits(timing); -} - -static void tsl2563_wait_adc(struct tsl2563_chip *chip) -{ -	unsigned int delay; - -	switch (chip->gainlevel->gaintime & TSL2563_TIMING_MASK) { -	case TSL2563_TIMING_13MS: -		delay = 14; -		break; -	case TSL2563_TIMING_100MS: -		delay = 101; -		break; -	default: -		delay = 402; -	} -	/* -	 * TODO: Make sure that we wait at least required delay but why we -	 * have to extend it one tick more? -	 */ -	schedule_timeout_interruptible(msecs_to_jiffies(delay) + 2); -} - -static int tsl2563_adjust_gainlevel(struct tsl2563_chip *chip, u16 adc) -{ -	struct i2c_client *client = chip->client; - -	if (adc > chip->gainlevel->max || adc < chip->gainlevel->min) { - -		(adc > chip->gainlevel->max) ? -			chip->gainlevel++ : chip->gainlevel--; - -		tsl2563_write(client, TSL2563_REG_TIMING, -			      chip->gainlevel->gaintime); - -		tsl2563_wait_adc(chip); -		tsl2563_wait_adc(chip); - -		return 1; -	} else -		return 0; -} - -static int tsl2563_get_adc(struct tsl2563_chip *chip) -{ -	struct i2c_client *client = chip->client; -	u8 buf0[2], buf1[2]; -	u16 adc0, adc1; -	int retry = 1; -	int ret = 0; - -	if (chip->state.event != PM_EVENT_ON) -		goto out; - -	if (!chip->int_enabled) { -		cancel_delayed_work(&chip->poweroff_work); - -		if (!tsl2563_get_power(chip)) { -			ret = tsl2563_set_power(chip, 1); -			if (ret) -				goto out; -			ret = tsl2563_configure(chip); -			if (ret) -				goto out; -			tsl2563_wait_adc(chip); -		} -	} - -	while (retry) { -		ret = tsl2563_read(client, -				   TSL2563_REG_DATA0LOW, -				   buf0, sizeof(buf0)); -		if (ret != sizeof(buf0)) -			goto out; - -		ret = tsl2563_read(client, TSL2563_REG_DATA1LOW, -				   buf1, sizeof(buf1)); -		if (ret != sizeof(buf1)) -			goto out; - -		adc0 = (buf0[1] << 8) + buf0[0]; -		adc1 = (buf1[1] << 8) + buf1[0]; - -		retry = tsl2563_adjust_gainlevel(chip, adc0); -	} - -	chip->data0 = normalize_adc(adc0, chip->gainlevel->gaintime); -	chip->data1 = normalize_adc(adc1, chip->gainlevel->gaintime); - -	if (!chip->int_enabled) -		schedule_delayed_work(&chip->poweroff_work, 5 * HZ); - -	ret = 0; -out: -	return ret; -} - -static inline int calib_to_sysfs(u32 calib) -{ -	return (int) (((calib * CALIB_BASE_SYSFS) + -		       CALIB_FRAC_HALF) >> CALIB_FRAC_BITS); -} - -static inline u32 calib_from_sysfs(int value) -{ -	return (((u32) value) << CALIB_FRAC_BITS) / CALIB_BASE_SYSFS; -} - -/* - * Conversions between lux and ADC values. - * - * The basic formula is lux = c0 * adc0 - c1 * adc1, where c0 and c1 are - * appropriate constants. Different constants are needed for different - * kinds of light, determined by the ratio adc1/adc0 (basically the ratio - * of the intensities in infrared and visible wavelengths). lux_table below - * lists the upper threshold of the adc1/adc0 ratio and the corresponding - * constants. - */ - -struct tsl2563_lux_coeff { -	unsigned long ch_ratio; -	unsigned long ch0_coeff; -	unsigned long ch1_coeff; -}; - -static const struct tsl2563_lux_coeff lux_table[] = { -	{ -		.ch_ratio	= FRAC10K(1300), -		.ch0_coeff	= FRAC10K(315), -		.ch1_coeff	= FRAC10K(262), -	}, { -		.ch_ratio	= FRAC10K(2600), -		.ch0_coeff	= FRAC10K(337), -		.ch1_coeff	= FRAC10K(430), -	}, { -		.ch_ratio	= FRAC10K(3900), -		.ch0_coeff	= FRAC10K(363), -		.ch1_coeff	= FRAC10K(529), -	}, { -		.ch_ratio	= FRAC10K(5200), -		.ch0_coeff	= FRAC10K(392), -		.ch1_coeff	= FRAC10K(605), -	}, { -		.ch_ratio	= FRAC10K(6500), -		.ch0_coeff	= FRAC10K(229), -		.ch1_coeff	= FRAC10K(291), -	}, { -		.ch_ratio	= FRAC10K(8000), -		.ch0_coeff	= FRAC10K(157), -		.ch1_coeff	= FRAC10K(180), -	}, { -		.ch_ratio	= FRAC10K(13000), -		.ch0_coeff	= FRAC10K(34), -		.ch1_coeff	= FRAC10K(26), -	}, { -		.ch_ratio	= ULONG_MAX, -		.ch0_coeff	= 0, -		.ch1_coeff	= 0, -	}, -}; - -/* - * Convert normalized, scaled ADC values to lux. - */ -static unsigned int adc_to_lux(u32 adc0, u32 adc1) -{ -	const struct tsl2563_lux_coeff *lp = lux_table; -	unsigned long ratio, lux, ch0 = adc0, ch1 = adc1; - -	ratio = ch0 ? ((ch1 << ADC_FRAC_BITS) / ch0) : ULONG_MAX; - -	while (lp->ch_ratio < ratio) -		lp++; - -	lux = ch0 * lp->ch0_coeff - ch1 * lp->ch1_coeff; - -	return (unsigned int) (lux >> ADC_FRAC_BITS); -} - -/*--------------------------------------------------------------*/ -/*                      Sysfs interface                         */ -/*--------------------------------------------------------------*/ - -static ssize_t tsl2563_adc_show(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; -	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); -	int ret; - -	mutex_lock(&chip->lock); - -	ret = tsl2563_get_adc(chip); -	if (ret) -		goto out; - -	switch (this_attr->address) { -	case 0: -		ret = snprintf(buf, PAGE_SIZE, "%d\n", chip->data0); -		break; -	case 1: -		ret = snprintf(buf, PAGE_SIZE, "%d\n", chip->data1); -		break; -	} -out: -	mutex_unlock(&chip->lock); -	return ret; -} - -/* Apply calibration coefficient to ADC count. */ -static u32 calib_adc(u32 adc, u32 calib) -{ -	unsigned long scaled = adc; - -	scaled *= calib; -	scaled >>= CALIB_FRAC_BITS; - -	return (u32) scaled; -} - -static ssize_t tsl2563_lux_show(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; -	u32 calib0, calib1; -	int ret; - -	mutex_lock(&chip->lock); - -	ret = tsl2563_get_adc(chip); -	if (ret) -		goto out; - -	calib0 = calib_adc(chip->data0, chip->calib0) * chip->cover_comp_gain; -	calib1 = calib_adc(chip->data1, chip->calib1) * chip->cover_comp_gain; - -	ret = snprintf(buf, PAGE_SIZE, "%d\n", adc_to_lux(calib0, calib1)); - -out: -	mutex_unlock(&chip->lock); -	return ret; -} - -static ssize_t format_calib(char *buf, int len, u32 calib) -{ -	return snprintf(buf, PAGE_SIZE, "%d\n", calib_to_sysfs(calib)); -} - -static ssize_t tsl2563_calib_show(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; -	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); -	int ret; - -	mutex_lock(&chip->lock); -	switch (this_attr->address) { -	case 0: -		ret = format_calib(buf, PAGE_SIZE, chip->calib0); -		break; -	case 1: -		ret = format_calib(buf, PAGE_SIZE, chip->calib1); -		break; -	default: -		ret = -ENODEV; -	} -	mutex_unlock(&chip->lock); -	return ret; -} - -static ssize_t tsl2563_calib_store(struct device *dev, -				struct device_attribute *attr, -				const char *buf, size_t len) -{ -	struct iio_dev *indio_dev = dev_get_drvdata(dev); -	struct tsl2563_chip *chip = indio_dev->dev_data; -	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); -	int value; -	u32 calib; - -	if (1 != sscanf(buf, "%d", &value)) -		return -EINVAL; - -	calib = calib_from_sysfs(value); - -	switch (this_attr->address) { -	case 0: -		chip->calib0 = calib; -		break; -	case 1: -		chip->calib1 = calib; -		break; -	} - -	return len; -} - -static IIO_DEVICE_ATTR(intensity0_both_raw, S_IRUGO, -		tsl2563_adc_show, NULL, 0); -static IIO_DEVICE_ATTR(intensity1_ir_raw, S_IRUGO, -		tsl2563_adc_show, NULL, 1); -static DEVICE_ATTR(illuminance0_input, S_IRUGO, tsl2563_lux_show, NULL); -static IIO_DEVICE_ATTR(intensity0_both_calibgain, S_IRUGO | S_IWUSR, -		tsl2563_calib_show, tsl2563_calib_store, 0); -static IIO_DEVICE_ATTR(intensity1_ir_calibgain, S_IRUGO | S_IWUSR, -		tsl2563_calib_show, tsl2563_calib_store, 1); - -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[] = { -	&iio_dev_attr_intensity0_both_raw.dev_attr.attr, -	&iio_dev_attr_intensity1_ir_raw.dev_attr.attr, -	&dev_attr_illuminance0_input.attr, -	&iio_dev_attr_intensity0_both_calibgain.dev_attr.attr, -	&iio_dev_attr_intensity1_ir_calibgain.dev_attr.attr, -	&dev_attr_name.attr, -	NULL -}; - -static const struct attribute_group tsl2563_group = { -	.attrs = tsl2563_attributes, -}; - -static ssize_t tsl2563_read_thresh(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; -	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); -	u16 val = 0; -	switch (this_attr->address) { -	case TSL2563_REG_HIGHLOW: -		val = chip->high_thres; -		break; -	case TSL2563_REG_LOWLOW: -		val = chip->low_thres; -		break; -	} -	return snprintf(buf, PAGE_SIZE, "%d\n", val); -} - -static ssize_t tsl2563_write_thresh(struct device *dev, -				struct device_attribute *attr, -				const char *buf, -				size_t len) -{ -	struct iio_dev *indio_dev = dev_get_drvdata(dev); -	struct tsl2563_chip *chip = indio_dev->dev_data; -	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); -	unsigned long val; -	int ret; - -	ret = strict_strtoul(buf, 10, &val); -	if (ret) -		return ret; -	mutex_lock(&chip->lock); -	ret = tsl2563_write(chip->client, this_attr->address, val & 0xFF); -	if (ret) -		goto error_ret; -	ret = tsl2563_write(chip->client, this_attr->address + 1, -			(val >> 8) & 0xFF); -	switch (this_attr->address) { -	case TSL2563_REG_HIGHLOW: -		chip->high_thres = val; -		break; -	case TSL2563_REG_LOWLOW: -		chip->low_thres = val; -		break; -	} - -error_ret: -	mutex_unlock(&chip->lock); - -	return ret < 0 ? ret : len; -} - -static IIO_DEVICE_ATTR(intensity0_both_raw_thresh_rising_value, -		S_IRUGO | S_IWUSR, -		tsl2563_read_thresh, -		tsl2563_write_thresh, -		TSL2563_REG_HIGHLOW); - -static IIO_DEVICE_ATTR(intensity0_both_raw_thresh_falling_value, -		S_IRUGO | S_IWUSR, -		tsl2563_read_thresh, -		tsl2563_write_thresh, -		TSL2563_REG_LOWLOW); - -static int tsl2563_int_th(struct iio_dev *dev_info, -			int index, -			s64 timestamp, -			int not_test) -{ -	struct tsl2563_chip *chip = dev_info->dev_data; - -	chip->event_timestamp = timestamp; -	schedule_work(&chip->work_thresh); - -	return 0; -} - -static void tsl2563_int_bh(struct work_struct *work_s) -{ -	struct tsl2563_chip *chip -		= container_of(work_s, -			struct tsl2563_chip, work_thresh); -	u8 cmd = TSL2563_CMD | TSL2563_CLEARINT; - -	iio_push_event(chip->indio_dev, 0, -		       IIO_UNMOD_EVENT_CODE(IIO_EV_CLASS_LIGHT, -					    0, -					    IIO_EV_TYPE_THRESH, -					    IIO_EV_DIR_EITHER), -		       chip->event_timestamp); - -	/* reenable_irq */ -	enable_irq(chip->client->irq); -	/* clear the interrupt and push the event */ -	i2c_master_send(chip->client, &cmd, sizeof(cmd)); - -} - -static ssize_t tsl2563_write_interrupt_config(struct device *dev, -					struct device_attribute *attr, -					const char *buf, -					size_t len) -{ -	struct iio_dev *indio_dev = dev_get_drvdata(dev); -	struct tsl2563_chip *chip = indio_dev->dev_data; -	struct iio_event_attr *this_attr = to_iio_event_attr(attr); -	int input, ret = 0; - -	ret = sscanf(buf, "%d", &input); -	if (ret != 1) -		return -EINVAL; -	mutex_lock(&chip->lock); -	if (input && !(chip->intr & 0x30)) { -		iio_add_event_to_list(this_attr->listel, -				&indio_dev->interrupts[0]->ev_list); -		chip->intr &= ~0x30; -		chip->intr |= 0x10; -		/* ensure the chip is actually on */ -		cancel_delayed_work(&chip->poweroff_work); -		if (!tsl2563_get_power(chip)) { -			ret = tsl2563_set_power(chip, 1); -			if (ret) -				goto out; -			ret = tsl2563_configure(chip); -			if (ret) -				goto out; -		} -		ret = tsl2563_write(chip->client, TSL2563_REG_INT, chip->intr); -		chip->int_enabled = true; -	} - -	if (!input && (chip->intr & 0x30)) { -		chip->intr |= ~0x30; -		ret = tsl2563_write(chip->client, TSL2563_REG_INT, chip->intr); -		iio_remove_event_from_list(this_attr->listel, -					&indio_dev->interrupts[0]->ev_list); -		chip->int_enabled = false; -		/* now the interrupt is not enabled, we can go to sleep */ -		schedule_delayed_work(&chip->poweroff_work, 5 * HZ); -	} -out: -	mutex_unlock(&chip->lock); - -	return (ret < 0) ? ret : len; -} - -static ssize_t tsl2563_read_interrupt_config(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; -	int ret; -	u8 rxbuf; -	ssize_t len; - -	mutex_lock(&chip->lock); -	ret = tsl2563_read(chip->client, -			TSL2563_REG_INT, -			&rxbuf, -			sizeof(rxbuf)); -	mutex_unlock(&chip->lock); -	if (ret < 0) -		goto error_ret; -	len = snprintf(buf, PAGE_SIZE, "%d\n", !!(rxbuf & 0x30)); -error_ret: - -	return (ret < 0) ? ret : len; -} - -IIO_EVENT_ATTR(intensity0_both_thresh_en, -	tsl2563_read_interrupt_config, -	tsl2563_write_interrupt_config, -	0, -	tsl2563_int_th); - -static struct attribute *tsl2563_event_attributes[] = { -	&iio_event_attr_intensity0_both_thresh_en.dev_attr.attr, -	&iio_dev_attr_intensity0_both_raw_thresh_rising_value.dev_attr.attr, -	&iio_dev_attr_intensity0_both_raw_thresh_falling_value.dev_attr.attr, -	NULL, -}; - -static struct attribute_group tsl2563_event_attribute_group = { -	.attrs = tsl2563_event_attributes, -}; - -/*--------------------------------------------------------------*/ -/*                      Probe, Attach, Remove                   */ -/*--------------------------------------------------------------*/ -static struct i2c_driver tsl2563_i2c_driver; - -static int __devinit tsl2563_probe(struct i2c_client *client, -				const struct i2c_device_id *device_id) -{ -	struct tsl2563_chip *chip; -	struct tsl2563_platform_data *pdata = client->dev.platform_data; -	int err = 0; -	int ret; -	u8 id; - -	chip = kzalloc(sizeof(*chip), GFP_KERNEL); -	if (!chip) -		return -ENOMEM; - -	INIT_WORK(&chip->work_thresh, tsl2563_int_bh); -	i2c_set_clientdata(client, chip); -	chip->client = client; - -	err = tsl2563_detect(chip); -	if (err) { -		dev_err(&client->dev, "device not found, error %d\n", -err); -		goto fail1; -	} - -	err = tsl2563_read_id(chip, &id); -	if (err) -		goto fail1; - -	mutex_init(&chip->lock); - -	/* Default values used until userspace says otherwise */ -	chip->low_thres = 0x0; -	chip->high_thres = 0xffff; -	chip->gainlevel = tsl2563_gainlevel_table; -	chip->intr = TSL2563_INT_PERSIST(4); -	chip->calib0 = calib_from_sysfs(CALIB_BASE_SYSFS); -	chip->calib1 = calib_from_sysfs(CALIB_BASE_SYSFS); - -	if (pdata) -		chip->cover_comp_gain = pdata->cover_comp_gain; -	else -		chip->cover_comp_gain = 1; - -	dev_info(&client->dev, "model %d, rev. %d\n", id >> 4, id & 0x0f); - -	chip->indio_dev = iio_allocate_device(); -	if (!chip->indio_dev) -		goto fail1; -	chip->indio_dev->attrs = &tsl2563_group; -	chip->indio_dev->dev.parent = &client->dev; -	chip->indio_dev->dev_data = (void *)(chip); -	chip->indio_dev->driver_module = THIS_MODULE; -	chip->indio_dev->modes = INDIO_DIRECT_MODE; -	if (client->irq) { -		chip->indio_dev->num_interrupt_lines = 1; -		chip->indio_dev->event_attrs -			= &tsl2563_event_attribute_group; -	} -	ret = iio_device_register(chip->indio_dev); -	if (ret) -		goto fail1; - -	if (client->irq) { -		ret = iio_register_interrupt_line(client->irq, -						chip->indio_dev, -						0, -						IRQF_TRIGGER_RISING, -						client->name); -		if (ret) -			goto fail2; -	} -	err = tsl2563_configure(chip); -	if (err) -		goto fail3; - -	INIT_DELAYED_WORK(&chip->poweroff_work, tsl2563_poweroff_work); -	/* The interrupt cannot yet be enabled so this is fine without lock */ -	schedule_delayed_work(&chip->poweroff_work, 5 * HZ); - -	return 0; -fail3: -	if (client->irq) -		iio_unregister_interrupt_line(chip->indio_dev, 0); -fail2: -	iio_device_unregister(chip->indio_dev); -fail1: -	kfree(chip); -	return err; -} - -static int tsl2563_remove(struct i2c_client *client) -{ -	struct tsl2563_chip *chip = i2c_get_clientdata(client); -	if (!chip->int_enabled) -		cancel_delayed_work(&chip->poweroff_work); -	/* Ensure that interrupts are disabled - then flush any bottom halves */ -	chip->intr |= ~0x30; -	tsl2563_write(chip->client, TSL2563_REG_INT, chip->intr); -	flush_scheduled_work(); -	tsl2563_set_power(chip, 0); -	if (client->irq) -		iio_unregister_interrupt_line(chip->indio_dev, 0); -	iio_device_unregister(chip->indio_dev); - -	kfree(chip); -	return 0; -} - -static int tsl2563_suspend(struct i2c_client *client, pm_message_t state) -{ -	struct tsl2563_chip *chip = i2c_get_clientdata(client); -	int ret; - -	mutex_lock(&chip->lock); - -	ret = tsl2563_set_power(chip, 0); -	if (ret) -		goto out; - -	chip->state = state; - -out: -	mutex_unlock(&chip->lock); -	return ret; -} - -static int tsl2563_resume(struct i2c_client *client) -{ -	struct tsl2563_chip *chip = i2c_get_clientdata(client); -	int ret; - -	mutex_lock(&chip->lock); - -	ret = tsl2563_set_power(chip, 1); -	if (ret) -		goto out; - -	ret = tsl2563_configure(chip); -	if (ret) -		goto out; - -	chip->state.event = PM_EVENT_ON; - -out: -	mutex_unlock(&chip->lock); -	return ret; -} - -static const struct i2c_device_id tsl2563_id[] = { -	{ "tsl2560", 0 }, -	{ "tsl2561", 1 }, -	{ "tsl2562", 2 }, -	{ "tsl2563", 3 }, -	{} -}; -MODULE_DEVICE_TABLE(i2c, tsl2563_id); - -static struct i2c_driver tsl2563_i2c_driver = { -	.driver = { -		.name	 = "tsl2563", -	}, -	.suspend	= tsl2563_suspend, -	.resume		= tsl2563_resume, -	.probe		= tsl2563_probe, -	.remove		= __devexit_p(tsl2563_remove), -	.id_table	= tsl2563_id, -}; - -static int __init tsl2563_init(void) -{ -	return i2c_add_driver(&tsl2563_i2c_driver); -} - -static void __exit tsl2563_exit(void) -{ -	i2c_del_driver(&tsl2563_i2c_driver); -} - -MODULE_AUTHOR("Nokia Corporation"); -MODULE_DESCRIPTION("tsl2563 light sensor driver"); -MODULE_LICENSE("GPL"); - -module_init(tsl2563_init); -module_exit(tsl2563_exit); diff --git a/drivers/staging/iio/light/tsl2563.h b/drivers/staging/iio/light/tsl2563.h deleted file mode 100644 index b97368bd7ff..00000000000 --- a/drivers/staging/iio/light/tsl2563.h +++ /dev/null @@ -1,9 +0,0 @@ -#ifndef __LINUX_TSL2563_H -#define __LINUX_TSL2563_H - -struct tsl2563_platform_data { -	int cover_comp_gain; -}; - -#endif /* __LINUX_TSL2563_H */ - diff --git a/drivers/staging/iio/light/tsl2583.c b/drivers/staging/iio/light/tsl2583.c new file mode 100644 index 00000000000..fa964987966 --- /dev/null +++ b/drivers/staging/iio/light/tsl2583.c @@ -0,0 +1,950 @@ +/* + * Device driver for monitoring ambient light intensity (lux) + * within the TAOS tsl258x family of devices (tsl2580, tsl2581). + * + * Copyright (c) 2011, TAOS Corporation. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA	02110-1301, USA. + */ + +#include <linux/kernel.h> +#include <linux/i2c.h> +#include <linux/errno.h> +#include <linux/delay.h> +#include <linux/string.h> +#include <linux/mutex.h> +#include <linux/unistd.h> +#include <linux/slab.h> +#include <linux/module.h> +#include <linux/iio/iio.h> + +#define TSL258X_MAX_DEVICE_REGS		32 + +/* Triton register offsets */ +#define	TSL258X_REG_MAX		8 + +/* Device Registers and Masks */ +#define TSL258X_CNTRL			0x00 +#define TSL258X_ALS_TIME		0X01 +#define TSL258X_INTERRUPT		0x02 +#define TSL258X_GAIN			0x07 +#define TSL258X_REVID			0x11 +#define TSL258X_CHIPID			0x12 +#define TSL258X_ALS_CHAN0LO		0x14 +#define TSL258X_ALS_CHAN0HI		0x15 +#define TSL258X_ALS_CHAN1LO		0x16 +#define TSL258X_ALS_CHAN1HI		0x17 +#define TSL258X_TMR_LO			0x18 +#define TSL258X_TMR_HI			0x19 + +/* tsl2583 cmd reg masks */ +#define TSL258X_CMD_REG			0x80 +#define TSL258X_CMD_SPL_FN		0x60 +#define TSL258X_CMD_ALS_INT_CLR	0X01 + +/* tsl2583 cntrl reg masks */ +#define TSL258X_CNTL_ADC_ENBL	0x02 +#define TSL258X_CNTL_PWR_ON		0x01 + +/* tsl2583 status reg masks */ +#define TSL258X_STA_ADC_VALID	0x01 +#define TSL258X_STA_ADC_INTR	0x10 + +/* Lux calculation constants */ +#define	TSL258X_LUX_CALC_OVER_FLOW		65535 + +enum { +	TSL258X_CHIP_UNKNOWN = 0, +	TSL258X_CHIP_WORKING = 1, +	TSL258X_CHIP_SUSPENDED = 2 +}; + +/* Per-device data */ +struct taos_als_info { +	u16 als_ch0; +	u16 als_ch1; +	u16 lux; +}; + +struct taos_settings { +	int als_time; +	int als_gain; +	int als_gain_trim; +	int als_cal_target; +}; + +struct tsl2583_chip { +	struct mutex als_mutex; +	struct i2c_client *client; +	struct taos_als_info als_cur_info; +	struct taos_settings taos_settings; +	int als_time_scale; +	int als_saturation; +	int taos_chip_status; +	u8 taos_config[8]; +}; + +/* + * Initial values for device - this values can/will be changed by driver. + * and applications as needed. + * These values are dynamic. + */ +static const u8 taos_config[8] = { +		0x00, 0xee, 0x00, 0x03, 0x00, 0xFF, 0xFF, 0x00 +}; /*	cntrl atime intC  Athl0 Athl1 Athh0 Athh1 gain */ + +struct taos_lux { +	unsigned int ratio; +	unsigned int ch0; +	unsigned int ch1; +}; + +/* This structure is intentionally large to accommodate updates via sysfs. */ +/* Sized to 11 = max 10 segments + 1 termination segment */ +/* Assumption is one and only one type of glass used  */ +static struct taos_lux taos_device_lux[11] = { +	{  9830,  8520, 15729 }, +	{ 12452, 10807, 23344 }, +	{ 14746,  6383, 11705 }, +	{ 17695,  4063,  6554 }, +}; + +struct gainadj { +	s16 ch0; +	s16 ch1; +}; + +/* Index = (0 - 3) Used to validate the gain selection index */ +static const struct gainadj gainadj[] = { +	{ 1, 1 }, +	{ 8, 8 }, +	{ 16, 16 }, +	{ 107, 115 } +}; + +/* + * Provides initial operational parameter defaults. + * These defaults may be changed through the device's sysfs files. + */ +static void taos_defaults(struct tsl2583_chip *chip) +{ +	/* Operational parameters */ +	chip->taos_settings.als_time = 100; +	/* must be a multiple of 50mS */ +	chip->taos_settings.als_gain = 0; +	/* this is actually an index into the gain table */ +	/* assume clear glass as default */ +	chip->taos_settings.als_gain_trim = 1000; +	/* default gain trim to account for aperture effects */ +	chip->taos_settings.als_cal_target = 130; +	/* Known external ALS reading used for calibration */ +} + +/* + * Read a number of bytes starting at register (reg) location. + * Return 0, or i2c_smbus_write_byte ERROR code. + */ +static int +taos_i2c_read(struct i2c_client *client, u8 reg, u8 *val, unsigned int len) +{ +	int i, ret; + +	for (i = 0; i < len; i++) { +		/* select register to write */ +		ret = i2c_smbus_write_byte(client, (TSL258X_CMD_REG | reg)); +		if (ret < 0) { +			dev_err(&client->dev, +				"taos_i2c_read failed to write register %x\n", +				reg); +			return ret; +		} +		/* read the data */ +		*val = i2c_smbus_read_byte(client); +		val++; +		reg++; +	} +	return 0; +} + +/* + * Reads and calculates current lux value. + * The raw ch0 and ch1 values of the ambient light sensed in the last + * integration cycle are read from the device. + * Time scale factor array values are adjusted based on the integration time. + * The raw values are multiplied by a scale factor, and device gain is obtained + * using gain index. Limit checks are done next, then the ratio of a multiple + * of ch1 value, to the ch0 value, is calculated. The array taos_device_lux[] + * declared above is then scanned to find the first ratio value that is just + * above the ratio we just calculated. The ch0 and ch1 multiplier constants in + * the array are then used along with the time scale factor array values, to + * calculate the lux. + */ +static int taos_get_lux(struct iio_dev *indio_dev) +{ +	u16 ch0, ch1; /* separated ch0/ch1 data from device */ +	u32 lux; /* raw lux calculated from device data */ +	u64 lux64; +	u32 ratio; +	u8 buf[5]; +	struct taos_lux *p; +	struct tsl2583_chip *chip = iio_priv(indio_dev); +	int i, ret; +	u32 ch0lux = 0; +	u32 ch1lux = 0; + +	if (mutex_trylock(&chip->als_mutex) == 0) { +		dev_info(&chip->client->dev, "taos_get_lux device is busy\n"); +		return chip->als_cur_info.lux; /* busy, so return LAST VALUE */ +	} + +	if (chip->taos_chip_status != TSL258X_CHIP_WORKING) { +		/* device is not enabled */ +		dev_err(&chip->client->dev, "taos_get_lux device is not enabled\n"); +		ret = -EBUSY; +		goto out_unlock; +	} + +	ret = taos_i2c_read(chip->client, (TSL258X_CMD_REG), &buf[0], 1); +	if (ret < 0) { +		dev_err(&chip->client->dev, "taos_get_lux failed to read CMD_REG\n"); +		goto out_unlock; +	} +	/* is data new & valid */ +	if (!(buf[0] & TSL258X_STA_ADC_INTR)) { +		dev_err(&chip->client->dev, "taos_get_lux data not valid\n"); +		ret = chip->als_cur_info.lux; /* return LAST VALUE */ +		goto out_unlock; +	} + +	for (i = 0; i < 4; i++) { +		int reg = TSL258X_CMD_REG | (TSL258X_ALS_CHAN0LO + i); +		ret = taos_i2c_read(chip->client, reg, &buf[i], 1); +		if (ret < 0) { +			dev_err(&chip->client->dev, +				"taos_get_lux failed to read register %x\n", +				reg); +			goto out_unlock; +		} +	} + +	/* clear status, really interrupt status (interrupts are off), but +	 * we use the bit anyway - don't forget 0x80 - this is a command*/ +	ret = i2c_smbus_write_byte(chip->client, +				   (TSL258X_CMD_REG | TSL258X_CMD_SPL_FN | +				    TSL258X_CMD_ALS_INT_CLR)); + +	if (ret < 0) { +		dev_err(&chip->client->dev, +			"taos_i2c_write_command failed in taos_get_lux, err = %d\n", +			ret); +		goto out_unlock; /* have no data, so return failure */ +	} + +	/* extract ALS/lux data */ +	ch0 = le16_to_cpup((const __le16 *)&buf[0]); +	ch1 = le16_to_cpup((const __le16 *)&buf[2]); + +	chip->als_cur_info.als_ch0 = ch0; +	chip->als_cur_info.als_ch1 = ch1; + +	if ((ch0 >= chip->als_saturation) || (ch1 >= chip->als_saturation)) +		goto return_max; + +	if (ch0 == 0) { +		/* have no data, so return LAST VALUE */ +		ret = chip->als_cur_info.lux = 0; +		goto out_unlock; +	} +	/* calculate ratio */ +	ratio = (ch1 << 15) / ch0; +	/* convert to unscaled lux using the pointer to the table */ +	for (p = (struct taos_lux *) taos_device_lux; +	     p->ratio != 0 && p->ratio < ratio; p++) +		; + +	if (p->ratio == 0) { +		lux = 0; +	} else { +		ch0lux = ((ch0 * p->ch0) + +			  (gainadj[chip->taos_settings.als_gain].ch0 >> 1)) +			 / gainadj[chip->taos_settings.als_gain].ch0; +		ch1lux = ((ch1 * p->ch1) + +			  (gainadj[chip->taos_settings.als_gain].ch1 >> 1)) +			 / gainadj[chip->taos_settings.als_gain].ch1; +		lux = ch0lux - ch1lux; +	} + +	/* note: lux is 31 bit max at this point */ +	if (ch1lux > ch0lux) { +		dev_dbg(&chip->client->dev, "No Data - Return last value\n"); +		ret = chip->als_cur_info.lux = 0; +		goto out_unlock; +	} + +	/* adjust for active time scale */ +	if (chip->als_time_scale == 0) +		lux = 0; +	else +		lux = (lux + (chip->als_time_scale >> 1)) / +			chip->als_time_scale; + +	/* Adjust for active gain scale. +	 * The taos_device_lux tables above have a factor of 8192 built in, +	 * so we need to shift right. +	 * User-specified gain provides a multiplier. +	 * Apply user-specified gain before shifting right to retain precision. +	 * Use 64 bits to avoid overflow on multiplication. +	 * Then go back to 32 bits before division to avoid using div_u64(). +	 */ +	lux64 = lux; +	lux64 = lux64 * chip->taos_settings.als_gain_trim; +	lux64 >>= 13; +	lux = lux64; +	lux = (lux + 500) / 1000; +	if (lux > TSL258X_LUX_CALC_OVER_FLOW) { /* check for overflow */ +return_max: +		lux = TSL258X_LUX_CALC_OVER_FLOW; +	} + +	/* Update the structure with the latest VALID lux. */ +	chip->als_cur_info.lux = lux; +	ret = lux; + +out_unlock: +	mutex_unlock(&chip->als_mutex); +	return ret; +} + +/* + * Obtain single reading and calculate the als_gain_trim (later used + * to derive actual lux). + * Return updated gain_trim value. + */ +static int taos_als_calibrate(struct iio_dev *indio_dev) +{ +	struct tsl2583_chip *chip = iio_priv(indio_dev); +	u8 reg_val; +	unsigned int gain_trim_val; +	int ret; +	int lux_val; + +	ret = i2c_smbus_write_byte(chip->client, +				   (TSL258X_CMD_REG | TSL258X_CNTRL)); +	if (ret < 0) { +		dev_err(&chip->client->dev, +			"taos_als_calibrate failed to reach the CNTRL register, ret=%d\n", +			ret); +		return ret; +	} + +	reg_val = i2c_smbus_read_byte(chip->client); +	if ((reg_val & (TSL258X_CNTL_ADC_ENBL | TSL258X_CNTL_PWR_ON)) +			!= (TSL258X_CNTL_ADC_ENBL | TSL258X_CNTL_PWR_ON)) { +		dev_err(&chip->client->dev, +			"taos_als_calibrate failed: device not powered on with ADC enabled\n"); +		return -1; +	} + +	ret = i2c_smbus_write_byte(chip->client, +				   (TSL258X_CMD_REG | TSL258X_CNTRL)); +	if (ret < 0) { +		dev_err(&chip->client->dev, +			"taos_als_calibrate failed to reach the STATUS register, ret=%d\n", +			ret); +		return ret; +	} +	reg_val = i2c_smbus_read_byte(chip->client); + +	if ((reg_val & TSL258X_STA_ADC_VALID) != TSL258X_STA_ADC_VALID) { +		dev_err(&chip->client->dev, +			"taos_als_calibrate failed: STATUS - ADC not valid.\n"); +		return -ENODATA; +	} +	lux_val = taos_get_lux(indio_dev); +	if (lux_val < 0) { +		dev_err(&chip->client->dev, "taos_als_calibrate failed to get lux\n"); +		return lux_val; +	} +	gain_trim_val = (unsigned int) (((chip->taos_settings.als_cal_target) +			* chip->taos_settings.als_gain_trim) / lux_val); + +	if ((gain_trim_val < 250) || (gain_trim_val > 4000)) { +		dev_err(&chip->client->dev, +			"taos_als_calibrate failed: trim_val of %d is out of range\n", +			gain_trim_val); +		return -ENODATA; +	} +	chip->taos_settings.als_gain_trim = (int) gain_trim_val; + +	return (int) gain_trim_val; +} + +/* + * Turn the device on. + * Configuration must be set before calling this function. + */ +static int taos_chip_on(struct iio_dev *indio_dev) +{ +	int i; +	int ret; +	u8 *uP; +	u8 utmp; +	int als_count; +	int als_time; +	struct tsl2583_chip *chip = iio_priv(indio_dev); + +	/* and make sure we're not already on */ +	if (chip->taos_chip_status == TSL258X_CHIP_WORKING) { +		/* if forcing a register update - turn off, then on */ +		dev_info(&chip->client->dev, "device is already enabled\n"); +		return -EINVAL; +	} + +	/* determine als integration register */ +	als_count = (chip->taos_settings.als_time * 100 + 135) / 270; +	if (als_count == 0) +		als_count = 1; /* ensure at least one cycle */ + +	/* convert back to time (encompasses overrides) */ +	als_time = (als_count * 27 + 5) / 10; +	chip->taos_config[TSL258X_ALS_TIME] = 256 - als_count; + +	/* Set the gain based on taos_settings struct */ +	chip->taos_config[TSL258X_GAIN] = chip->taos_settings.als_gain; + +	/* set chip struct re scaling and saturation */ +	chip->als_saturation = als_count * 922; /* 90% of full scale */ +	chip->als_time_scale = (als_time + 25) / 50; + +	/* TSL258x Specific power-on / adc enable sequence +	 * Power on the device 1st. */ +	utmp = TSL258X_CNTL_PWR_ON; +	ret = i2c_smbus_write_byte_data(chip->client, +					TSL258X_CMD_REG | TSL258X_CNTRL, utmp); +	if (ret < 0) { +		dev_err(&chip->client->dev, "taos_chip_on failed on CNTRL reg.\n"); +		return ret; +	} + +	/* Use the following shadow copy for our delay before enabling ADC. +	 * Write all the registers. */ +	for (i = 0, uP = chip->taos_config; i < TSL258X_REG_MAX; i++) { +		ret = i2c_smbus_write_byte_data(chip->client, +						TSL258X_CMD_REG + i, +						*uP++); +		if (ret < 0) { +			dev_err(&chip->client->dev, +				"taos_chip_on failed on reg %d.\n", i); +			return ret; +		} +	} + +	usleep_range(3000, 3500); +	/* NOW enable the ADC +	 * initialize the desired mode of operation */ +	utmp = TSL258X_CNTL_PWR_ON | TSL258X_CNTL_ADC_ENBL; +	ret = i2c_smbus_write_byte_data(chip->client, +					TSL258X_CMD_REG | TSL258X_CNTRL, +					utmp); +	if (ret < 0) { +		dev_err(&chip->client->dev, "taos_chip_on failed on 2nd CTRL reg.\n"); +		return ret; +	} +	chip->taos_chip_status = TSL258X_CHIP_WORKING; + +	return ret; +} + +static int taos_chip_off(struct iio_dev *indio_dev) +{ +	struct tsl2583_chip *chip = iio_priv(indio_dev); +	int ret; + +	/* turn device off */ +	chip->taos_chip_status = TSL258X_CHIP_SUSPENDED; +	ret = i2c_smbus_write_byte_data(chip->client, +					TSL258X_CMD_REG | TSL258X_CNTRL, +					0x00); +	return ret; +} + +/* Sysfs Interface Functions */ + +static ssize_t taos_power_state_show(struct device *dev, +	struct device_attribute *attr, char *buf) +{ +	struct iio_dev *indio_dev = dev_to_iio_dev(dev); +	struct tsl2583_chip *chip = iio_priv(indio_dev); + +	return sprintf(buf, "%d\n", chip->taos_chip_status); +} + +static ssize_t taos_power_state_store(struct device *dev, +	struct device_attribute *attr, const char *buf, size_t len) +{ +	struct iio_dev *indio_dev = dev_to_iio_dev(dev); +	int value; + +	if (kstrtoint(buf, 0, &value)) +		return -EINVAL; + +	if (value == 0) +		taos_chip_off(indio_dev); +	else +		taos_chip_on(indio_dev); + +	return len; +} + +static ssize_t taos_gain_show(struct device *dev, +	struct device_attribute *attr, char *buf) +{ +	struct iio_dev *indio_dev = dev_to_iio_dev(dev); +	struct tsl2583_chip *chip = iio_priv(indio_dev); +	char gain[4] = {0}; + +	switch (chip->taos_settings.als_gain) { +	case 0: +		strcpy(gain, "001"); +		break; +	case 1: +		strcpy(gain, "008"); +		break; +	case 2: +		strcpy(gain, "016"); +		break; +	case 3: +		strcpy(gain, "111"); +		break; +	} + +	return sprintf(buf, "%s\n", gain); +} + +static ssize_t taos_gain_store(struct device *dev, +	struct device_attribute *attr, const char *buf, size_t len) +{ +	struct iio_dev *indio_dev = dev_to_iio_dev(dev); +	struct tsl2583_chip *chip = iio_priv(indio_dev); +	int value; + +	if (kstrtoint(buf, 0, &value)) +		return -EINVAL; + +	switch (value) { +	case 1: +		chip->taos_settings.als_gain = 0; +		break; +	case 8: +		chip->taos_settings.als_gain = 1; +		break; +	case 16: +		chip->taos_settings.als_gain = 2; +		break; +	case 111: +		chip->taos_settings.als_gain = 3; +		break; +	default: +		dev_err(dev, "Invalid Gain Index (must be 1,8,16,111)\n"); +		return -1; +	} + +	return len; +} + +static ssize_t taos_gain_available_show(struct device *dev, +	struct device_attribute *attr, char *buf) +{ +	return sprintf(buf, "%s\n", "1 8 16 111"); +} + +static ssize_t taos_als_time_show(struct device *dev, +	struct device_attribute *attr, char *buf) +{ +	struct iio_dev *indio_dev = dev_to_iio_dev(dev); +	struct tsl2583_chip *chip = iio_priv(indio_dev); + +	return sprintf(buf, "%d\n", chip->taos_settings.als_time); +} + +static ssize_t taos_als_time_store(struct device *dev, +	struct device_attribute *attr, const char *buf, size_t len) +{ +	struct iio_dev *indio_dev = dev_to_iio_dev(dev); +	struct tsl2583_chip *chip = iio_priv(indio_dev); +	int value; + +	if (kstrtoint(buf, 0, &value)) +		return -EINVAL; + +	if ((value < 50) || (value > 650)) +		return -EINVAL; + +	if (value % 50) +		return -EINVAL; + +	chip->taos_settings.als_time = value; + +	return len; +} + +static ssize_t taos_als_time_available_show(struct device *dev, +	struct device_attribute *attr, char *buf) +{ +	return sprintf(buf, "%s\n", +		"50 100 150 200 250 300 350 400 450 500 550 600 650"); +} + +static ssize_t taos_als_trim_show(struct device *dev, +	struct device_attribute *attr, char *buf) +{ +	struct iio_dev *indio_dev = dev_to_iio_dev(dev); +	struct tsl2583_chip *chip = iio_priv(indio_dev); + +	return sprintf(buf, "%d\n", chip->taos_settings.als_gain_trim); +} + +static ssize_t taos_als_trim_store(struct device *dev, +	struct device_attribute *attr, const char *buf, size_t len) +{ +	struct iio_dev *indio_dev = dev_to_iio_dev(dev); +	struct tsl2583_chip *chip = iio_priv(indio_dev); +	int value; + +	if (kstrtoint(buf, 0, &value)) +		return -EINVAL; + +	if (value) +		chip->taos_settings.als_gain_trim = value; + +	return len; +} + +static ssize_t taos_als_cal_target_show(struct device *dev, +	struct device_attribute *attr, char *buf) +{ +	struct iio_dev *indio_dev = dev_to_iio_dev(dev); +	struct tsl2583_chip *chip = iio_priv(indio_dev); + +	return sprintf(buf, "%d\n", chip->taos_settings.als_cal_target); +} + +static ssize_t taos_als_cal_target_store(struct device *dev, +	struct device_attribute *attr, const char *buf, size_t len) +{ +	struct iio_dev *indio_dev = dev_to_iio_dev(dev); +	struct tsl2583_chip *chip = iio_priv(indio_dev); +	int value; + +	if (kstrtoint(buf, 0, &value)) +		return -EINVAL; + +	if (value) +		chip->taos_settings.als_cal_target = value; + +	return len; +} + +static ssize_t taos_lux_show(struct device *dev, struct device_attribute *attr, +	char *buf) +{ +	int ret; + +	ret = taos_get_lux(dev_to_iio_dev(dev)); +	if (ret < 0) +		return ret; + +	return sprintf(buf, "%d\n", ret); +} + +static ssize_t taos_do_calibrate(struct device *dev, +	struct device_attribute *attr, const char *buf, size_t len) +{ +	struct iio_dev *indio_dev = dev_to_iio_dev(dev); +	int value; + +	if (kstrtoint(buf, 0, &value)) +		return -EINVAL; + +	if (value == 1) +		taos_als_calibrate(indio_dev); + +	return len; +} + +static ssize_t taos_luxtable_show(struct device *dev, +	struct device_attribute *attr, char *buf) +{ +	int i; +	int offset = 0; + +	for (i = 0; i < ARRAY_SIZE(taos_device_lux); i++) { +		offset += sprintf(buf + offset, "%d,%d,%d,", +				  taos_device_lux[i].ratio, +				  taos_device_lux[i].ch0, +				  taos_device_lux[i].ch1); +		if (taos_device_lux[i].ratio == 0) { +			/* We just printed the first "0" entry. +			 * Now get rid of the extra "," and break. */ +			offset--; +			break; +		} +	} + +	offset += sprintf(buf + offset, "\n"); +	return offset; +} + +static ssize_t taos_luxtable_store(struct device *dev, +	struct device_attribute *attr, const char *buf, size_t len) +{ +	struct iio_dev *indio_dev = dev_to_iio_dev(dev); +	struct tsl2583_chip *chip = iio_priv(indio_dev); +	int value[ARRAY_SIZE(taos_device_lux)*3 + 1]; +	int n; + +	get_options(buf, ARRAY_SIZE(value), value); + +	/* We now have an array of ints starting at value[1], and +	 * enumerated by value[0]. +	 * We expect each group of three ints is one table entry, +	 * and the last table entry is all 0. +	 */ +	n = value[0]; +	if ((n % 3) || n < 6 || n > ((ARRAY_SIZE(taos_device_lux) - 1) * 3)) { +		dev_info(dev, "LUX TABLE INPUT ERROR 1 Value[0]=%d\n", n); +		return -EINVAL; +	} +	if ((value[(n - 2)] | value[(n - 1)] | value[n]) != 0) { +		dev_info(dev, "LUX TABLE INPUT ERROR 2 Value[0]=%d\n", n); +		return -EINVAL; +	} + +	if (chip->taos_chip_status == TSL258X_CHIP_WORKING) +		taos_chip_off(indio_dev); + +	/* Zero out the table */ +	memset(taos_device_lux, 0, sizeof(taos_device_lux)); +	memcpy(taos_device_lux, &value[1], (value[0] * 4)); + +	taos_chip_on(indio_dev); + +	return len; +} + +static DEVICE_ATTR(power_state, S_IRUGO | S_IWUSR, +		taos_power_state_show, taos_power_state_store); + +static DEVICE_ATTR(illuminance0_calibscale, S_IRUGO | S_IWUSR, +		taos_gain_show, taos_gain_store); +static DEVICE_ATTR(illuminance0_calibscale_available, S_IRUGO, +		taos_gain_available_show, NULL); + +static DEVICE_ATTR(illuminance0_integration_time, S_IRUGO | S_IWUSR, +		taos_als_time_show, taos_als_time_store); +static DEVICE_ATTR(illuminance0_integration_time_available, S_IRUGO, +		taos_als_time_available_show, NULL); + +static DEVICE_ATTR(illuminance0_calibbias, S_IRUGO | S_IWUSR, +		taos_als_trim_show, taos_als_trim_store); + +static DEVICE_ATTR(illuminance0_input_target, S_IRUGO | S_IWUSR, +		taos_als_cal_target_show, taos_als_cal_target_store); + +static DEVICE_ATTR(illuminance0_input, S_IRUGO, taos_lux_show, NULL); +static DEVICE_ATTR(illuminance0_calibrate, S_IWUSR, NULL, taos_do_calibrate); +static DEVICE_ATTR(illuminance0_lux_table, S_IRUGO | S_IWUSR, +		taos_luxtable_show, taos_luxtable_store); + +static struct attribute *sysfs_attrs_ctrl[] = { +	&dev_attr_power_state.attr, +	&dev_attr_illuminance0_calibscale.attr,			/* Gain  */ +	&dev_attr_illuminance0_calibscale_available.attr, +	&dev_attr_illuminance0_integration_time.attr,	/* I time*/ +	&dev_attr_illuminance0_integration_time_available.attr, +	&dev_attr_illuminance0_calibbias.attr,			/* trim  */ +	&dev_attr_illuminance0_input_target.attr, +	&dev_attr_illuminance0_input.attr, +	&dev_attr_illuminance0_calibrate.attr, +	&dev_attr_illuminance0_lux_table.attr, +	NULL +}; + +static struct attribute_group tsl2583_attribute_group = { +	.attrs = sysfs_attrs_ctrl, +}; + +/* Use the default register values to identify the Taos device */ +static int taos_tsl258x_device(unsigned char *bufp) +{ +	return ((bufp[TSL258X_CHIPID] & 0xf0) == 0x90); +} + +static const struct iio_info tsl2583_info = { +	.attrs = &tsl2583_attribute_group, +	.driver_module = THIS_MODULE, +}; + +/* + * Client probe function - When a valid device is found, the driver's device + * data structure is updated, and initialization completes successfully. + */ +static int taos_probe(struct i2c_client *clientp, +		      const struct i2c_device_id *idp) +{ +	int i, ret; +	unsigned char buf[TSL258X_MAX_DEVICE_REGS]; +	struct tsl2583_chip *chip; +	struct iio_dev *indio_dev; + +	if (!i2c_check_functionality(clientp->adapter, +		I2C_FUNC_SMBUS_BYTE_DATA)) { +		dev_err(&clientp->dev, "taos_probe() - i2c smbus byte data func unsupported\n"); +		return -EOPNOTSUPP; +	} + +	indio_dev = devm_iio_device_alloc(&clientp->dev, sizeof(*chip)); +	if (!indio_dev) +		return -ENOMEM; +	chip = iio_priv(indio_dev); +	chip->client = clientp; +	i2c_set_clientdata(clientp, indio_dev); + +	mutex_init(&chip->als_mutex); +	chip->taos_chip_status = TSL258X_CHIP_UNKNOWN; +	memcpy(chip->taos_config, taos_config, sizeof(chip->taos_config)); + +	for (i = 0; i < TSL258X_MAX_DEVICE_REGS; i++) { +		ret = i2c_smbus_write_byte(clientp, +				(TSL258X_CMD_REG | (TSL258X_CNTRL + i))); +		if (ret < 0) { +			dev_err(&clientp->dev, +				"i2c_smbus_write_byte to cmd reg failed in taos_probe(), err = %d\n", +				ret); +			return ret; +		} +		ret = i2c_smbus_read_byte(clientp); +		if (ret < 0) { +			dev_err(&clientp->dev, +				"i2c_smbus_read_byte from reg failed in taos_probe(), err = %d\n", +				ret); +			return ret; +		} +		buf[i] = ret; +	} + +	if (!taos_tsl258x_device(buf)) { +		dev_info(&clientp->dev, +			"i2c device found but does not match expected id in taos_probe()\n"); +		return -EINVAL; +	} + +	ret = i2c_smbus_write_byte(clientp, (TSL258X_CMD_REG | TSL258X_CNTRL)); +	if (ret < 0) { +		dev_err(&clientp->dev, +			"i2c_smbus_write_byte() to cmd reg failed in taos_probe(), err = %d\n", +			ret); +		return ret; +	} + +	indio_dev->info = &tsl2583_info; +	indio_dev->dev.parent = &clientp->dev; +	indio_dev->modes = INDIO_DIRECT_MODE; +	indio_dev->name = chip->client->name; +	ret = iio_device_register(indio_dev); +	if (ret) { +		dev_err(&clientp->dev, "iio registration failed\n"); +		return ret; +	} + +	/* Load up the V2 defaults (these are hard coded defaults for now) */ +	taos_defaults(chip); + +	/* Make sure the chip is on */ +	taos_chip_on(indio_dev); + +	dev_info(&clientp->dev, "Light sensor found.\n"); +	return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int taos_suspend(struct device *dev) +{ +	struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); +	struct tsl2583_chip *chip = iio_priv(indio_dev); +	int ret = 0; + +	mutex_lock(&chip->als_mutex); + +	if (chip->taos_chip_status == TSL258X_CHIP_WORKING) { +		ret = taos_chip_off(indio_dev); +		chip->taos_chip_status = TSL258X_CHIP_SUSPENDED; +	} + +	mutex_unlock(&chip->als_mutex); +	return ret; +} + +static int taos_resume(struct device *dev) +{ +	struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); +	struct tsl2583_chip *chip = iio_priv(indio_dev); +	int ret = 0; + +	mutex_lock(&chip->als_mutex); + +	if (chip->taos_chip_status == TSL258X_CHIP_SUSPENDED) +		ret = taos_chip_on(indio_dev); + +	mutex_unlock(&chip->als_mutex); +	return ret; +} + +static SIMPLE_DEV_PM_OPS(taos_pm_ops, taos_suspend, taos_resume); +#define TAOS_PM_OPS (&taos_pm_ops) +#else +#define TAOS_PM_OPS NULL +#endif + +static int taos_remove(struct i2c_client *client) +{ +	iio_device_unregister(i2c_get_clientdata(client)); + +	return 0; +} + +static struct i2c_device_id taos_idtable[] = { +	{ "tsl2580", 0 }, +	{ "tsl2581", 1 }, +	{ "tsl2583", 2 }, +	{} +}; +MODULE_DEVICE_TABLE(i2c, taos_idtable); + +/* Driver definition */ +static struct i2c_driver taos_driver = { +	.driver = { +		.name = "tsl2583", +		.pm = TAOS_PM_OPS, +	}, +	.id_table = taos_idtable, +	.probe = taos_probe, +	.remove = taos_remove, +}; +module_i2c_driver(taos_driver); + +MODULE_AUTHOR("J. August Brenner<jbrenner@taosinc.com>"); +MODULE_DESCRIPTION("TAOS tsl2583 ambient light sensor driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/staging/iio/light/tsl2x7x.h b/drivers/staging/iio/light/tsl2x7x.h new file mode 100644 index 00000000000..c4acf5ff179 --- /dev/null +++ b/drivers/staging/iio/light/tsl2x7x.h @@ -0,0 +1,100 @@ +/* + * Device driver for monitoring ambient light intensity (lux) + * and proximity (prox) within the TAOS TSL2X7X family of devices. + * + * Copyright (c) 2012, TAOS Corporation. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA	02110-1301, USA. + */ + +#ifndef __TSL2X7X_H +#define __TSL2X7X_H +#include <linux/pm.h> + +/* Max number of segments allowable in LUX table */ +#define TSL2X7X_MAX_LUX_TABLE_SIZE		9 +#define MAX_DEFAULT_TABLE_BYTES (sizeof(int) * TSL2X7X_MAX_LUX_TABLE_SIZE) + +struct iio_dev; + +struct tsl2x7x_lux { +	unsigned int ratio; +	unsigned int ch0; +	unsigned int ch1; +}; + +/** + * struct tsl2x7x_default_settings - power on defaults unless + *                                   overridden by platform data. + *  @als_time:              ALS Integration time - multiple of 50mS + *  @als_gain:              Index into the ALS gain table. + *  @als_gain_trim:         default gain trim to account for + *                          aperture effects. + *  @wait_time:             Time between PRX and ALS cycles + *                          in 2.7 periods + *  @prx_time:              5.2ms prox integration time - + *                          decrease in 2.7ms periods + *  @prx_gain:              Proximity gain index + *  @prox_config:           Prox configuration filters. + *  @als_cal_target:        Known external ALS reading for + *                          calibration. + *  @interrupts_en:         Enable/Disable - 0x00 = none, 0x10 = als, + *                                           0x20 = prx,  0x30 = bth + *  @persistence:           H/W Filters, Number of 'out of limits' + *                          ADC readings PRX/ALS. + *  @als_thresh_low:        CH0 'low' count to trigger interrupt. + *  @als_thresh_high:       CH0 'high' count to trigger interrupt. + *  @prox_thres_low:        Low threshold proximity detection. + *  @prox_thres_high:       High threshold proximity detection + *  @prox_pulse_count:      Number if proximity emitter pulses + *  @prox_max_samples_cal:  Used for prox cal. + */ +struct tsl2x7x_settings { +	int als_time; +	int als_gain; +	int als_gain_trim; +	int wait_time; +	int prx_time; +	int prox_gain; +	int prox_config; +	int als_cal_target; +	u8  interrupts_en; +	u8  persistence; +	int als_thresh_low; +	int als_thresh_high; +	int prox_thres_low; +	int prox_thres_high; +	int prox_pulse_count; +	int prox_max_samples_cal; +}; + +/** + * struct tsl2X7X_platform_data - Platform callback, glass and defaults + * @platform_power:            Suspend/resume platform callback + * @power_on:                  Power on callback + * @power_off:                 Power off callback + * @platform_lux_table:        Device specific glass coefficents + * @platform_default_settings: Device specific power on defaults + * + */ +struct tsl2X7X_platform_data { +	int (*platform_power)(struct device *dev, pm_message_t); +	int (*power_on)      (struct iio_dev *indio_dev); +	int (*power_off)     (struct i2c_client *dev); +	struct tsl2x7x_lux platform_lux_table[TSL2X7X_MAX_LUX_TABLE_SIZE]; +	struct tsl2x7x_settings *platform_default_settings; +}; + +#endif /* __TSL2X7X_H */ diff --git a/drivers/staging/iio/light/tsl2x7x_core.c b/drivers/staging/iio/light/tsl2x7x_core.c new file mode 100644 index 00000000000..ab338e3ddd0 --- /dev/null +++ b/drivers/staging/iio/light/tsl2x7x_core.c @@ -0,0 +1,2037 @@ +/* + * Device driver for monitoring ambient light intensity in (lux) + * and proximity detection (prox) within the TAOS TSL2X7X family of devices. + * + * Copyright (c) 2012, TAOS Corporation. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA        02110-1301, USA. + */ + +#include <linux/kernel.h> +#include <linux/i2c.h> +#include <linux/errno.h> +#include <linux/delay.h> +#include <linux/mutex.h> +#include <linux/interrupt.h> +#include <linux/slab.h> +#include <linux/module.h> +#include <linux/iio/events.h> +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> +#include "tsl2x7x.h" + +/* Cal defs*/ +#define PROX_STAT_CAL        0 +#define PROX_STAT_SAMP       1 +#define MAX_SAMPLES_CAL      200 + +/* TSL2X7X Device ID */ +#define TRITON_ID    0x00 +#define SWORDFISH_ID 0x30 +#define HALIBUT_ID   0x20 + +/* Lux calculation constants */ +#define TSL2X7X_LUX_CALC_OVER_FLOW     65535 + +/* TAOS Register definitions - note: + * depending on device, some of these register are not used and the + * register address is benign. + */ +/* 2X7X register offsets */ +#define TSL2X7X_MAX_CONFIG_REG         16 + +/* Device Registers and Masks */ +#define TSL2X7X_CNTRL                  0x00 +#define TSL2X7X_ALS_TIME               0X01 +#define TSL2X7X_PRX_TIME               0x02 +#define TSL2X7X_WAIT_TIME              0x03 +#define TSL2X7X_ALS_MINTHRESHLO        0X04 +#define TSL2X7X_ALS_MINTHRESHHI        0X05 +#define TSL2X7X_ALS_MAXTHRESHLO        0X06 +#define TSL2X7X_ALS_MAXTHRESHHI        0X07 +#define TSL2X7X_PRX_MINTHRESHLO        0X08 +#define TSL2X7X_PRX_MINTHRESHHI        0X09 +#define TSL2X7X_PRX_MAXTHRESHLO        0X0A +#define TSL2X7X_PRX_MAXTHRESHHI        0X0B +#define TSL2X7X_PERSISTENCE            0x0C +#define TSL2X7X_PRX_CONFIG             0x0D +#define TSL2X7X_PRX_COUNT              0x0E +#define TSL2X7X_GAIN                   0x0F +#define TSL2X7X_NOTUSED                0x10 +#define TSL2X7X_REVID                  0x11 +#define TSL2X7X_CHIPID                 0x12 +#define TSL2X7X_STATUS                 0x13 +#define TSL2X7X_ALS_CHAN0LO            0x14 +#define TSL2X7X_ALS_CHAN0HI            0x15 +#define TSL2X7X_ALS_CHAN1LO            0x16 +#define TSL2X7X_ALS_CHAN1HI            0x17 +#define TSL2X7X_PRX_LO                 0x18 +#define TSL2X7X_PRX_HI                 0x19 + +/* tsl2X7X cmd reg masks */ +#define TSL2X7X_CMD_REG                0x80 +#define TSL2X7X_CMD_SPL_FN             0x60 + +#define TSL2X7X_CMD_PROX_INT_CLR       0X05 +#define TSL2X7X_CMD_ALS_INT_CLR        0x06 +#define TSL2X7X_CMD_PROXALS_INT_CLR    0X07 + +/* tsl2X7X cntrl reg masks */ +#define TSL2X7X_CNTL_ADC_ENBL          0x02 +#define TSL2X7X_CNTL_PWR_ON            0x01 + +/* tsl2X7X status reg masks */ +#define TSL2X7X_STA_ADC_VALID          0x01 +#define TSL2X7X_STA_PRX_VALID          0x02 +#define TSL2X7X_STA_ADC_PRX_VALID      (TSL2X7X_STA_ADC_VALID |\ +					TSL2X7X_STA_PRX_VALID) +#define TSL2X7X_STA_ALS_INTR           0x10 +#define TSL2X7X_STA_PRX_INTR           0x20 + +/* tsl2X7X cntrl reg masks */ +#define TSL2X7X_CNTL_REG_CLEAR         0x00 +#define TSL2X7X_CNTL_PROX_INT_ENBL     0X20 +#define TSL2X7X_CNTL_ALS_INT_ENBL      0X10 +#define TSL2X7X_CNTL_WAIT_TMR_ENBL     0X08 +#define TSL2X7X_CNTL_PROX_DET_ENBL     0X04 +#define TSL2X7X_CNTL_PWRON             0x01 +#define TSL2X7X_CNTL_ALSPON_ENBL       0x03 +#define TSL2X7X_CNTL_INTALSPON_ENBL    0x13 +#define TSL2X7X_CNTL_PROXPON_ENBL      0x0F +#define TSL2X7X_CNTL_INTPROXPON_ENBL   0x2F + +/*Prox diode to use */ +#define TSL2X7X_DIODE0                 0x10 +#define TSL2X7X_DIODE1                 0x20 +#define TSL2X7X_DIODE_BOTH             0x30 + +/* LED Power */ +#define TSL2X7X_mA100                  0x00 +#define TSL2X7X_mA50                   0x40 +#define TSL2X7X_mA25                   0x80 +#define TSL2X7X_mA13                   0xD0 +#define TSL2X7X_MAX_TIMER_CNT          (0xFF) + +#define TSL2X7X_MIN_ITIME 3 + +/* TAOS txx2x7x Device family members */ +enum { +	tsl2571, +	tsl2671, +	tmd2671, +	tsl2771, +	tmd2771, +	tsl2572, +	tsl2672, +	tmd2672, +	tsl2772, +	tmd2772 +}; + +enum { +	TSL2X7X_CHIP_UNKNOWN = 0, +	TSL2X7X_CHIP_WORKING = 1, +	TSL2X7X_CHIP_SUSPENDED = 2 +}; + +struct tsl2x7x_parse_result { +	int integer; +	int fract; +}; + +/* Per-device data */ +struct tsl2x7x_als_info { +	u16 als_ch0; +	u16 als_ch1; +	u16 lux; +}; + +struct tsl2x7x_prox_stat { +	int min; +	int max; +	int mean; +	unsigned long stddev; +}; + +struct tsl2x7x_chip_info { +	int chan_table_elements; +	struct iio_chan_spec		channel[4]; +	const struct iio_info		*info; +}; + +struct tsl2X7X_chip { +	kernel_ulong_t id; +	struct mutex prox_mutex; +	struct mutex als_mutex; +	struct i2c_client *client; +	u16 prox_data; +	struct tsl2x7x_als_info als_cur_info; +	struct tsl2x7x_settings tsl2x7x_settings; +	struct tsl2X7X_platform_data *pdata; +	int als_time_scale; +	int als_saturation; +	int tsl2x7x_chip_status; +	u8 tsl2x7x_config[TSL2X7X_MAX_CONFIG_REG]; +	const struct tsl2x7x_chip_info	*chip_info; +	const struct iio_info *info; +	s64 event_timestamp; +	/* This structure is intentionally large to accommodate +	 * updates via sysfs. */ +	/* Sized to 9 = max 8 segments + 1 termination segment */ +	struct tsl2x7x_lux tsl2x7x_device_lux[TSL2X7X_MAX_LUX_TABLE_SIZE]; +}; + +/* Different devices require different coefficents */ +static const struct tsl2x7x_lux tsl2x71_lux_table[] = { +	{ 14461,   611,   1211 }, +	{ 18540,   352,    623 }, +	{     0,     0,      0 }, +}; + +static const struct tsl2x7x_lux tmd2x71_lux_table[] = { +	{ 11635,   115,    256 }, +	{ 15536,    87,    179 }, +	{     0,     0,      0 }, +}; + +static const struct tsl2x7x_lux tsl2x72_lux_table[] = { +	{ 14013,   466,   917 }, +	{ 18222,   310,   552 }, +	{     0,     0,     0 }, +}; + +static const struct tsl2x7x_lux tmd2x72_lux_table[] = { +	{ 13218,   130,   262 }, +	{ 17592,   92,    169 }, +	{     0,     0,     0 }, +}; + +static const struct tsl2x7x_lux *tsl2x7x_default_lux_table_group[] = { +	[tsl2571] =	tsl2x71_lux_table, +	[tsl2671] =	tsl2x71_lux_table, +	[tmd2671] =	tmd2x71_lux_table, +	[tsl2771] =	tsl2x71_lux_table, +	[tmd2771] =	tmd2x71_lux_table, +	[tsl2572] =	tsl2x72_lux_table, +	[tsl2672] =	tsl2x72_lux_table, +	[tmd2672] =	tmd2x72_lux_table, +	[tsl2772] =	tsl2x72_lux_table, +	[tmd2772] =	tmd2x72_lux_table, +}; + +static const struct tsl2x7x_settings tsl2x7x_default_settings = { +	.als_time = 219, /* 101 ms */ +	.als_gain = 0, +	.prx_time = 254, /* 5.4 ms */ +	.prox_gain = 1, +	.wait_time = 245, +	.prox_config = 0, +	.als_gain_trim = 1000, +	.als_cal_target = 150, +	.als_thresh_low = 200, +	.als_thresh_high = 256, +	.persistence = 255, +	.interrupts_en = 0, +	.prox_thres_low  = 0, +	.prox_thres_high = 512, +	.prox_max_samples_cal = 30, +	.prox_pulse_count = 8 +}; + +static const s16 tsl2X7X_als_gainadj[] = { +	1, +	8, +	16, +	120 +}; + +static const s16 tsl2X7X_prx_gainadj[] = { +	1, +	2, +	4, +	8 +}; + +/* Channel variations */ +enum { +	ALS, +	PRX, +	ALSPRX, +	PRX2, +	ALSPRX2, +}; + +static const u8 device_channel_config[] = { +	ALS, +	PRX, +	PRX, +	ALSPRX, +	ALSPRX, +	ALS, +	PRX2, +	PRX2, +	ALSPRX2, +	ALSPRX2 +}; + +/** + * tsl2x7x_i2c_read() - Read a byte from a register. + * @client:	i2c client + * @reg:	device register to read from + * @*val:	pointer to location to store register contents. + * + */ +static int +tsl2x7x_i2c_read(struct i2c_client *client, u8 reg, u8 *val) +{ +	int ret = 0; + +	/* select register to write */ +	ret = i2c_smbus_write_byte(client, (TSL2X7X_CMD_REG | reg)); +	if (ret < 0) { +		dev_err(&client->dev, "%s: failed to write register %x\n" +				, __func__, reg); +		return ret; +	} + +	/* read the data */ +	ret = i2c_smbus_read_byte(client); +	if (ret >= 0) +		*val = (u8)ret; +	else +		dev_err(&client->dev, "%s: failed to read register %x\n" +						, __func__, reg); + +	return ret; +} + +/** + * tsl2x7x_get_lux() - Reads and calculates current lux value. + * @indio_dev:	pointer to IIO device + * + * The raw ch0 and ch1 values of the ambient light sensed in the last + * integration cycle are read from the device. + * Time scale factor array values are adjusted based on the integration time. + * The raw values are multiplied by a scale factor, and device gain is obtained + * using gain index. Limit checks are done next, then the ratio of a multiple + * of ch1 value, to the ch0 value, is calculated. Array tsl2x7x_device_lux[] + * is then scanned to find the first ratio value that is just above the ratio + * we just calculated. The ch0 and ch1 multiplier constants in the array are + * then used along with the time scale factor array values, to calculate the + * lux. + */ +static int tsl2x7x_get_lux(struct iio_dev *indio_dev) +{ +	u16 ch0, ch1; /* separated ch0/ch1 data from device */ +	u32 lux; /* raw lux calculated from device data */ +	u64 lux64; +	u32 ratio; +	u8 buf[4]; +	struct tsl2x7x_lux *p; +	struct tsl2X7X_chip *chip = iio_priv(indio_dev); +	int i, ret; +	u32 ch0lux = 0; +	u32 ch1lux = 0; + +	if (mutex_trylock(&chip->als_mutex) == 0) +		return chip->als_cur_info.lux; /* busy, so return LAST VALUE */ + +	if (chip->tsl2x7x_chip_status != TSL2X7X_CHIP_WORKING) { +		/* device is not enabled */ +		dev_err(&chip->client->dev, "%s: device is not enabled\n", +				__func__); +		ret = -EBUSY; +		goto out_unlock; +	} + +	ret = tsl2x7x_i2c_read(chip->client, +		(TSL2X7X_CMD_REG | TSL2X7X_STATUS), &buf[0]); +	if (ret < 0) { +		dev_err(&chip->client->dev, +			"%s: Failed to read STATUS Reg\n", __func__); +		goto out_unlock; +	} +	/* is data new & valid */ +	if (!(buf[0] & TSL2X7X_STA_ADC_VALID)) { +		dev_err(&chip->client->dev, +			"%s: data not valid yet\n", __func__); +		ret = chip->als_cur_info.lux; /* return LAST VALUE */ +		goto out_unlock; +	} + +	for (i = 0; i < 4; i++) { +		ret = tsl2x7x_i2c_read(chip->client, +			(TSL2X7X_CMD_REG | (TSL2X7X_ALS_CHAN0LO + i)), +			&buf[i]); +		if (ret < 0) { +			dev_err(&chip->client->dev, +				"%s: failed to read. err=%x\n", __func__, ret); +			goto out_unlock; +		} +	} + +	/* clear any existing interrupt status */ +	ret = i2c_smbus_write_byte(chip->client, +		(TSL2X7X_CMD_REG | +				TSL2X7X_CMD_SPL_FN | +				TSL2X7X_CMD_ALS_INT_CLR)); +	if (ret < 0) { +		dev_err(&chip->client->dev, +		"%s: i2c_write_command failed - err = %d\n", +			__func__, ret); +		goto out_unlock; /* have no data, so return failure */ +	} + +	/* extract ALS/lux data */ +	ch0 = le16_to_cpup((const __le16 *)&buf[0]); +	ch1 = le16_to_cpup((const __le16 *)&buf[2]); + +	chip->als_cur_info.als_ch0 = ch0; +	chip->als_cur_info.als_ch1 = ch1; + +	if ((ch0 >= chip->als_saturation) || (ch1 >= chip->als_saturation)) { +		lux = TSL2X7X_LUX_CALC_OVER_FLOW; +		goto return_max; +	} + +	if (ch0 == 0) { +		/* have no data, so return LAST VALUE */ +		ret = chip->als_cur_info.lux; +		goto out_unlock; +	} +	/* calculate ratio */ +	ratio = (ch1 << 15) / ch0; +	/* convert to unscaled lux using the pointer to the table */ +	p = (struct tsl2x7x_lux *) chip->tsl2x7x_device_lux; +	while (p->ratio != 0 && p->ratio < ratio) +		p++; + +	if (p->ratio == 0) { +		lux = 0; +	} else { +		ch0lux = DIV_ROUND_UP((ch0 * p->ch0), +			tsl2X7X_als_gainadj[chip->tsl2x7x_settings.als_gain]); +		ch1lux = DIV_ROUND_UP((ch1 * p->ch1), +			tsl2X7X_als_gainadj[chip->tsl2x7x_settings.als_gain]); +		lux = ch0lux - ch1lux; +	} + +	/* note: lux is 31 bit max at this point */ +	if (ch1lux > ch0lux) { +		dev_dbg(&chip->client->dev, "ch1lux > ch0lux-return last value\n"); +		ret = chip->als_cur_info.lux; +		goto out_unlock; +	} + +	/* adjust for active time scale */ +	if (chip->als_time_scale == 0) +		lux = 0; +	else +		lux = (lux + (chip->als_time_scale >> 1)) / +			chip->als_time_scale; + +	/* adjust for active gain scale +	 * The tsl2x7x_device_lux tables have a factor of 256 built-in. +	 * User-specified gain provides a multiplier. +	 * Apply user-specified gain before shifting right to retain precision. +	 * Use 64 bits to avoid overflow on multiplication. +	 * Then go back to 32 bits before division to avoid using div_u64(). +	 */ + +	lux64 = lux; +	lux64 = lux64 * chip->tsl2x7x_settings.als_gain_trim; +	lux64 >>= 8; +	lux = lux64; +	lux = (lux + 500) / 1000; + +	if (lux > TSL2X7X_LUX_CALC_OVER_FLOW) /* check for overflow */ +		lux = TSL2X7X_LUX_CALC_OVER_FLOW; + +	/* Update the structure with the latest lux. */ +return_max: +	chip->als_cur_info.lux = lux; +	ret = lux; + +out_unlock: +	mutex_unlock(&chip->als_mutex); + +	return ret; +} + +/** + * tsl2x7x_get_prox() - Reads proximity data registers and updates + *                      chip->prox_data. + * + * @indio_dev:	pointer to IIO device + */ +static int tsl2x7x_get_prox(struct iio_dev *indio_dev) +{ +	int i; +	int ret; +	u8 status; +	u8 chdata[2]; +	struct tsl2X7X_chip *chip = iio_priv(indio_dev); + +	if (mutex_trylock(&chip->prox_mutex) == 0) { +		dev_err(&chip->client->dev, +			"%s: Can't get prox mutex\n", __func__); +		return -EBUSY; +	} + +	ret = tsl2x7x_i2c_read(chip->client, +		(TSL2X7X_CMD_REG | TSL2X7X_STATUS), &status); +	if (ret < 0) { +		dev_err(&chip->client->dev, +		"%s: i2c err=%d\n", __func__, ret); +		goto prox_poll_err; +	} + +	switch (chip->id) { +	case tsl2571: +	case tsl2671: +	case tmd2671: +	case tsl2771: +	case tmd2771: +		if (!(status & TSL2X7X_STA_ADC_VALID)) +			goto prox_poll_err; +	break; +	case tsl2572: +	case tsl2672: +	case tmd2672: +	case tsl2772: +	case tmd2772: +		if (!(status & TSL2X7X_STA_PRX_VALID)) +			goto prox_poll_err; +	break; +	} + +	for (i = 0; i < 2; i++) { +		ret = tsl2x7x_i2c_read(chip->client, +			(TSL2X7X_CMD_REG | +					(TSL2X7X_PRX_LO + i)), &chdata[i]); +		if (ret < 0) +			goto prox_poll_err; +	} + +	chip->prox_data = +			le16_to_cpup((const __le16 *)&chdata[0]); + +prox_poll_err: + +	mutex_unlock(&chip->prox_mutex); + +	return chip->prox_data; +} + +/** + * tsl2x7x_defaults() - Populates the device nominal operating parameters + *                      with those provided by a 'platform' data struct or + *                      with prefined defaults. + * + * @chip:               pointer to device structure. + */ +static void tsl2x7x_defaults(struct tsl2X7X_chip *chip) +{ +	/* If Operational settings defined elsewhere.. */ +	if (chip->pdata && chip->pdata->platform_default_settings) +		memcpy(&(chip->tsl2x7x_settings), +			chip->pdata->platform_default_settings, +			sizeof(tsl2x7x_default_settings)); +	else +		memcpy(&(chip->tsl2x7x_settings), +			&tsl2x7x_default_settings, +			sizeof(tsl2x7x_default_settings)); + +	/* Load up the proper lux table. */ +	if (chip->pdata && chip->pdata->platform_lux_table[0].ratio != 0) +		memcpy(chip->tsl2x7x_device_lux, +			chip->pdata->platform_lux_table, +			sizeof(chip->pdata->platform_lux_table)); +	else +		memcpy(chip->tsl2x7x_device_lux, +		(struct tsl2x7x_lux *)tsl2x7x_default_lux_table_group[chip->id], +				MAX_DEFAULT_TABLE_BYTES); +} + +/** + * tsl2x7x_als_calibrate() -	Obtain single reading and calculate + *                              the als_gain_trim. + * + * @indio_dev:	pointer to IIO device + */ +static int tsl2x7x_als_calibrate(struct iio_dev *indio_dev) +{ +	struct tsl2X7X_chip *chip = iio_priv(indio_dev); +	u8 reg_val; +	int gain_trim_val; +	int ret; +	int lux_val; + +	ret = i2c_smbus_write_byte(chip->client, +			(TSL2X7X_CMD_REG | TSL2X7X_CNTRL)); +	if (ret < 0) { +		dev_err(&chip->client->dev, +		"%s: failed to write CNTRL register, ret=%d\n", +		__func__, ret); +		return ret; +	} + +	reg_val = i2c_smbus_read_byte(chip->client); +	if ((reg_val & (TSL2X7X_CNTL_ADC_ENBL | TSL2X7X_CNTL_PWR_ON)) +		!= (TSL2X7X_CNTL_ADC_ENBL | TSL2X7X_CNTL_PWR_ON)) { +		dev_err(&chip->client->dev, +			"%s: failed: ADC not enabled\n", __func__); +		return -1; +	} + +	ret = i2c_smbus_write_byte(chip->client, +			(TSL2X7X_CMD_REG | TSL2X7X_CNTRL)); +	if (ret < 0) { +		dev_err(&chip->client->dev, +			"%s: failed to write ctrl reg: ret=%d\n", +			__func__, ret); +		return ret; +	} + +	reg_val = i2c_smbus_read_byte(chip->client); +	if ((reg_val & TSL2X7X_STA_ADC_VALID) != TSL2X7X_STA_ADC_VALID) { +		dev_err(&chip->client->dev, +			"%s: failed: STATUS - ADC not valid.\n", __func__); +		return -ENODATA; +	} + +	lux_val = tsl2x7x_get_lux(indio_dev); +	if (lux_val < 0) { +		dev_err(&chip->client->dev, +		"%s: failed to get lux\n", __func__); +		return lux_val; +	} + +	gain_trim_val =  (((chip->tsl2x7x_settings.als_cal_target) +			* chip->tsl2x7x_settings.als_gain_trim) / lux_val); +	if ((gain_trim_val < 250) || (gain_trim_val > 4000)) +		return -ERANGE; + +	chip->tsl2x7x_settings.als_gain_trim = gain_trim_val; +	dev_info(&chip->client->dev, +		"%s als_calibrate completed\n", chip->client->name); + +	return (int) gain_trim_val; +} + +static int tsl2x7x_chip_on(struct iio_dev *indio_dev) +{ +	int i; +	int ret = 0; +	u8 *dev_reg; +	u8 utmp; +	int als_count; +	int als_time; +	struct tsl2X7X_chip *chip = iio_priv(indio_dev); +	u8 reg_val = 0; + +	if (chip->pdata && chip->pdata->power_on) +		chip->pdata->power_on(indio_dev); + +	/* Non calculated parameters */ +	chip->tsl2x7x_config[TSL2X7X_PRX_TIME] = +			chip->tsl2x7x_settings.prx_time; +	chip->tsl2x7x_config[TSL2X7X_WAIT_TIME] = +			chip->tsl2x7x_settings.wait_time; +	chip->tsl2x7x_config[TSL2X7X_PRX_CONFIG] = +			chip->tsl2x7x_settings.prox_config; + +	chip->tsl2x7x_config[TSL2X7X_ALS_MINTHRESHLO] = +		(chip->tsl2x7x_settings.als_thresh_low) & 0xFF; +	chip->tsl2x7x_config[TSL2X7X_ALS_MINTHRESHHI] = +		(chip->tsl2x7x_settings.als_thresh_low >> 8) & 0xFF; +	chip->tsl2x7x_config[TSL2X7X_ALS_MAXTHRESHLO] = +		(chip->tsl2x7x_settings.als_thresh_high) & 0xFF; +	chip->tsl2x7x_config[TSL2X7X_ALS_MAXTHRESHHI] = +		(chip->tsl2x7x_settings.als_thresh_high >> 8) & 0xFF; +	chip->tsl2x7x_config[TSL2X7X_PERSISTENCE] = +		chip->tsl2x7x_settings.persistence; + +	chip->tsl2x7x_config[TSL2X7X_PRX_COUNT] = +			chip->tsl2x7x_settings.prox_pulse_count; +	chip->tsl2x7x_config[TSL2X7X_PRX_MINTHRESHLO] = +			(chip->tsl2x7x_settings.prox_thres_low) & 0xFF; +	chip->tsl2x7x_config[TSL2X7X_PRX_MINTHRESHHI] = +			(chip->tsl2x7x_settings.prox_thres_low >> 8) & 0xFF; +	chip->tsl2x7x_config[TSL2X7X_PRX_MAXTHRESHLO] = +			(chip->tsl2x7x_settings.prox_thres_high) & 0xFF; +	chip->tsl2x7x_config[TSL2X7X_PRX_MAXTHRESHHI] = +			(chip->tsl2x7x_settings.prox_thres_high >> 8) & 0xFF; + +	/* and make sure we're not already on */ +	if (chip->tsl2x7x_chip_status == TSL2X7X_CHIP_WORKING) { +		/* if forcing a register update - turn off, then on */ +		dev_info(&chip->client->dev, "device is already enabled\n"); +		return -EINVAL; +	} + +	/* determine als integration register */ +	als_count = (chip->tsl2x7x_settings.als_time * 100 + 135) / 270; +	if (als_count == 0) +		als_count = 1; /* ensure at least one cycle */ + +	/* convert back to time (encompasses overrides) */ +	als_time = (als_count * 27 + 5) / 10; +	chip->tsl2x7x_config[TSL2X7X_ALS_TIME] = 256 - als_count; + +	/* Set the gain based on tsl2x7x_settings struct */ +	chip->tsl2x7x_config[TSL2X7X_GAIN] = +		(chip->tsl2x7x_settings.als_gain | +			(TSL2X7X_mA100 | TSL2X7X_DIODE1) +			| ((chip->tsl2x7x_settings.prox_gain) << 2)); + +	/* set chip struct re scaling and saturation */ +	chip->als_saturation = als_count * 922; /* 90% of full scale */ +	chip->als_time_scale = (als_time + 25) / 50; + +	/* TSL2X7X Specific power-on / adc enable sequence +	 * Power on the device 1st. */ +	utmp = TSL2X7X_CNTL_PWR_ON; +	ret = i2c_smbus_write_byte_data(chip->client, +		TSL2X7X_CMD_REG | TSL2X7X_CNTRL, utmp); +	if (ret < 0) { +		dev_err(&chip->client->dev, +			"%s: failed on CNTRL reg.\n", __func__); +		return ret; +	} + +	/* Use the following shadow copy for our delay before enabling ADC. +	 * Write all the registers. */ +	for (i = 0, dev_reg = chip->tsl2x7x_config; +			i < TSL2X7X_MAX_CONFIG_REG; i++) { +		ret = i2c_smbus_write_byte_data(chip->client, +				TSL2X7X_CMD_REG + i, *dev_reg++); +		if (ret < 0) { +			dev_err(&chip->client->dev, +			"%s: failed on write to reg %d.\n", __func__, i); +			return ret; +		} +	} + +	mdelay(3);	/* Power-on settling time */ + +	/* NOW enable the ADC +	 * initialize the desired mode of operation */ +	utmp = TSL2X7X_CNTL_PWR_ON | +			TSL2X7X_CNTL_ADC_ENBL | +			TSL2X7X_CNTL_PROX_DET_ENBL; +	ret = i2c_smbus_write_byte_data(chip->client, +			TSL2X7X_CMD_REG | TSL2X7X_CNTRL, utmp); +	if (ret < 0) { +		dev_err(&chip->client->dev, +			"%s: failed on 2nd CTRL reg.\n", __func__); +		return ret; +	} + +	chip->tsl2x7x_chip_status = TSL2X7X_CHIP_WORKING; + +	if (chip->tsl2x7x_settings.interrupts_en != 0) { +		dev_info(&chip->client->dev, "Setting Up Interrupt(s)\n"); + +		reg_val = TSL2X7X_CNTL_PWR_ON | TSL2X7X_CNTL_ADC_ENBL; +		if ((chip->tsl2x7x_settings.interrupts_en == 0x20) || +			(chip->tsl2x7x_settings.interrupts_en == 0x30)) +			reg_val |= TSL2X7X_CNTL_PROX_DET_ENBL; + +		reg_val |= chip->tsl2x7x_settings.interrupts_en; +		ret = i2c_smbus_write_byte_data(chip->client, +			(TSL2X7X_CMD_REG | TSL2X7X_CNTRL), reg_val); +		if (ret < 0) +			dev_err(&chip->client->dev, +				"%s: failed in tsl2x7x_IOCTL_INT_SET.\n", +				__func__); + +		/* Clear out any initial interrupts  */ +		ret = i2c_smbus_write_byte(chip->client, +			TSL2X7X_CMD_REG | TSL2X7X_CMD_SPL_FN | +			TSL2X7X_CMD_PROXALS_INT_CLR); +		if (ret < 0) { +			dev_err(&chip->client->dev, +				"%s: Failed to clear Int status\n", +				__func__); +		return ret; +		} +	} + +	return ret; +} + +static int tsl2x7x_chip_off(struct iio_dev *indio_dev) +{ +	int ret; +	struct tsl2X7X_chip *chip = iio_priv(indio_dev); + +	/* turn device off */ +	chip->tsl2x7x_chip_status = TSL2X7X_CHIP_SUSPENDED; + +	ret = i2c_smbus_write_byte_data(chip->client, +		TSL2X7X_CMD_REG | TSL2X7X_CNTRL, 0x00); + +	if (chip->pdata && chip->pdata->power_off) +		chip->pdata->power_off(chip->client); + +	return ret; +} + +/** + * tsl2x7x_invoke_change + * @indio_dev:	pointer to IIO device + * + * Obtain and lock both ALS and PROX resources, + * determine and save device state (On/Off), + * cycle device to implement updated parameter, + * put device back into proper state, and unlock + * resource. + */ +static +int tsl2x7x_invoke_change(struct iio_dev *indio_dev) +{ +	struct tsl2X7X_chip *chip = iio_priv(indio_dev); +	int device_status = chip->tsl2x7x_chip_status; + +	mutex_lock(&chip->als_mutex); +	mutex_lock(&chip->prox_mutex); + +	if (device_status == TSL2X7X_CHIP_WORKING) +		tsl2x7x_chip_off(indio_dev); + +	tsl2x7x_chip_on(indio_dev); + +	if (device_status != TSL2X7X_CHIP_WORKING) +		tsl2x7x_chip_off(indio_dev); + +	mutex_unlock(&chip->prox_mutex); +	mutex_unlock(&chip->als_mutex); + +	return 0; +} + +static +void tsl2x7x_prox_calculate(int *data, int length, +		struct tsl2x7x_prox_stat *statP) +{ +	int i; +	int sample_sum; +	int tmp; + +	if (length == 0) +		length = 1; + +	sample_sum = 0; +	statP->min = INT_MAX; +	statP->max = INT_MIN; +	for (i = 0; i < length; i++) { +		sample_sum += data[i]; +		statP->min = min(statP->min, data[i]); +		statP->max = max(statP->max, data[i]); +	} + +	statP->mean = sample_sum / length; +	sample_sum = 0; +	for (i = 0; i < length; i++) { +		tmp = data[i] - statP->mean; +		sample_sum += tmp * tmp; +	} +	statP->stddev = int_sqrt((long)sample_sum)/length; +} + +/** + * tsl2x7x_prox_cal() - Calculates std. and sets thresholds. + * @indio_dev:	pointer to IIO device + * + * Calculates a standard deviation based on the samples, + * and sets the threshold accordingly. + */ +static void tsl2x7x_prox_cal(struct iio_dev *indio_dev) +{ +	int prox_history[MAX_SAMPLES_CAL + 1]; +	int i; +	struct tsl2x7x_prox_stat prox_stat_data[2]; +	struct tsl2x7x_prox_stat *calP; +	struct tsl2X7X_chip *chip = iio_priv(indio_dev); +	u8 tmp_irq_settings; +	u8 current_state = chip->tsl2x7x_chip_status; + +	if (chip->tsl2x7x_settings.prox_max_samples_cal > MAX_SAMPLES_CAL) { +		dev_err(&chip->client->dev, +			"%s: max prox samples cal is too big: %d\n", +			__func__, chip->tsl2x7x_settings.prox_max_samples_cal); +		chip->tsl2x7x_settings.prox_max_samples_cal = MAX_SAMPLES_CAL; +	} + +	/* have to stop to change settings */ +	tsl2x7x_chip_off(indio_dev); + +	/* Enable proximity detection save just in case prox not wanted yet*/ +	tmp_irq_settings = chip->tsl2x7x_settings.interrupts_en; +	chip->tsl2x7x_settings.interrupts_en |= TSL2X7X_CNTL_PROX_INT_ENBL; + +	/*turn on device if not already on*/ +	tsl2x7x_chip_on(indio_dev); + +	/*gather the samples*/ +	for (i = 0; i < chip->tsl2x7x_settings.prox_max_samples_cal; i++) { +		mdelay(15); +		tsl2x7x_get_prox(indio_dev); +		prox_history[i] = chip->prox_data; +		dev_info(&chip->client->dev, "2 i=%d prox data= %d\n", +			i, chip->prox_data); +	} + +	tsl2x7x_chip_off(indio_dev); +	calP = &prox_stat_data[PROX_STAT_CAL]; +	tsl2x7x_prox_calculate(prox_history, +		chip->tsl2x7x_settings.prox_max_samples_cal, calP); +	chip->tsl2x7x_settings.prox_thres_high = (calP->max << 1) - calP->mean; + +	dev_info(&chip->client->dev, " cal min=%d mean=%d max=%d\n", +		calP->min, calP->mean, calP->max); +	dev_info(&chip->client->dev, +		"%s proximity threshold set to %d\n", +		chip->client->name, chip->tsl2x7x_settings.prox_thres_high); + +	/* back to the way they were */ +	chip->tsl2x7x_settings.interrupts_en = tmp_irq_settings; +	if (current_state == TSL2X7X_CHIP_WORKING) +		tsl2x7x_chip_on(indio_dev); +} + +static ssize_t tsl2x7x_power_state_show(struct device *dev, +	struct device_attribute *attr, char *buf) +{ +	struct tsl2X7X_chip *chip = iio_priv(dev_to_iio_dev(dev)); + +	return snprintf(buf, PAGE_SIZE, "%d\n", chip->tsl2x7x_chip_status); +} + +static ssize_t tsl2x7x_power_state_store(struct device *dev, +	struct device_attribute *attr, const char *buf, size_t len) +{ +	struct iio_dev *indio_dev = dev_to_iio_dev(dev); +	bool value; + +	if (strtobool(buf, &value)) +		return -EINVAL; + +	if (value) +		tsl2x7x_chip_on(indio_dev); +	else +		tsl2x7x_chip_off(indio_dev); + +	return len; +} + +static ssize_t tsl2x7x_gain_available_show(struct device *dev, +	struct device_attribute *attr, char *buf) +{ +	struct tsl2X7X_chip *chip = iio_priv(dev_to_iio_dev(dev)); + +	switch (chip->id) { +	case tsl2571: +	case tsl2671: +	case tmd2671: +	case tsl2771: +	case tmd2771: +		return snprintf(buf, PAGE_SIZE, "%s\n", "1 8 16 128"); +	} + +	return snprintf(buf, PAGE_SIZE, "%s\n", "1 8 16 120"); +} + +static ssize_t tsl2x7x_prox_gain_available_show(struct device *dev, +	struct device_attribute *attr, char *buf) +{ +		return snprintf(buf, PAGE_SIZE, "%s\n", "1 2 4 8"); +} + +static ssize_t tsl2x7x_als_time_show(struct device *dev, +	struct device_attribute *attr, char *buf) +{ +	struct tsl2X7X_chip *chip = iio_priv(dev_to_iio_dev(dev)); +	int y, z; + +	y = (TSL2X7X_MAX_TIMER_CNT - (u8)chip->tsl2x7x_settings.als_time) + 1; +	z = y * TSL2X7X_MIN_ITIME; +	y /= 1000; +	z %= 1000; + +	return snprintf(buf, PAGE_SIZE, "%d.%03d\n", y, z); +} + +static ssize_t tsl2x7x_als_time_store(struct device *dev, +	struct device_attribute *attr, const char *buf, size_t len) +{ +	struct iio_dev *indio_dev = dev_to_iio_dev(dev); +	struct tsl2X7X_chip *chip = iio_priv(indio_dev); +	struct tsl2x7x_parse_result result; +	int ret; + +	ret = iio_str_to_fixpoint(buf, 100, &result.integer, &result.fract); +	if (ret) +		return ret; + +	result.fract /= 3; +	chip->tsl2x7x_settings.als_time = +			(TSL2X7X_MAX_TIMER_CNT - (u8)result.fract); + +	dev_info(&chip->client->dev, "%s: als time = %d", +		__func__, chip->tsl2x7x_settings.als_time); + +	tsl2x7x_invoke_change(indio_dev); + +	return IIO_VAL_INT_PLUS_MICRO; +} + +static IIO_CONST_ATTR(in_illuminance0_integration_time_available, +		".00272 - .696"); + +static ssize_t tsl2x7x_als_cal_target_show(struct device *dev, +	struct device_attribute *attr, char *buf) +{ +	struct tsl2X7X_chip *chip = iio_priv(dev_to_iio_dev(dev)); + +	return snprintf(buf, PAGE_SIZE, "%d\n", +			chip->tsl2x7x_settings.als_cal_target); +} + +static ssize_t tsl2x7x_als_cal_target_store(struct device *dev, +	struct device_attribute *attr, const char *buf, size_t len) +{ +	struct iio_dev *indio_dev = dev_to_iio_dev(dev); +	struct tsl2X7X_chip *chip = iio_priv(indio_dev); +	unsigned long value; + +	if (kstrtoul(buf, 0, &value)) +		return -EINVAL; + +	if (value) +		chip->tsl2x7x_settings.als_cal_target = value; + +	tsl2x7x_invoke_change(indio_dev); + +	return len; +} + +/* persistence settings */ +static ssize_t tsl2x7x_als_persistence_show(struct device *dev, +	struct device_attribute *attr, char *buf) +{ +	struct tsl2X7X_chip *chip = iio_priv(dev_to_iio_dev(dev)); +	int y, z, filter_delay; + +	/* Determine integration time */ +	y = (TSL2X7X_MAX_TIMER_CNT - (u8)chip->tsl2x7x_settings.als_time) + 1; +	z = y * TSL2X7X_MIN_ITIME; +	filter_delay = z * (chip->tsl2x7x_settings.persistence & 0x0F); +	y = (filter_delay / 1000); +	z = (filter_delay % 1000); + +	return snprintf(buf, PAGE_SIZE, "%d.%03d\n", y, z); +} + +static ssize_t tsl2x7x_als_persistence_store(struct device *dev, +	struct device_attribute *attr, const char *buf, size_t len) +{ +	struct iio_dev *indio_dev = dev_to_iio_dev(dev); +	struct tsl2X7X_chip *chip = iio_priv(indio_dev); +	struct tsl2x7x_parse_result result; +	int y, z, filter_delay; +	int ret; + +	ret = iio_str_to_fixpoint(buf, 100, &result.integer, &result.fract); +	if (ret) +		return ret; + +	y = (TSL2X7X_MAX_TIMER_CNT - (u8)chip->tsl2x7x_settings.als_time) + 1; +	z = y * TSL2X7X_MIN_ITIME; + +	filter_delay = +		DIV_ROUND_UP(((result.integer * 1000) + result.fract), z); + +	chip->tsl2x7x_settings.persistence &= 0xF0; +	chip->tsl2x7x_settings.persistence |= (filter_delay & 0x0F); + +	dev_info(&chip->client->dev, "%s: als persistence = %d", +		__func__, filter_delay); + +	tsl2x7x_invoke_change(indio_dev); + +	return IIO_VAL_INT_PLUS_MICRO; +} + +static ssize_t tsl2x7x_prox_persistence_show(struct device *dev, +	struct device_attribute *attr, char *buf) +{ +	struct tsl2X7X_chip *chip = iio_priv(dev_to_iio_dev(dev)); +	int y, z, filter_delay; + +	/* Determine integration time */ +	y = (TSL2X7X_MAX_TIMER_CNT - (u8)chip->tsl2x7x_settings.prx_time) + 1; +	z = y * TSL2X7X_MIN_ITIME; +	filter_delay = z * ((chip->tsl2x7x_settings.persistence & 0xF0) >> 4); +	y = (filter_delay / 1000); +	z = (filter_delay % 1000); + +	return snprintf(buf, PAGE_SIZE, "%d.%03d\n", y, z); +} + +static ssize_t tsl2x7x_prox_persistence_store(struct device *dev, +	struct device_attribute *attr, const char *buf, size_t len) +{ +	struct iio_dev *indio_dev = dev_to_iio_dev(dev); +	struct tsl2X7X_chip *chip = iio_priv(indio_dev); +	struct tsl2x7x_parse_result result; +	int y, z, filter_delay; +	int ret; + +	ret = iio_str_to_fixpoint(buf, 100, &result.integer, &result.fract); +	if (ret) +		return ret; + +	y = (TSL2X7X_MAX_TIMER_CNT - (u8)chip->tsl2x7x_settings.prx_time) + 1; +	z = y * TSL2X7X_MIN_ITIME; + +	filter_delay = +		DIV_ROUND_UP(((result.integer * 1000) + result.fract), z); + +	chip->tsl2x7x_settings.persistence &= 0x0F; +	chip->tsl2x7x_settings.persistence |= ((filter_delay << 4) & 0xF0); + +	dev_info(&chip->client->dev, "%s: prox persistence = %d", +		__func__, filter_delay); + +	tsl2x7x_invoke_change(indio_dev); + +	return IIO_VAL_INT_PLUS_MICRO; +} + +static ssize_t tsl2x7x_do_calibrate(struct device *dev, +	struct device_attribute *attr, const char *buf, size_t len) +{ +	struct iio_dev *indio_dev = dev_to_iio_dev(dev); +	bool value; + +	if (strtobool(buf, &value)) +		return -EINVAL; + +	if (value) +		tsl2x7x_als_calibrate(indio_dev); + +	tsl2x7x_invoke_change(indio_dev); + +	return len; +} + +static ssize_t tsl2x7x_luxtable_show(struct device *dev, +	struct device_attribute *attr, char *buf) +{ +	struct tsl2X7X_chip *chip = iio_priv(dev_to_iio_dev(dev)); +	int i = 0; +	int offset = 0; + +	while (i < (TSL2X7X_MAX_LUX_TABLE_SIZE * 3)) { +		offset += snprintf(buf + offset, PAGE_SIZE, "%d,%d,%d,", +			chip->tsl2x7x_device_lux[i].ratio, +			chip->tsl2x7x_device_lux[i].ch0, +			chip->tsl2x7x_device_lux[i].ch1); +		if (chip->tsl2x7x_device_lux[i].ratio == 0) { +			/* We just printed the first "0" entry. +			 * Now get rid of the extra "," and break. */ +			offset--; +			break; +		} +		i++; +	} + +	offset += snprintf(buf + offset, PAGE_SIZE, "\n"); +	return offset; +} + +static ssize_t tsl2x7x_luxtable_store(struct device *dev, +	struct device_attribute *attr, const char *buf, size_t len) +{ +	struct iio_dev *indio_dev = dev_to_iio_dev(dev); +	struct tsl2X7X_chip *chip = iio_priv(indio_dev); +	int value[ARRAY_SIZE(chip->tsl2x7x_device_lux)*3 + 1]; +	int n; + +	get_options(buf, ARRAY_SIZE(value), value); + +	/* We now have an array of ints starting at value[1], and +	 * enumerated by value[0]. +	 * We expect each group of three ints is one table entry, +	 * and the last table entry is all 0. +	 */ +	n = value[0]; +	if ((n % 3) || n < 6 || +			n > ((ARRAY_SIZE(chip->tsl2x7x_device_lux) - 1) * 3)) { +		dev_info(dev, "LUX TABLE INPUT ERROR 1 Value[0]=%d\n", n); +		return -EINVAL; +	} + +	if ((value[(n - 2)] | value[(n - 1)] | value[n]) != 0) { +		dev_info(dev, "LUX TABLE INPUT ERROR 2 Value[0]=%d\n", n); +		return -EINVAL; +	} + +	if (chip->tsl2x7x_chip_status == TSL2X7X_CHIP_WORKING) +		tsl2x7x_chip_off(indio_dev); + +	/* Zero out the table */ +	memset(chip->tsl2x7x_device_lux, 0, sizeof(chip->tsl2x7x_device_lux)); +	memcpy(chip->tsl2x7x_device_lux, &value[1], (value[0] * 4)); + +	tsl2x7x_invoke_change(indio_dev); + +	return len; +} + +static ssize_t tsl2x7x_do_prox_calibrate(struct device *dev, +	struct device_attribute *attr, const char *buf, size_t len) +{ +	struct iio_dev *indio_dev = dev_to_iio_dev(dev); +	bool value; + +	if (strtobool(buf, &value)) +		return -EINVAL; + +	if (value) +		tsl2x7x_prox_cal(indio_dev); + +	tsl2x7x_invoke_change(indio_dev); + +	return len; +} + +static int tsl2x7x_read_interrupt_config(struct iio_dev *indio_dev, +					 const struct iio_chan_spec *chan, +					 enum iio_event_type type, +					 enum iio_event_direction dir) +{ +	struct tsl2X7X_chip *chip = iio_priv(indio_dev); +	int ret; + +	if (chan->type == IIO_INTENSITY) +		ret = !!(chip->tsl2x7x_settings.interrupts_en & 0x10); +	else +		ret = !!(chip->tsl2x7x_settings.interrupts_en & 0x20); + +	return ret; +} + +static int tsl2x7x_write_interrupt_config(struct iio_dev *indio_dev, +					  const struct iio_chan_spec *chan, +					  enum iio_event_type type, +					  enum iio_event_direction dir, +					  int val) +{ +	struct tsl2X7X_chip *chip = iio_priv(indio_dev); + +	if (chan->type == IIO_INTENSITY) { +		if (val) +			chip->tsl2x7x_settings.interrupts_en |= 0x10; +		else +			chip->tsl2x7x_settings.interrupts_en &= 0x20; +	} else { +		if (val) +			chip->tsl2x7x_settings.interrupts_en |= 0x20; +		else +			chip->tsl2x7x_settings.interrupts_en &= 0x10; +	} + +	tsl2x7x_invoke_change(indio_dev); + +	return 0; +} + +static int tsl2x7x_write_thresh(struct iio_dev *indio_dev, +				const struct iio_chan_spec *chan, +				enum iio_event_type type, +				enum iio_event_direction dir, +				enum iio_event_info info, +				int val, int val2) +{ +	struct tsl2X7X_chip *chip = iio_priv(indio_dev); + +	if (chan->type == IIO_INTENSITY) { +		switch (dir) { +		case IIO_EV_DIR_RISING: +			chip->tsl2x7x_settings.als_thresh_high = val; +			break; +		case IIO_EV_DIR_FALLING: +			chip->tsl2x7x_settings.als_thresh_low = val; +			break; +		default: +			return -EINVAL; +		} +	} else { +		switch (dir) { +		case IIO_EV_DIR_RISING: +			chip->tsl2x7x_settings.prox_thres_high = val; +			break; +		case IIO_EV_DIR_FALLING: +			chip->tsl2x7x_settings.prox_thres_low = val; +			break; +		default: +			return -EINVAL; +		} +	} + +	tsl2x7x_invoke_change(indio_dev); + +	return 0; +} + +static int tsl2x7x_read_thresh(struct iio_dev *indio_dev, +			       const struct iio_chan_spec *chan, +			       enum iio_event_type type, +			       enum iio_event_direction dir, +				   enum iio_event_info info, +			       int *val, int *val2) +{ +	struct tsl2X7X_chip *chip = iio_priv(indio_dev); + +	if (chan->type == IIO_INTENSITY) { +		switch (dir) { +		case IIO_EV_DIR_RISING: +			*val = chip->tsl2x7x_settings.als_thresh_high; +			break; +		case IIO_EV_DIR_FALLING: +			*val = chip->tsl2x7x_settings.als_thresh_low; +			break; +		default: +			return -EINVAL; +		} +	} else { +		switch (dir) { +		case IIO_EV_DIR_RISING: +			*val = chip->tsl2x7x_settings.prox_thres_high; +			break; +		case IIO_EV_DIR_FALLING: +			*val = chip->tsl2x7x_settings.prox_thres_low; +			break; +		default: +			return -EINVAL; +		} +	} + +	return IIO_VAL_INT; +} + +static int tsl2x7x_read_raw(struct iio_dev *indio_dev, +			    struct iio_chan_spec const *chan, +			    int *val, +			    int *val2, +			    long mask) +{ +	int ret = -EINVAL; +	struct tsl2X7X_chip *chip = iio_priv(indio_dev); + +	switch (mask) { +	case IIO_CHAN_INFO_PROCESSED: +		switch (chan->type) { +		case IIO_LIGHT: +			tsl2x7x_get_lux(indio_dev); +			*val = chip->als_cur_info.lux; +			ret = IIO_VAL_INT; +			break; +		default: +			return -EINVAL; +		} +		break; +	case IIO_CHAN_INFO_RAW: +		switch (chan->type) { +		case IIO_INTENSITY: +			tsl2x7x_get_lux(indio_dev); +			if (chan->channel == 0) +				*val = chip->als_cur_info.als_ch0; +			else +				*val = chip->als_cur_info.als_ch1; +			ret = IIO_VAL_INT; +			break; +		case IIO_PROXIMITY: +			tsl2x7x_get_prox(indio_dev); +			*val = chip->prox_data; +			ret = IIO_VAL_INT; +			break; +		default: +			return -EINVAL; +		} +		break; +	case IIO_CHAN_INFO_CALIBSCALE: +		if (chan->type == IIO_LIGHT) +			*val = +			tsl2X7X_als_gainadj[chip->tsl2x7x_settings.als_gain]; +		else +			*val = +			tsl2X7X_prx_gainadj[chip->tsl2x7x_settings.prox_gain]; +		ret = IIO_VAL_INT; +		break; +	case IIO_CHAN_INFO_CALIBBIAS: +		*val = chip->tsl2x7x_settings.als_gain_trim; +		ret = IIO_VAL_INT; +		break; + +	default: +		ret = -EINVAL; +	} + +	return ret; +} + +static int tsl2x7x_write_raw(struct iio_dev *indio_dev, +			       struct iio_chan_spec const *chan, +			       int val, +			       int val2, +			       long mask) +{ +	struct tsl2X7X_chip *chip = iio_priv(indio_dev); + +	switch (mask) { +	case IIO_CHAN_INFO_CALIBSCALE: +		if (chan->type == IIO_INTENSITY) { +			switch (val) { +			case 1: +				chip->tsl2x7x_settings.als_gain = 0; +				break; +			case 8: +				chip->tsl2x7x_settings.als_gain = 1; +				break; +			case 16: +				chip->tsl2x7x_settings.als_gain = 2; +				break; +			case 120: +				switch (chip->id) { +				case tsl2572: +				case tsl2672: +				case tmd2672: +				case tsl2772: +				case tmd2772: +					return -EINVAL; +				} +				chip->tsl2x7x_settings.als_gain = 3; +				break; +			case 128: +				switch (chip->id) { +				case tsl2571: +				case tsl2671: +				case tmd2671: +				case tsl2771: +				case tmd2771: +					return -EINVAL; +				} +				chip->tsl2x7x_settings.als_gain = 3; +				break; +			default: +				return -EINVAL; +			} +		} else { +			switch (val) { +			case 1: +				chip->tsl2x7x_settings.prox_gain = 0; +				break; +			case 2: +				chip->tsl2x7x_settings.prox_gain = 1; +				break; +			case 4: +				chip->tsl2x7x_settings.prox_gain = 2; +				break; +			case 8: +				chip->tsl2x7x_settings.prox_gain = 3; +				break; +			default: +				return -EINVAL; +			} +		} +		break; +	case IIO_CHAN_INFO_CALIBBIAS: +		chip->tsl2x7x_settings.als_gain_trim = val; +		break; + +	default: +		return -EINVAL; +	} + +	tsl2x7x_invoke_change(indio_dev); + +	return 0; +} + +static DEVICE_ATTR(power_state, S_IRUGO | S_IWUSR, +		tsl2x7x_power_state_show, tsl2x7x_power_state_store); + +static DEVICE_ATTR(in_proximity0_calibscale_available, S_IRUGO, +		tsl2x7x_prox_gain_available_show, NULL); + +static DEVICE_ATTR(in_illuminance0_calibscale_available, S_IRUGO, +		tsl2x7x_gain_available_show, NULL); + +static DEVICE_ATTR(in_illuminance0_integration_time, S_IRUGO | S_IWUSR, +		tsl2x7x_als_time_show, tsl2x7x_als_time_store); + +static DEVICE_ATTR(in_illuminance0_target_input, S_IRUGO | S_IWUSR, +		tsl2x7x_als_cal_target_show, tsl2x7x_als_cal_target_store); + +static DEVICE_ATTR(in_illuminance0_calibrate, S_IWUSR, NULL, +		tsl2x7x_do_calibrate); + +static DEVICE_ATTR(in_proximity0_calibrate, S_IWUSR, NULL, +		tsl2x7x_do_prox_calibrate); + +static DEVICE_ATTR(in_illuminance0_lux_table, S_IRUGO | S_IWUSR, +		tsl2x7x_luxtable_show, tsl2x7x_luxtable_store); + +static DEVICE_ATTR(in_intensity0_thresh_period, S_IRUGO | S_IWUSR, +		tsl2x7x_als_persistence_show, tsl2x7x_als_persistence_store); + +static DEVICE_ATTR(in_proximity0_thresh_period, S_IRUGO | S_IWUSR, +		tsl2x7x_prox_persistence_show, tsl2x7x_prox_persistence_store); + +/* Use the default register values to identify the Taos device */ +static int tsl2x7x_device_id(unsigned char *id, int target) +{ +	switch (target) { +	case tsl2571: +	case tsl2671: +	case tsl2771: +		return (*id & 0xf0) == TRITON_ID; +	case tmd2671: +	case tmd2771: +		return (*id & 0xf0) == HALIBUT_ID; +	case tsl2572: +	case tsl2672: +	case tmd2672: +	case tsl2772: +	case tmd2772: +		return (*id & 0xf0) == SWORDFISH_ID; +	} + +	return -EINVAL; +} + +static irqreturn_t tsl2x7x_event_handler(int irq, void *private) +{ +	struct iio_dev *indio_dev = private; +	struct tsl2X7X_chip *chip = iio_priv(indio_dev); +	s64 timestamp = iio_get_time_ns(); +	int ret; +	u8 value; + +	value = i2c_smbus_read_byte_data(chip->client, +		TSL2X7X_CMD_REG | TSL2X7X_STATUS); + +	/* What type of interrupt do we need to process */ +	if (value & TSL2X7X_STA_PRX_INTR) { +		tsl2x7x_get_prox(indio_dev); /* freshen data for ABI */ +		iio_push_event(indio_dev, +			       IIO_UNMOD_EVENT_CODE(IIO_PROXIMITY, +						    0, +						    IIO_EV_TYPE_THRESH, +						    IIO_EV_DIR_EITHER), +						    timestamp); +	} + +	if (value & TSL2X7X_STA_ALS_INTR) { +		tsl2x7x_get_lux(indio_dev); /* freshen data for ABI */ +		iio_push_event(indio_dev, +		       IIO_UNMOD_EVENT_CODE(IIO_LIGHT, +					    0, +					    IIO_EV_TYPE_THRESH, +					    IIO_EV_DIR_EITHER), +					    timestamp); +	} +	/* Clear interrupt now that we have handled it. */ +	ret = i2c_smbus_write_byte(chip->client, +		TSL2X7X_CMD_REG | TSL2X7X_CMD_SPL_FN | +		TSL2X7X_CMD_PROXALS_INT_CLR); +	if (ret < 0) +		dev_err(&chip->client->dev, +			"%s: Failed to clear irq from event handler. err = %d\n", +			__func__, ret); + +	return IRQ_HANDLED; +} + +static struct attribute *tsl2x7x_ALS_device_attrs[] = { +	&dev_attr_power_state.attr, +	&dev_attr_in_illuminance0_calibscale_available.attr, +	&dev_attr_in_illuminance0_integration_time.attr, +	&iio_const_attr_in_illuminance0_integration_time_available\ +	.dev_attr.attr, +	&dev_attr_in_illuminance0_target_input.attr, +	&dev_attr_in_illuminance0_calibrate.attr, +	&dev_attr_in_illuminance0_lux_table.attr, +	NULL +}; + +static struct attribute *tsl2x7x_PRX_device_attrs[] = { +	&dev_attr_power_state.attr, +	&dev_attr_in_proximity0_calibrate.attr, +	NULL +}; + +static struct attribute *tsl2x7x_ALSPRX_device_attrs[] = { +	&dev_attr_power_state.attr, +	&dev_attr_in_illuminance0_calibscale_available.attr, +	&dev_attr_in_illuminance0_integration_time.attr, +	&iio_const_attr_in_illuminance0_integration_time_available\ +	.dev_attr.attr, +	&dev_attr_in_illuminance0_target_input.attr, +	&dev_attr_in_illuminance0_calibrate.attr, +	&dev_attr_in_illuminance0_lux_table.attr, +	&dev_attr_in_proximity0_calibrate.attr, +	NULL +}; + +static struct attribute *tsl2x7x_PRX2_device_attrs[] = { +	&dev_attr_power_state.attr, +	&dev_attr_in_proximity0_calibrate.attr, +	&dev_attr_in_proximity0_calibscale_available.attr, +	NULL +}; + +static struct attribute *tsl2x7x_ALSPRX2_device_attrs[] = { +	&dev_attr_power_state.attr, +	&dev_attr_in_illuminance0_calibscale_available.attr, +	&dev_attr_in_illuminance0_integration_time.attr, +	&iio_const_attr_in_illuminance0_integration_time_available\ +	.dev_attr.attr, +	&dev_attr_in_illuminance0_target_input.attr, +	&dev_attr_in_illuminance0_calibrate.attr, +	&dev_attr_in_illuminance0_lux_table.attr, +	&dev_attr_in_proximity0_calibrate.attr, +	&dev_attr_in_proximity0_calibscale_available.attr, +	NULL +}; + +static struct attribute *tsl2X7X_ALS_event_attrs[] = { +	&dev_attr_in_intensity0_thresh_period.attr, +	NULL, +}; +static struct attribute *tsl2X7X_PRX_event_attrs[] = { +	&dev_attr_in_proximity0_thresh_period.attr, +	NULL, +}; + +static struct attribute *tsl2X7X_ALSPRX_event_attrs[] = { +	&dev_attr_in_intensity0_thresh_period.attr, +	&dev_attr_in_proximity0_thresh_period.attr, +	NULL, +}; + +static const struct attribute_group tsl2X7X_device_attr_group_tbl[] = { +	[ALS] = { +		.attrs = tsl2x7x_ALS_device_attrs, +	}, +	[PRX] = { +		.attrs = tsl2x7x_PRX_device_attrs, +	}, +	[ALSPRX] = { +		.attrs = tsl2x7x_ALSPRX_device_attrs, +	}, +	[PRX2] = { +		.attrs = tsl2x7x_PRX2_device_attrs, +	}, +	[ALSPRX2] = { +		.attrs = tsl2x7x_ALSPRX2_device_attrs, +	}, +}; + +static struct attribute_group tsl2X7X_event_attr_group_tbl[] = { +	[ALS] = { +		.attrs = tsl2X7X_ALS_event_attrs, +		.name = "events", +	}, +	[PRX] = { +		.attrs = tsl2X7X_PRX_event_attrs, +		.name = "events", +	}, +	[ALSPRX] = { +		.attrs = tsl2X7X_ALSPRX_event_attrs, +		.name = "events", +	}, +}; + +static const struct iio_info tsl2X7X_device_info[] = { +	[ALS] = { +		.attrs = &tsl2X7X_device_attr_group_tbl[ALS], +		.event_attrs = &tsl2X7X_event_attr_group_tbl[ALS], +		.driver_module = THIS_MODULE, +		.read_raw = &tsl2x7x_read_raw, +		.write_raw = &tsl2x7x_write_raw, +		.read_event_value = &tsl2x7x_read_thresh, +		.write_event_value = &tsl2x7x_write_thresh, +		.read_event_config = &tsl2x7x_read_interrupt_config, +		.write_event_config = &tsl2x7x_write_interrupt_config, +	}, +	[PRX] = { +		.attrs = &tsl2X7X_device_attr_group_tbl[PRX], +		.event_attrs = &tsl2X7X_event_attr_group_tbl[PRX], +		.driver_module = THIS_MODULE, +		.read_raw = &tsl2x7x_read_raw, +		.write_raw = &tsl2x7x_write_raw, +		.read_event_value = &tsl2x7x_read_thresh, +		.write_event_value = &tsl2x7x_write_thresh, +		.read_event_config = &tsl2x7x_read_interrupt_config, +		.write_event_config = &tsl2x7x_write_interrupt_config, +	}, +	[ALSPRX] = { +		.attrs = &tsl2X7X_device_attr_group_tbl[ALSPRX], +		.event_attrs = &tsl2X7X_event_attr_group_tbl[ALSPRX], +		.driver_module = THIS_MODULE, +		.read_raw = &tsl2x7x_read_raw, +		.write_raw = &tsl2x7x_write_raw, +		.read_event_value = &tsl2x7x_read_thresh, +		.write_event_value = &tsl2x7x_write_thresh, +		.read_event_config = &tsl2x7x_read_interrupt_config, +		.write_event_config = &tsl2x7x_write_interrupt_config, +	}, +	[PRX2] = { +		.attrs = &tsl2X7X_device_attr_group_tbl[PRX2], +		.event_attrs = &tsl2X7X_event_attr_group_tbl[PRX], +		.driver_module = THIS_MODULE, +		.read_raw = &tsl2x7x_read_raw, +		.write_raw = &tsl2x7x_write_raw, +		.read_event_value = &tsl2x7x_read_thresh, +		.write_event_value = &tsl2x7x_write_thresh, +		.read_event_config = &tsl2x7x_read_interrupt_config, +		.write_event_config = &tsl2x7x_write_interrupt_config, +	}, +	[ALSPRX2] = { +		.attrs = &tsl2X7X_device_attr_group_tbl[ALSPRX2], +		.event_attrs = &tsl2X7X_event_attr_group_tbl[ALSPRX], +		.driver_module = THIS_MODULE, +		.read_raw = &tsl2x7x_read_raw, +		.write_raw = &tsl2x7x_write_raw, +		.read_event_value = &tsl2x7x_read_thresh, +		.write_event_value = &tsl2x7x_write_thresh, +		.read_event_config = &tsl2x7x_read_interrupt_config, +		.write_event_config = &tsl2x7x_write_interrupt_config, +	}, +}; + +static const struct iio_event_spec tsl2x7x_events[] = { +	{ +		.type = IIO_EV_TYPE_THRESH, +		.dir = IIO_EV_DIR_RISING, +		.mask_separate = BIT(IIO_EV_INFO_VALUE) | +			BIT(IIO_EV_INFO_ENABLE), +	}, { +		.type = IIO_EV_TYPE_THRESH, +		.dir = IIO_EV_DIR_FALLING, +		.mask_separate = BIT(IIO_EV_INFO_VALUE) | +			BIT(IIO_EV_INFO_ENABLE), +	}, +}; + +static const struct tsl2x7x_chip_info tsl2x7x_chip_info_tbl[] = { +	[ALS] = { +		.channel = { +			{ +			.type = IIO_LIGHT, +			.indexed = 1, +			.channel = 0, +			.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), +			}, { +			.type = IIO_INTENSITY, +			.indexed = 1, +			.channel = 0, +			.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | +				BIT(IIO_CHAN_INFO_CALIBSCALE) | +				BIT(IIO_CHAN_INFO_CALIBBIAS), +			.event_spec = tsl2x7x_events, +			.num_event_specs = ARRAY_SIZE(tsl2x7x_events), +			}, { +			.type = IIO_INTENSITY, +			.indexed = 1, +			.channel = 1, +			}, +		}, +	.chan_table_elements = 3, +	.info = &tsl2X7X_device_info[ALS], +	}, +	[PRX] = { +		.channel = { +			{ +			.type = IIO_PROXIMITY, +			.indexed = 1, +			.channel = 0, +			.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), +			.event_spec = tsl2x7x_events, +			.num_event_specs = ARRAY_SIZE(tsl2x7x_events), +			}, +		}, +	.chan_table_elements = 1, +	.info = &tsl2X7X_device_info[PRX], +	}, +	[ALSPRX] = { +		.channel = { +			{ +			.type = IIO_LIGHT, +			.indexed = 1, +			.channel = 0, +			.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED) +			}, { +			.type = IIO_INTENSITY, +			.indexed = 1, +			.channel = 0, +			.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | +				BIT(IIO_CHAN_INFO_CALIBSCALE) | +				BIT(IIO_CHAN_INFO_CALIBBIAS), +			.event_spec = tsl2x7x_events, +			.num_event_specs = ARRAY_SIZE(tsl2x7x_events), +			}, { +			.type = IIO_INTENSITY, +			.indexed = 1, +			.channel = 1, +			.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), +			}, { +			.type = IIO_PROXIMITY, +			.indexed = 1, +			.channel = 0, +			.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), +			.event_spec = tsl2x7x_events, +			.num_event_specs = ARRAY_SIZE(tsl2x7x_events), +			}, +		}, +	.chan_table_elements = 4, +	.info = &tsl2X7X_device_info[ALSPRX], +	}, +	[PRX2] = { +		.channel = { +			{ +			.type = IIO_PROXIMITY, +			.indexed = 1, +			.channel = 0, +			.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | +				BIT(IIO_CHAN_INFO_CALIBSCALE), +			.event_spec = tsl2x7x_events, +			.num_event_specs = ARRAY_SIZE(tsl2x7x_events), +			}, +		}, +	.chan_table_elements = 1, +	.info = &tsl2X7X_device_info[PRX2], +	}, +	[ALSPRX2] = { +		.channel = { +			{ +			.type = IIO_LIGHT, +			.indexed = 1, +			.channel = 0, +			.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), +			}, { +			.type = IIO_INTENSITY, +			.indexed = 1, +			.channel = 0, +			.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | +				BIT(IIO_CHAN_INFO_CALIBSCALE) | +				BIT(IIO_CHAN_INFO_CALIBBIAS), +			.event_spec = tsl2x7x_events, +			.num_event_specs = ARRAY_SIZE(tsl2x7x_events), +			}, { +			.type = IIO_INTENSITY, +			.indexed = 1, +			.channel = 1, +			.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), +			}, { +			.type = IIO_PROXIMITY, +			.indexed = 1, +			.channel = 0, +			.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | +				BIT(IIO_CHAN_INFO_CALIBSCALE), +			.event_spec = tsl2x7x_events, +			.num_event_specs = ARRAY_SIZE(tsl2x7x_events), +			}, +		}, +	.chan_table_elements = 4, +	.info = &tsl2X7X_device_info[ALSPRX2], +	}, +}; + +static int tsl2x7x_probe(struct i2c_client *clientp, +	const struct i2c_device_id *id) +{ +	int ret; +	unsigned char device_id; +	struct iio_dev *indio_dev; +	struct tsl2X7X_chip *chip; + +	indio_dev = devm_iio_device_alloc(&clientp->dev, sizeof(*chip)); +	if (!indio_dev) +		return -ENOMEM; + +	chip = iio_priv(indio_dev); +	chip->client = clientp; +	i2c_set_clientdata(clientp, indio_dev); + +	ret = tsl2x7x_i2c_read(chip->client, +		TSL2X7X_CHIPID, &device_id); +	if (ret < 0) +		return ret; + +	if ((!tsl2x7x_device_id(&device_id, id->driver_data)) || +		(tsl2x7x_device_id(&device_id, id->driver_data) == -EINVAL)) { +		dev_info(&chip->client->dev, +				"%s: i2c device found does not match expected id\n", +				__func__); +		return -EINVAL; +	} + +	ret = i2c_smbus_write_byte(clientp, (TSL2X7X_CMD_REG | TSL2X7X_CNTRL)); +	if (ret < 0) { +		dev_err(&clientp->dev, "%s: write to cmd reg failed. err = %d\n", +				__func__, ret); +		return ret; +	} + +	/* ALS and PROX functions can be invoked via user space poll +	 * or H/W interrupt. If busy return last sample. */ +	mutex_init(&chip->als_mutex); +	mutex_init(&chip->prox_mutex); + +	chip->tsl2x7x_chip_status = TSL2X7X_CHIP_UNKNOWN; +	chip->pdata = clientp->dev.platform_data; +	chip->id = id->driver_data; +	chip->chip_info = +		&tsl2x7x_chip_info_tbl[device_channel_config[id->driver_data]]; + +	indio_dev->info = chip->chip_info->info; +	indio_dev->dev.parent = &clientp->dev; +	indio_dev->modes = INDIO_DIRECT_MODE; +	indio_dev->name = chip->client->name; +	indio_dev->channels = chip->chip_info->channel; +	indio_dev->num_channels = chip->chip_info->chan_table_elements; + +	if (clientp->irq) { +		ret = devm_request_threaded_irq(&clientp->dev, clientp->irq, +						NULL, +						&tsl2x7x_event_handler, +						IRQF_TRIGGER_RISING | +						IRQF_ONESHOT, +						"TSL2X7X_event", +						indio_dev); +		if (ret) { +			dev_err(&clientp->dev, +				"%s: irq request failed", __func__); +			return ret; +		} +	} + +	/* Load up the defaults */ +	tsl2x7x_defaults(chip); +	/* Make sure the chip is on */ +	tsl2x7x_chip_on(indio_dev); + +	ret = iio_device_register(indio_dev); +	if (ret) { +		dev_err(&clientp->dev, +			"%s: iio registration failed\n", __func__); +		return ret; +	} + +	dev_info(&clientp->dev, "%s Light sensor found.\n", id->name); + +	return 0; +} + +static int tsl2x7x_suspend(struct device *dev) +{ +	struct iio_dev *indio_dev = dev_get_drvdata(dev); +	struct tsl2X7X_chip *chip = iio_priv(indio_dev); +	int ret = 0; + +	if (chip->tsl2x7x_chip_status == TSL2X7X_CHIP_WORKING) { +		ret = tsl2x7x_chip_off(indio_dev); +		chip->tsl2x7x_chip_status = TSL2X7X_CHIP_SUSPENDED; +	} + +	if (chip->pdata && chip->pdata->platform_power) { +		pm_message_t pmm = {PM_EVENT_SUSPEND}; +		chip->pdata->platform_power(dev, pmm); +	} + +	return ret; +} + +static int tsl2x7x_resume(struct device *dev) +{ +	struct iio_dev *indio_dev = dev_get_drvdata(dev); +	struct tsl2X7X_chip *chip = iio_priv(indio_dev); +	int ret = 0; + +	if (chip->pdata && chip->pdata->platform_power) { +		pm_message_t pmm = {PM_EVENT_RESUME}; +		chip->pdata->platform_power(dev, pmm); +	} + +	if (chip->tsl2x7x_chip_status == TSL2X7X_CHIP_SUSPENDED) +		ret = tsl2x7x_chip_on(indio_dev); + +	return ret; +} + +static int tsl2x7x_remove(struct i2c_client *client) +{ +	struct iio_dev *indio_dev = i2c_get_clientdata(client); + +	tsl2x7x_chip_off(indio_dev); + +	iio_device_unregister(indio_dev); + +	return 0; +} + +static struct i2c_device_id tsl2x7x_idtable[] = { +	{ "tsl2571", tsl2571 }, +	{ "tsl2671", tsl2671 }, +	{ "tmd2671", tmd2671 }, +	{ "tsl2771", tsl2771 }, +	{ "tmd2771", tmd2771 }, +	{ "tsl2572", tsl2572 }, +	{ "tsl2672", tsl2672 }, +	{ "tmd2672", tmd2672 }, +	{ "tsl2772", tsl2772 }, +	{ "tmd2772", tmd2772 }, +	{} +}; + +MODULE_DEVICE_TABLE(i2c, tsl2x7x_idtable); + +static const struct dev_pm_ops tsl2x7x_pm_ops = { +	.suspend = tsl2x7x_suspend, +	.resume  = tsl2x7x_resume, +}; + +/* Driver definition */ +static struct i2c_driver tsl2x7x_driver = { +	.driver = { +		.name = "tsl2x7x", +		.pm = &tsl2x7x_pm_ops, +	}, +	.id_table = tsl2x7x_idtable, +	.probe = tsl2x7x_probe, +	.remove = tsl2x7x_remove, +}; + +module_i2c_driver(tsl2x7x_driver); + +MODULE_AUTHOR("J. August Brenner<jbrenner@taosinc.com>"); +MODULE_DESCRIPTION("TAOS tsl2x7x ambient and proximity light sensor driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/staging/iio/magnetometer/Kconfig b/drivers/staging/iio/magnetometer/Kconfig index 81b579d371d..34634da1f9f 100644 --- a/drivers/staging/iio/magnetometer/Kconfig +++ b/drivers/staging/iio/magnetometer/Kconfig @@ -1,25 +1,18 @@  #  # Magnetometer sensors  # -comment "Magnetometer sensors" - -config SENSORS_AK8975 -	tristate "Asahi Kasei AK8975 3-Axis Magnetometer" -	depends on I2C -	help -	  Say yes here to build support for Asahi Kasei AK8975 3-Axis -	  Magnetometer. - -	  To compile this driver as a module, choose M here: the module -	  will be called ak8975. +menu "Magnetometer sensors"  config SENSORS_HMC5843 -	tristate "Honeywell HMC5843 3-Axis Magnetometer" +	tristate "Honeywell HMC5843/5883/5883L 3-Axis Magnetometer"  	depends on I2C +	select IIO_BUFFER +	select IIO_TRIGGERED_BUFFER  	help -	  Say Y here to add support for the Honeywell HMC 5843 3-Axis -	  Magnetometer (digital compass). +	  Say Y here to add support for the Honeywell HMC5843, HMC5883 and +	  HMC5883L 3-Axis Magnetometer (digital compass).  	  To compile this driver as a module, choose M here: the module -	  will be called hmc5843 +	  will be called hmc5843. +endmenu diff --git a/drivers/staging/iio/magnetometer/Makefile b/drivers/staging/iio/magnetometer/Makefile index f2a753f8079..f9bfb2e11d7 100644 --- a/drivers/staging/iio/magnetometer/Makefile +++ b/drivers/staging/iio/magnetometer/Makefile @@ -2,5 +2,4 @@  # Makefile for industrial I/O Magnetometer sensors  # -obj-$(CONFIG_SENSORS_AK8975)	+= ak8975.o  obj-$(CONFIG_SENSORS_HMC5843)	+= hmc5843.o diff --git a/drivers/staging/iio/magnetometer/ak8975.c b/drivers/staging/iio/magnetometer/ak8975.c deleted file mode 100644 index 420f206cf51..00000000000 --- a/drivers/staging/iio/magnetometer/ak8975.c +++ /dev/null @@ -1,558 +0,0 @@ -/* - * A sensor driver for the magnetometer AK8975. - * - * Magnetic compass sensor driver for monitoring magnetic flux information. - * - * Copyright (c) 2010, NVIDIA Corporation. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA	02110-1301, USA. - */ - -#include <linux/module.h> -#include <linux/kernel.h> -#include <linux/slab.h> -#include <linux/i2c.h> -#include <linux/err.h> -#include <linux/mutex.h> -#include <linux/delay.h> - -#include <linux/gpio.h> - -#include "../iio.h" -#include "magnet.h" - -/* - * Register definitions, as well as various shifts and masks to get at the - * individual fields of the registers. - */ -#define AK8975_REG_WIA			0x00 -#define AK8975_DEVICE_ID		0x48 - -#define AK8975_REG_INFO			0x01 - -#define AK8975_REG_ST1			0x02 -#define AK8975_REG_ST1_DRDY_SHIFT	0 -#define AK8975_REG_ST1_DRDY_MASK	(1 << AK8975_REG_ST1_DRDY_SHIFT) - -#define AK8975_REG_HXL			0x03 -#define AK8975_REG_HXH			0x04 -#define AK8975_REG_HYL			0x05 -#define AK8975_REG_HYH			0x06 -#define AK8975_REG_HZL			0x07 -#define AK8975_REG_HZH			0x08 -#define AK8975_REG_ST2			0x09 -#define AK8975_REG_ST2_DERR_SHIFT	2 -#define AK8975_REG_ST2_DERR_MASK	(1 << AK8975_REG_ST2_DERR_SHIFT) - -#define AK8975_REG_ST2_HOFL_SHIFT	3 -#define AK8975_REG_ST2_HOFL_MASK	(1 << AK8975_REG_ST2_HOFL_SHIFT) - -#define AK8975_REG_CNTL			0x0A -#define AK8975_REG_CNTL_MODE_SHIFT	0 -#define AK8975_REG_CNTL_MODE_MASK	(0xF << AK8975_REG_CNTL_MODE_SHIFT) -#define AK8975_REG_CNTL_MODE_POWER_DOWN	0 -#define AK8975_REG_CNTL_MODE_ONCE	1 -#define AK8975_REG_CNTL_MODE_SELF_TEST	8 -#define AK8975_REG_CNTL_MODE_FUSE_ROM	0xF - -#define AK8975_REG_RSVC			0x0B -#define AK8975_REG_ASTC			0x0C -#define AK8975_REG_TS1			0x0D -#define AK8975_REG_TS2			0x0E -#define AK8975_REG_I2CDIS		0x0F -#define AK8975_REG_ASAX			0x10 -#define AK8975_REG_ASAY			0x11 -#define AK8975_REG_ASAZ			0x12 - -#define AK8975_MAX_REGS			AK8975_REG_ASAZ - -/* - * Miscellaneous values. - */ -#define AK8975_MAX_CONVERSION_TIMEOUT	500 -#define AK8975_CONVERSION_DONE_POLL_TIME 10 - -/* - * Per-instance context data for the device. - */ -struct ak8975_data { -	struct i2c_client	*client; -	struct iio_dev		*indio_dev; -	struct attribute_group	attrs; -	struct mutex		lock; -	u8			asa[3]; -	long			raw_to_gauss[3]; -	unsigned long		mode; -	u8			reg_cache[AK8975_MAX_REGS]; -	int			eoc_gpio; -	int			eoc_irq; -}; - -/* - * Helper function to write to the I2C device's registers. - */ -static int ak8975_write_data(struct i2c_client *client, -			     u8 reg, u8 val, u8 mask, u8 shift) -{ -	u8 regval; -	struct i2c_msg msg; -	u8 w_data[2]; -	int ret = 0; - -	struct ak8975_data *data = i2c_get_clientdata(client); - -	regval = data->reg_cache[reg]; -	regval &= ~mask; -	regval |= val << shift; - -	w_data[0] = reg; -	w_data[1] = regval; - -	msg.addr = client->addr; -	msg.flags = 0; -	msg.len = 2; -	msg.buf = w_data; - -	ret = i2c_transfer(client->adapter, &msg, 1); -	if (ret < 0) { -		dev_err(&client->dev, "Write to device fails status %x\n", ret); -		return ret; -	} -	data->reg_cache[reg] = regval; - -	return 0; -} - -/* - * Helper function to read a contiguous set of the I2C device's registers. - */ -static int ak8975_read_data(struct i2c_client *client, -			    u8 reg, u8 length, u8 *buffer) -{ -	struct i2c_msg msg[2]; -	u8 w_data[2]; -	int ret; - -	w_data[0] = reg; - -	msg[0].addr = client->addr; -	msg[0].flags = I2C_M_NOSTART;	/* set repeated start and write */ -	msg[0].len = 1; -	msg[0].buf = w_data; - -	msg[1].addr = client->addr; -	msg[1].flags = I2C_M_RD; -	msg[1].len = length; -	msg[1].buf = buffer; - -	ret = i2c_transfer(client->adapter, msg, 2); -	if (ret < 0) { -		dev_err(&client->dev, "Read from device fails\n"); -		return ret; -	} - -	return 0; -} - -/* - * Perform some start-of-day setup, including reading the asa calibration - * values and caching them. - */ -static int ak8975_setup(struct i2c_client *client) -{ -	struct ak8975_data *data = i2c_get_clientdata(client); -	u8 device_id; -	int ret; - -	/* Confirm that the device we're talking to is really an AK8975. */ -	ret = ak8975_read_data(client, AK8975_REG_WIA, 1, &device_id); -	if (ret < 0) { -		dev_err(&client->dev, "Error reading WIA\n"); -		return ret; -	} -	if (device_id != AK8975_DEVICE_ID) { -		dev_err(&client->dev, "Device ak8975 not found\n"); -		return -ENODEV; -	} - -	/* Write the fused rom access mode. */ -	ret = ak8975_write_data(client, -				AK8975_REG_CNTL, -				AK8975_REG_CNTL_MODE_FUSE_ROM, -				AK8975_REG_CNTL_MODE_MASK, -				AK8975_REG_CNTL_MODE_SHIFT); -	if (ret < 0) { -		dev_err(&client->dev, "Error in setting fuse access mode\n"); -		return ret; -	} - -	/* Get asa data and store in the device data. */ -	ret = ak8975_read_data(client, AK8975_REG_ASAX, 3, data->asa); -	if (ret < 0) { -		dev_err(&client->dev, "Not able to read asa data\n"); -		return ret; -	} - -	/* Precalculate scale factor for each axis and -           store in the device data. */ -	data->raw_to_gauss[0] = ((data->asa[0] + 128) * 30) >> 8; -	data->raw_to_gauss[1] = ((data->asa[1] + 128) * 30) >> 8; -	data->raw_to_gauss[2] = ((data->asa[2] + 128) * 30) >> 8; - -	return 0; -} - -/* - * Shows the device's mode.  0 = off, 1 = on. - */ -static ssize_t show_mode(struct device *dev, struct device_attribute *devattr, -			 char *buf) -{ -	struct iio_dev *indio_dev = dev_get_drvdata(dev); -	struct ak8975_data *data = indio_dev->dev_data; - -	return sprintf(buf, "%lu\n", data->mode); -} - -/* - * Sets the device's mode.  0 = off, 1 = on.  The device's mode must be on - * for the magn raw attributes to be available. - */ -static ssize_t store_mode(struct device *dev, struct device_attribute *devattr, -			  const char *buf, size_t count) -{ -	struct iio_dev *indio_dev = dev_get_drvdata(dev); -	struct ak8975_data *data = indio_dev->dev_data; -	struct i2c_client *client = data->client; -	unsigned long oval; -	int ret; - -	/* Convert mode string and do some basic sanity checking on it. -	   only 0 or 1 are valid. */ -	if (strict_strtoul(buf, 10, &oval)) -		return -EINVAL; - -	if (oval > 1) { -		dev_err(dev, "mode value is not supported\n"); -		return -EINVAL; -	} - -	mutex_lock(&data->lock); - -	/* Write the mode to the device. */ -	if (data->mode != oval) { -		ret = ak8975_write_data(client, -					AK8975_REG_CNTL, -					(u8)oval, -					AK8975_REG_CNTL_MODE_MASK, -					AK8975_REG_CNTL_MODE_SHIFT); - -		if (ret < 0) { -			dev_err(&client->dev, "Error in setting mode\n"); -			mutex_unlock(&data->lock); -			return ret; -		} -		data->mode = oval; -	} - -	mutex_unlock(&data->lock); - -	return count; -} - -/* - * Emits the scale factor to bring the raw value into Gauss units. - * - * This scale factor is axis-dependent, and is derived from 3 calibration - * factors ASA(x), ASA(y), and ASA(z). - * - * These ASA values are read from the sensor device at start of day, and - * cached in the device context struct. - * - * Adjusting the flux value with the sensitivity adjustment value should be - * done via the following formula: - * - * Hadj = H * ( ( ( (ASA-128)*0.5 ) / 128 ) + 1 ) - * - * where H is the raw value, ASA is the sensitivity adjustment, and Hadj - * is the resultant adjusted value. - * - * We reduce the formula to: - * - * Hadj = H * (ASA + 128) / 256 - * - * H is in the range of -4096 to 4095.  The magnetometer has a range of - * +-1229uT.  To go from the raw value to uT is: - * - * HuT = H * 1229/4096, or roughly, 3/10. - * - * Since 1uT = 100 gauss, our final scale factor becomes: - * - * Hadj = H * ((ASA + 128) / 256) * 3/10 * 100 - * Hadj = H * ((ASA + 128) * 30 / 256 - * - * Since ASA doesn't change, we cache the resultant scale factor into the - * device context in ak8975_setup(). - */ -static ssize_t show_scale(struct device *dev, struct device_attribute *devattr, -			  char *buf) -{ -	struct iio_dev *indio_dev = dev_get_drvdata(dev); -	struct ak8975_data *data = indio_dev->dev_data; -	struct iio_dev_attr *this_attr = to_iio_dev_attr(devattr); - -	return sprintf(buf, "%ld\n", data->raw_to_gauss[this_attr->address]); -} - -/* - * Emits the raw flux value for the x, y, or z axis. - */ -static ssize_t show_raw(struct device *dev, struct device_attribute *devattr, -			char *buf) -{ -	struct iio_dev *indio_dev = dev_get_drvdata(dev); -	struct ak8975_data *data = indio_dev->dev_data; -	struct i2c_client *client = data->client; -	struct iio_dev_attr *this_attr = to_iio_dev_attr(devattr); -	u32 timeout_ms = AK8975_MAX_CONVERSION_TIMEOUT; -	u16 meas_reg; -	s16 raw; -	u8 read_status; -	int ret; - -	mutex_lock(&data->lock); - -	if (data->mode == 0) { -		dev_err(&client->dev, "Operating mode is in power down mode\n"); -		ret = -EBUSY; -		goto exit; -	} - -	/* Set up the device for taking a sample. */ -	ret = ak8975_write_data(client, -				AK8975_REG_CNTL, -				AK8975_REG_CNTL_MODE_ONCE, -				AK8975_REG_CNTL_MODE_MASK, -				AK8975_REG_CNTL_MODE_SHIFT); -	if (ret < 0) { -		dev_err(&client->dev, "Error in setting operating mode\n"); -		goto exit; -	} - -	/* Wait for the conversion to complete. */ -	while (timeout_ms) { -		msleep(AK8975_CONVERSION_DONE_POLL_TIME); -		if (gpio_get_value(data->eoc_gpio)) -			break; -		timeout_ms -= AK8975_CONVERSION_DONE_POLL_TIME; -	} -	if (!timeout_ms) { -		dev_err(&client->dev, "Conversion timeout happened\n"); -		ret = -EINVAL; -		goto exit; -	} - -	ret = ak8975_read_data(client, AK8975_REG_ST1, 1, &read_status); -	if (ret < 0) { -		dev_err(&client->dev, "Error in reading ST1\n"); -		goto exit; -	} - -	if (read_status & AK8975_REG_ST1_DRDY_MASK) { -		ret = ak8975_read_data(client, AK8975_REG_ST2, 1, &read_status); -		if (ret < 0) { -			dev_err(&client->dev, "Error in reading ST2\n"); -			goto exit; -		} -		if (read_status & (AK8975_REG_ST2_DERR_MASK | -				   AK8975_REG_ST2_HOFL_MASK)) { -			dev_err(&client->dev, "ST2 status error 0x%x\n", -				read_status); -			ret = -EINVAL; -			goto exit; -		} -	} - -	/* Read the flux value from the appropriate register -	   (the register is specified in the iio device attributes). */ -	ret = ak8975_read_data(client, this_attr->address, 2, (u8 *)&meas_reg); -	if (ret < 0) { -		dev_err(&client->dev, "Read axis data fails\n"); -		goto exit; -	} - -	mutex_unlock(&data->lock); - -	/* Endian conversion of the measured values. */ -	raw = (s16) (le16_to_cpu(meas_reg)); - -	/* Clamp to valid range. */ -	raw = clamp_t(s16, raw, -4096, 4095); - -	return sprintf(buf, "%d\n", raw); - -exit: -	mutex_unlock(&data->lock); -	return ret; -} - -static IIO_DEVICE_ATTR(mode, S_IRUGO | S_IWUSR, show_mode, store_mode, 0); -static IIO_DEV_ATTR_MAGN_X_SCALE(S_IRUGO, show_scale, NULL, 0); -static IIO_DEV_ATTR_MAGN_Y_SCALE(S_IRUGO, show_scale, NULL, 1); -static IIO_DEV_ATTR_MAGN_Z_SCALE(S_IRUGO, show_scale, NULL, 2); -static IIO_DEV_ATTR_MAGN_X(show_raw, AK8975_REG_HXL); -static IIO_DEV_ATTR_MAGN_Y(show_raw, AK8975_REG_HYL); -static IIO_DEV_ATTR_MAGN_Z(show_raw, AK8975_REG_HZL); - -static struct attribute *ak8975_attr[] = { -	&iio_dev_attr_mode.dev_attr.attr, -	&iio_dev_attr_magn_x_scale.dev_attr.attr, -	&iio_dev_attr_magn_y_scale.dev_attr.attr, -	&iio_dev_attr_magn_z_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, -	NULL -}; - -static struct attribute_group ak8975_attr_group = { -	.attrs = ak8975_attr, -}; - -static int ak8975_probe(struct i2c_client *client, -			const struct i2c_device_id *id) -{ -	struct ak8975_data *data; -	int err; - -	/* Allocate our device context. */ -	data = kzalloc(sizeof(struct ak8975_data), GFP_KERNEL); -	if (!data) { -		dev_err(&client->dev, "Memory allocation fails\n"); -		err = -ENOMEM; -		goto exit; -	} - -	i2c_set_clientdata(client, data); -	data->client = client; - -	mutex_init(&data->lock); - -	/* Grab and set up the supplied GPIO. */ -	data->eoc_irq = client->irq; -	data->eoc_gpio = irq_to_gpio(client->irq); - -	if (!data->eoc_gpio) { -		dev_err(&client->dev, "failed, no valid GPIO\n"); -		err = -EINVAL; -		goto exit_free; -	} - -	err = gpio_request(data->eoc_gpio, "ak_8975"); -	if (err < 0) { -		dev_err(&client->dev, "failed to request GPIO %d, error %d\n", -			data->eoc_gpio, err); -		goto exit_free; -	} - -	err = gpio_direction_input(data->eoc_gpio); -	if (err < 0) { -		dev_err(&client->dev, "Failed to configure input direction for" -			" GPIO %d, error %d\n", data->eoc_gpio, err); -		goto exit_gpio; -	} - -	/* Perform some basic start-of-day setup of the device. */ -	err = ak8975_setup(client); -	if (err < 0) { -		dev_err(&client->dev, "AK8975 initialization fails\n"); -		goto exit_gpio; -	} - -	/* Register with IIO */ -	data->indio_dev = iio_allocate_device(); -	if (data->indio_dev == NULL) { -		err = -ENOMEM; -		goto exit_gpio; -	} - -	data->indio_dev->dev.parent = &client->dev; -	data->indio_dev->attrs = &ak8975_attr_group; -	data->indio_dev->dev_data = (void *)(data); -	data->indio_dev->driver_module = THIS_MODULE; -	data->indio_dev->modes = INDIO_DIRECT_MODE; - -	err = iio_device_register(data->indio_dev); -	if (err < 0) -		goto exit_free_iio; - -	return 0; - -exit_free_iio: -	iio_free_device(data->indio_dev); -exit_gpio: -	gpio_free(data->eoc_gpio); -exit_free: -	kfree(data); -exit: -	return err; -} - -static int ak8975_remove(struct i2c_client *client) -{ -	struct ak8975_data *data = i2c_get_clientdata(client); - -	iio_device_unregister(data->indio_dev); -	iio_free_device(data->indio_dev); - -	gpio_free(data->eoc_gpio); - -	kfree(data); - -	return 0; -} - -static const struct i2c_device_id ak8975_id[] = { -	{"ak8975", 0}, -	{} -}; - -MODULE_DEVICE_TABLE(i2c, ak8975_id); - -static struct i2c_driver ak8975_driver = { -	.driver = { -		.name	= "ak8975", -	}, -	.probe		= ak8975_probe, -	.remove		= __devexit_p(ak8975_remove), -	.id_table	= ak8975_id, -}; - -static int __init ak8975_init(void) -{ -	return i2c_add_driver(&ak8975_driver); -} - -static void __exit ak8975_exit(void) -{ -	i2c_del_driver(&ak8975_driver); -} - -module_init(ak8975_init); -module_exit(ak8975_exit); - -MODULE_AUTHOR("Laxman Dewangan <ldewangan@nvidia.com>"); -MODULE_DESCRIPTION("AK8975 magnetometer driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/staging/iio/magnetometer/hmc5843.c b/drivers/staging/iio/magnetometer/hmc5843.c index 51689177e00..d4f4dd90c69 100644 --- a/drivers/staging/iio/magnetometer/hmc5843.c +++ b/drivers/staging/iio/magnetometer/hmc5843.c @@ -1,6 +1,8 @@  /*  Copyright (C) 2010 Texas Instruments      Author: Shubhrajyoti Datta <shubhrajyoti@ti.com> -    Acknowledgement: Jonathan Cameron <jic23@cam.ac.uk> for valuable inputs. +    Acknowledgement: Jonathan Cameron <jic23@kernel.org> for valuable inputs. + +    Support for HMC5883 and HMC5883L by Peter Meerwald <pmeerw@pmeerw.net>.      This program is free software; you can redistribute it and/or modify      it under the terms of the GNU General Public License as published by @@ -18,267 +20,214 @@  */  #include <linux/module.h> -#include <linux/init.h>  #include <linux/i2c.h> -#include <linux/slab.h> -#include <linux/types.h> -#include "../iio.h" -#include "../sysfs.h" -#include "magnet.h" - -#define HMC5843_I2C_ADDRESS			0x1E +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> +#include <linux/iio/trigger_consumer.h> +#include <linux/iio/buffer.h> +#include <linux/iio/triggered_buffer.h> +#include <linux/delay.h>  #define HMC5843_CONFIG_REG_A			0x00  #define HMC5843_CONFIG_REG_B			0x01  #define HMC5843_MODE_REG			0x02 -#define HMC5843_DATA_OUT_X_MSB_REG		0x03 -#define HMC5843_DATA_OUT_X_LSB_REG		0x04 -#define HMC5843_DATA_OUT_Y_MSB_REG		0x05 -#define HMC5843_DATA_OUT_Y_LSB_REG		0x06 -#define HMC5843_DATA_OUT_Z_MSB_REG		0x07 -#define HMC5843_DATA_OUT_Z_LSB_REG		0x08 +#define HMC5843_DATA_OUT_MSB_REGS		0x03  #define HMC5843_STATUS_REG			0x09 -#define HMC5843_ID_REG_A			0x0A -#define HMC5843_ID_REG_B			0x0B -#define HMC5843_ID_REG_C			0x0C - -#define HMC5843_ID_REG_LENGTH			0x03 -#define HMC5843_ID_STRING			"H43" +#define HMC5843_ID_REG				0x0a -/* - * Range settings in  (+-)Ga - * */ -#define RANGE_GAIN_OFFSET			0x05 - -#define	RANGE_0_7				0x00 -#define	RANGE_1_0				0x01 /* default */ -#define	RANGE_1_5				0x02 -#define	RANGE_2_0				0x03 -#define	RANGE_3_2				0x04 -#define	RANGE_3_8				0x05 -#define	RANGE_4_5				0x06 -#define	RANGE_6_5				0x07 /* Not recommended */ +enum hmc5843_ids { +	HMC5843_ID, +	HMC5883_ID, +	HMC5883L_ID, +};  /* - * Device status + * Range gain settings in (+-)Ga + * Beware: HMC5843 and HMC5883 have different recommended sensor field + * ranges; default corresponds to +-1.0 Ga and +-1.3 Ga, respectively   */ -#define	DATA_READY  				0x01 -#define	DATA_OUTPUT_LOCK  			0x02 -#define	VOLTAGE_REGULATOR_ENABLED  		0x04 +#define HMC5843_RANGE_GAIN_OFFSET		0x05 +#define HMC5843_RANGE_GAIN_DEFAULT		0x01 +#define HMC5843_RANGE_GAINS			8 + +/* Device status */ +#define HMC5843_DATA_READY			0x01 +#define HMC5843_DATA_OUTPUT_LOCK		0x02 + +/* Mode register configuration */ +#define HMC5843_MODE_CONVERSION_CONTINUOUS	0x00 +#define HMC5843_MODE_CONVERSION_SINGLE		0x01 +#define HMC5843_MODE_IDLE			0x02 +#define HMC5843_MODE_SLEEP			0x03 +#define HMC5843_MODE_MASK			0x03  /* - * Mode register configuration + * HMC5843: Minimum data output rate + * HMC5883: Typical data output rate   */ -#define	MODE_CONVERSION_CONTINUOUS		0x00 -#define	MODE_CONVERSION_SINGLE			0x01 -#define	MODE_IDLE				0x02 -#define	MODE_SLEEP				0x03 - -/* Minimum Data Output Rate in 1/10 Hz  */ -#define RATE_OFFSET				0x02 -#define RATE_BITMASK				0x1C -#define	RATE_5					0x00 -#define	RATE_10					0x01 -#define	RATE_20					0x02 -#define	RATE_50					0x03 -#define	RATE_100				0x04 -#define	RATE_200				0x05 -#define	RATE_500				0x06 -#define	RATE_NOT_USED				0x07 +#define HMC5843_RATE_OFFSET			0x02 +#define HMC5843_RATE_DEFAULT			0x04 +#define HMC5843_RATES				7 + +/* Device measurement configuration */ +#define HMC5843_MEAS_CONF_NORMAL		0x00 +#define HMC5843_MEAS_CONF_POSITIVE_BIAS		0x01 +#define HMC5843_MEAS_CONF_NEGATIVE_BIAS		0x02 +#define HMC5843_MEAS_CONF_MASK			0x03 + +/* Scaling factors: 10000000/Gain */ +static const int hmc5843_regval_to_nanoscale[HMC5843_RANGE_GAINS] = { +	6173, 7692, 10309, 12821, 18868, 21739, 25641, 35714 +}; + +static const int hmc5883_regval_to_nanoscale[HMC5843_RANGE_GAINS] = { +	7812, 9766, 13021, 16287, 24096, 27701, 32573, 45662 +}; + +static const int hmc5883l_regval_to_nanoscale[HMC5843_RANGE_GAINS] = { +	7299, 9174, 12195, 15152, 22727, 25641, 30303, 43478 +};  /* - * Device Configutration + * From the datasheet: + * Value	| HMC5843		| HMC5883/HMC5883L + *		| Data output rate (Hz)	| Data output rate (Hz) + * 0		| 0.5			| 0.75 + * 1		| 1			| 1.5 + * 2		| 2			| 3 + * 3		| 5			| 7.5 + * 4		| 10 (default)		| 15 + * 5		| 20			| 30 + * 6		| 50			| 75 + * 7		| Not used		| Not used   */ -#define	CONF_NORMAL  				0x00 -#define	CONF_POSITIVE_BIAS			0x01 -#define	CONF_NEGATIVE_BIAS			0x02 -#define	CONF_NOT_USED				0x03 -#define	MEAS_CONF_MASK				0x03 - -static const char *regval_to_scale[] = { -	"0.0000006173", -	"0.0000007692", -	"0.0000010309", -	"0.0000012821", -	"0.0000018868", -	"0.0000021739", -	"0.0000025641", -	"0.0000035714", -}; -static const int regval_to_input_field_mg[] = { -	700, -	1000, -	1500, -	2000, -	3200, -	3800, -	4500, -	6500 +static const int hmc5843_regval_to_samp_freq[7][2] = { +	{0, 500000}, {1, 0}, {2, 0}, {5, 0}, {10, 0}, {20, 0}, {50, 0}  }; -static const char *regval_to_samp_freq[] = { -	"0.5", -	"1", -	"2", -	"5", -	"10", -	"20", -	"50", + +static const int hmc5883_regval_to_samp_freq[7][2] = { +	{0, 750000}, {1, 500000}, {3, 0}, {7, 500000}, {15, 0}, {30, 0}, +	{75, 0}  }; -/* Addresses to scan: 0x1E */ -static const unsigned short normal_i2c[] = { HMC5843_I2C_ADDRESS, -							I2C_CLIENT_END }; +/* Describe chip variants */ +struct hmc5843_chip_info { +	const struct iio_chan_spec *channels; +	const int (*regval_to_samp_freq)[2]; +	const int *regval_to_nanoscale; +};  /* Each client has this additional data */  struct hmc5843_data { -	struct iio_dev	*indio_dev; +	struct i2c_client *client;  	struct mutex lock; -	u8		rate; -	u8		meas_conf; -	u8		operating_mode; -	u8		range; +	u8 rate; +	u8 meas_conf; +	u8 operating_mode; +	u8 range; +	const struct hmc5843_chip_info *variant; +	__be16 buffer[8]; /* 3x 16-bit channels + padding + 64-bit timestamp */  }; -static void hmc5843_init_client(struct i2c_client *client); - -static s32 hmc5843_configure(struct i2c_client *client, -				       u8 operating_mode) +/* The lower two bits contain the current conversion mode */ +static s32 hmc5843_set_mode(struct hmc5843_data *data, u8 operating_mode)  { -	/* The lower two bits contain the current conversion mode */ -	return i2c_smbus_write_byte_data(client, -					HMC5843_MODE_REG, -					(operating_mode & 0x03)); -} - -/* Return the measurement value from the  specified channel */ -static ssize_t hmc5843_read_measurement(struct device *dev, -		struct device_attribute *attr, -		char *buf) -{ -	struct iio_dev *indio_dev = dev_get_drvdata(dev); -	struct i2c_client *client = to_i2c_client(indio_dev->dev.parent); -	s16 coordinate_val; -	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); -	struct hmc5843_data *data = indio_dev->dev_data; -	s32 result; +	int ret;  	mutex_lock(&data->lock); - -	result = i2c_smbus_read_byte_data(client, HMC5843_STATUS_REG); -	while (!(result & DATA_READY)) -		result = i2c_smbus_read_byte_data(client, HMC5843_STATUS_REG); - -	result = i2c_smbus_read_word_data(client, this_attr->address); +	ret = i2c_smbus_write_byte_data(data->client, HMC5843_MODE_REG, +					operating_mode & HMC5843_MODE_MASK); +	if (ret >= 0) +		data->operating_mode = operating_mode;  	mutex_unlock(&data->lock); -	if (result < 0) -		return -EINVAL; -	coordinate_val	= (s16)swab16((u16)result); -	return sprintf(buf, "%d\n", coordinate_val); +	return ret;  } -static IIO_DEV_ATTR_MAGN_X(hmc5843_read_measurement, -		HMC5843_DATA_OUT_X_MSB_REG); -static IIO_DEV_ATTR_MAGN_Y(hmc5843_read_measurement, -		HMC5843_DATA_OUT_Y_MSB_REG); -static IIO_DEV_ATTR_MAGN_Z(hmc5843_read_measurement, -		HMC5843_DATA_OUT_Z_MSB_REG); -/* - * From the datasheet - * 0 - Continuous-Conversion Mode: In continuous-conversion mode, the - * device continuously performs conversions an places the result in the - * data register. - * - * 1 - Single-Conversion Mode : device performs a single measurement, - *  sets RDY high and returned to sleep mode - * - * 2 - Idle Mode :  Device is placed in idle mode. - * - * 3 - Sleep Mode. Device is placed in sleep mode. - * - */ -static ssize_t hmc5843_show_operating_mode(struct device *dev, -					struct device_attribute *attr, -					char *buf) +static int hmc5843_wait_measurement(struct hmc5843_data *data)  { -	struct iio_dev *indio_dev = dev_get_drvdata(dev); -	struct hmc5843_data *data = indio_dev->dev_data; -	return sprintf(buf, "%d\n", data->operating_mode); +	s32 result; +	int tries = 150; + +	while (tries-- > 0) { +		result = i2c_smbus_read_byte_data(data->client, +			HMC5843_STATUS_REG); +		if (result < 0) +			return result; +		if (result & HMC5843_DATA_READY) +			break; +		msleep(20); +	} + +	if (tries < 0) { +		dev_err(&data->client->dev, "data not ready\n"); +		return -EIO; +	} + +	return 0;  } -static ssize_t hmc5843_set_operating_mode(struct device *dev, -				struct device_attribute *attr, -				const char *buf, -				size_t count) +/* Return the measurement value from the specified channel */ +static int hmc5843_read_measurement(struct hmc5843_data *data, +				    int idx, int *val)  { -	struct iio_dev *indio_dev = dev_get_drvdata(dev); -	struct i2c_client *client = to_i2c_client(indio_dev->dev.parent); -	struct hmc5843_data *data = indio_dev->dev_data; -	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); -	unsigned long operating_mode = 0; -	s32 status; -	int error; -	mutex_lock(&data->lock); -	error = strict_strtoul(buf, 10, &operating_mode); -	if (error) { -		count = error; -		goto exit; -	} -	dev_dbg(dev, "set Conversion mode to %lu\n", operating_mode); -	if (operating_mode > MODE_SLEEP) { -		count = -EINVAL; -		goto exit; -	} +	s32 result; +	__be16 values[3]; -	status = i2c_smbus_write_byte_data(client, this_attr->address, -					operating_mode); -	if (status) { -		count = -EINVAL; -		goto exit; +	mutex_lock(&data->lock); +	result = hmc5843_wait_measurement(data); +	if (result < 0) { +		mutex_unlock(&data->lock); +		return result;  	} -	data->operating_mode = operating_mode; - -exit: +	result = i2c_smbus_read_i2c_block_data(data->client, +		HMC5843_DATA_OUT_MSB_REGS, sizeof(values), (u8 *) values);  	mutex_unlock(&data->lock); -	return count; +	if (result < 0) +		return -EINVAL; + +	*val = sign_extend32(be16_to_cpu(values[idx]), 15); +	return IIO_VAL_INT;  } -static IIO_DEVICE_ATTR(operating_mode, -			S_IWUSR | S_IRUGO, -			hmc5843_show_operating_mode, -			hmc5843_set_operating_mode, -			HMC5843_MODE_REG);  /*   * API for setting the measurement configuration to   * Normal, Positive bias and Negative bias - * From the datasheet   * - * Normal measurement configuration (default): In normal measurement - * configuration the device follows normal measurement flow. Pins BP and BN - * are left floating and high impedance. + * From the datasheet: + * 0 - Normal measurement configuration (default): In normal measurement + *     configuration the device follows normal measurement flow. Pins BP + *     and BN are left floating and high impedance.   * - * Positive bias configuration: In positive bias configuration, a positive - * current is forced across the resistive load on pins BP and BN. + * 1 - Positive bias configuration: In positive bias configuration, a + *     positive current is forced across the resistive load on pins BP + *     and BN.   * - * Negative bias configuration. In negative bias configuration, a negative - * current is forced across the resistive load on pins BP and BN. + * 2 - Negative bias configuration. In negative bias configuration, a + *     negative current is forced across the resistive load on pins BP + *     and BN.   *   */ -static s32 hmc5843_set_meas_conf(struct i2c_client *client, -				      u8 meas_conf) +static s32 hmc5843_set_meas_conf(struct hmc5843_data *data, u8 meas_conf)  { -	struct hmc5843_data *data = i2c_get_clientdata(client); -	u8 reg_val; -	reg_val = (meas_conf & MEAS_CONF_MASK) |  (data->rate << RATE_OFFSET); -	return i2c_smbus_write_byte_data(client, HMC5843_CONFIG_REG_A, reg_val); +	int ret; + +	mutex_lock(&data->lock); +	ret = i2c_smbus_write_byte_data(data->client, HMC5843_CONFIG_REG_A, +		(meas_conf & HMC5843_MEAS_CONF_MASK) | +		(data->rate << HMC5843_RATE_OFFSET)); +	if (ret >= 0) +		data->meas_conf = meas_conf; +	mutex_unlock(&data->lock); + +	return ret;  }  static ssize_t hmc5843_show_measurement_configuration(struct device *dev,  						struct device_attribute *attr,  						char *buf)  { -	struct iio_dev *indio_dev = dev_get_drvdata(dev); -	struct hmc5843_data *data = indio_dev->dev_data; +	struct hmc5843_data *data = iio_priv(dev_to_iio_dev(dev));  	return sprintf(buf, "%d\n", data->meas_conf);  } @@ -287,210 +236,248 @@ static ssize_t hmc5843_set_measurement_configuration(struct device *dev,  						const char *buf,  						size_t count)  { -	struct iio_dev *indio_dev = dev_get_drvdata(dev); -	struct i2c_client *client = to_i2c_client(indio_dev->dev.parent); -	struct hmc5843_data *data = i2c_get_clientdata(client); +	struct hmc5843_data *data = iio_priv(dev_to_iio_dev(dev));  	unsigned long meas_conf = 0; -	int error = strict_strtoul(buf, 10, &meas_conf); -	if (error) -		return error; -	mutex_lock(&data->lock); +	int ret; -	dev_dbg(dev, "set mode to %lu\n", meas_conf); -	if (hmc5843_set_meas_conf(client, meas_conf)) { -		count = -EINVAL; -		goto exit; -	} -	data->meas_conf = meas_conf; +	ret = kstrtoul(buf, 10, &meas_conf); +	if (ret) +		return ret; +	if (meas_conf >= HMC5843_MEAS_CONF_MASK) +		return -EINVAL; -exit: -	mutex_unlock(&data->lock); -	return count; +	ret = hmc5843_set_meas_conf(data, meas_conf); + +	return (ret < 0) ? ret : count;  } +  static IIO_DEVICE_ATTR(meas_conf,  			S_IWUSR | S_IRUGO,  			hmc5843_show_measurement_configuration,  			hmc5843_set_measurement_configuration,  			0); -/* - * From Datasheet - * The table shows the minimum data output - * Value	| Minimum data output rate(Hz) - * 0		| 0.5 - * 1		| 1 - * 2		| 2 - * 3		| 5 - * 4		| 10 (default) - * 5		| 20 - * 6		| 50 - * 7		| Not used - */ -static IIO_CONST_ATTR_SAMP_FREQ_AVAIL("0.5 1 2 5 10 20 50"); +static ssize_t hmc5843_show_samp_freq_avail(struct device *dev, +				struct device_attribute *attr, char *buf) +{ +	struct hmc5843_data *data = iio_priv(dev_to_iio_dev(dev)); +	size_t len = 0; +	int i; + +	for (i = 0; i < HMC5843_RATES; i++) +		len += scnprintf(buf + len, PAGE_SIZE - len, +			"%d.%d ", data->variant->regval_to_samp_freq[i][0], +			data->variant->regval_to_samp_freq[i][1]); + +	/* replace trailing space by newline */ +	buf[len - 1] = '\n'; + +	return len; +} + +static IIO_DEV_ATTR_SAMP_FREQ_AVAIL(hmc5843_show_samp_freq_avail); -static s32 hmc5843_set_rate(struct i2c_client *client, -				u8 rate) +static int hmc5843_set_samp_freq(struct hmc5843_data *data, u8 rate)  { -	struct hmc5843_data *data = i2c_get_clientdata(client); -	u8 reg_val; +	int ret; -	reg_val = (data->meas_conf) |  (rate << RATE_OFFSET); -	if (rate >= RATE_NOT_USED) { -		dev_err(&client->dev, -			"This data output rate is not supported \n"); -		return -EINVAL; -	} -	return i2c_smbus_write_byte_data(client, HMC5843_CONFIG_REG_A, reg_val); +	mutex_lock(&data->lock); +	ret = i2c_smbus_write_byte_data(data->client, HMC5843_CONFIG_REG_A, +		data->meas_conf | (rate << HMC5843_RATE_OFFSET)); +	if (ret >= 0) +		data->rate = rate; +	mutex_unlock(&data->lock); + +	return ret;  } -static ssize_t set_sampling_frequency(struct device *dev, -					struct device_attribute *attr, -					const char *buf, size_t count) +static int hmc5843_get_samp_freq_index(struct hmc5843_data *data, +				   int val, int val2)  { +	int i; -	struct iio_dev *indio_dev = dev_get_drvdata(dev); -	struct i2c_client *client = to_i2c_client(indio_dev->dev.parent); -	struct hmc5843_data *data = indio_dev->dev_data; -	unsigned long rate = 0; - -	if (strncmp(buf, "0.5" , 3) == 0) -		rate = RATE_5; -	else if (strncmp(buf, "1" , 1) == 0) -		rate = RATE_10; -	else if (strncmp(buf, "2", 1) == 0) -		rate = RATE_20; -	else if (strncmp(buf, "5", 1) == 0) -		rate = RATE_50; -	else if (strncmp(buf, "10", 2) == 0) -		rate = RATE_100; -	else if (strncmp(buf, "20" , 2) == 0) -		rate = RATE_200; -	else if (strncmp(buf, "50" , 2) == 0) -		rate = RATE_500; -	else -		return -EINVAL; +	for (i = 0; i < HMC5843_RATES; i++) +		if (val == data->variant->regval_to_samp_freq[i][0] && +			val2 == data->variant->regval_to_samp_freq[i][1]) +			return i; -	mutex_lock(&data->lock); -	dev_dbg(dev, "set rate to %lu\n", rate); -	if (hmc5843_set_rate(client, rate)) { -		count = -EINVAL; -		goto exit; -	} -	data->rate = rate; +	return -EINVAL; +} + +static int hmc5843_set_range_gain(struct hmc5843_data *data, u8 range) +{ +	int ret; -exit: +	mutex_lock(&data->lock); +	ret = i2c_smbus_write_byte_data(data->client, HMC5843_CONFIG_REG_B, +		range << HMC5843_RANGE_GAIN_OFFSET); +	if (ret >= 0) +		data->range = range;  	mutex_unlock(&data->lock); -	return count; + +	return ret;  } -static ssize_t show_sampling_frequency(struct device *dev, -			struct device_attribute *attr, char *buf) +static ssize_t hmc5843_show_scale_avail(struct device *dev, +				struct device_attribute *attr, char *buf)  { -	struct iio_dev *indio_dev = dev_get_drvdata(dev); -	struct i2c_client *client = to_i2c_client(indio_dev->dev.parent); -	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); -	s32 rate; - -	rate = i2c_smbus_read_byte_data(client,  this_attr->address); -	if (rate < 0) -		return rate; -	rate = (rate & RATE_BITMASK) >> RATE_OFFSET; -	return sprintf(buf, "%s\n", regval_to_samp_freq[rate]); +	struct hmc5843_data *data = iio_priv(dev_to_iio_dev(dev)); + +	size_t len = 0; +	int i; + +	for (i = 0; i < HMC5843_RANGE_GAINS; i++) +		len += scnprintf(buf + len, PAGE_SIZE - len, +			"0.%09d ", data->variant->regval_to_nanoscale[i]); + +	/* replace trailing space by newline */ +	buf[len - 1] = '\n'; + +	return len;  } -static IIO_DEVICE_ATTR(sampling_frequency, -			S_IWUSR | S_IRUGO, -			show_sampling_frequency, -			set_sampling_frequency, -			HMC5843_CONFIG_REG_A); -/* - * From Datasheet - *	Nominal gain settings - * Value	| Sensor Input Field Range(Ga)	| Gain(counts/ milli-gauss) - *0		|(+-)0.7			|1620 - *1		|(+-)1.0			|1300 - *2		|(+-)1.5			|970 - *3		|(+-)2.0			|780 - *4		|(+-)3.2			|530 - *5		|(+-)3.8			|460 - *6		|(+-)4.5			|390 - *7		|(+-)6.5			|280 - */ -static ssize_t show_range(struct device *dev, -				struct device_attribute *attr, -				char *buf) +static IIO_DEVICE_ATTR(scale_available, S_IRUGO, +	hmc5843_show_scale_avail, NULL, 0); + +static int hmc5843_get_scale_index(struct hmc5843_data *data, int val, int val2)  { -	u8 range; -	struct iio_dev *indio_dev = dev_get_drvdata(dev); -	struct hmc5843_data *data = indio_dev->dev_data; +	int i; -	range = data->range; -	return sprintf(buf, "%d\n", regval_to_input_field_mg[range]); +	if (val != 0) +		return -EINVAL; + +	for (i = 0; i < HMC5843_RANGE_GAINS; i++) +		if (val2 == data->variant->regval_to_nanoscale[i]) +			return i; + +	return -EINVAL;  } -static ssize_t set_range(struct device *dev, -			struct device_attribute *attr, -			const char *buf, -			size_t count) +static int hmc5843_read_raw(struct iio_dev *indio_dev, +			    struct iio_chan_spec const *chan, +			    int *val, int *val2, long mask)  { -	struct iio_dev *indio_dev = dev_get_drvdata(dev); -	struct i2c_client *client = to_i2c_client(indio_dev->dev.parent); -	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); -	struct hmc5843_data *data = indio_dev->dev_data; -	unsigned long range = 0; -	int error; -	mutex_lock(&data->lock); -	error = strict_strtoul(buf, 10, &range); -	if (error) { -		count = error; -		goto exit; +	struct hmc5843_data *data = iio_priv(indio_dev); + +	switch (mask) { +	case IIO_CHAN_INFO_RAW: +		return hmc5843_read_measurement(data, chan->scan_index, val); +	case IIO_CHAN_INFO_SCALE: +		*val = 0; +		*val2 = data->variant->regval_to_nanoscale[data->range]; +		return IIO_VAL_INT_PLUS_NANO; +	case IIO_CHAN_INFO_SAMP_FREQ: +		*val = data->variant->regval_to_samp_freq[data->rate][0]; +		*val2 = data->variant->regval_to_samp_freq[data->rate][1]; +		return IIO_VAL_INT_PLUS_MICRO; +	} +	return -EINVAL; +} + +static int hmc5843_write_raw(struct iio_dev *indio_dev, +			     struct iio_chan_spec const *chan, +			     int val, int val2, long mask) +{ +	struct hmc5843_data *data = iio_priv(indio_dev); +	int rate, range; + +	switch (mask) { +	case IIO_CHAN_INFO_SAMP_FREQ: +		rate = hmc5843_get_samp_freq_index(data, val, val2); +		if (rate < 0) +			return -EINVAL; + +		return hmc5843_set_samp_freq(data, rate); +	case IIO_CHAN_INFO_SCALE: +		range = hmc5843_get_scale_index(data, val, val2); +		if (range < 0) +			return -EINVAL; + +		return hmc5843_set_range_gain(data, range); +	default: +		return -EINVAL;  	} -	dev_dbg(dev, "set range to %lu\n", range); +} -	if (range > RANGE_6_5) { -		count = -EINVAL; -		goto exit; +static int hmc5843_write_raw_get_fmt(struct iio_dev *indio_dev, +			       struct iio_chan_spec const *chan, long mask) +{ +	switch (mask) { +	case IIO_CHAN_INFO_SAMP_FREQ: +		return IIO_VAL_INT_PLUS_MICRO; +	case IIO_CHAN_INFO_SCALE: +		return IIO_VAL_INT_PLUS_NANO; +	default: +		return -EINVAL;  	} +} + +static irqreturn_t hmc5843_trigger_handler(int irq, void *p) +{ +	struct iio_poll_func *pf = p; +	struct iio_dev *indio_dev = pf->indio_dev; +	struct hmc5843_data *data = iio_priv(indio_dev); +	int ret; -	data->range = range; -	range = range << RANGE_GAIN_OFFSET; -	if (i2c_smbus_write_byte_data(client, this_attr->address, range)) -		count = -EINVAL; +	mutex_lock(&data->lock); +	ret = hmc5843_wait_measurement(data); +	if (ret < 0) { +		mutex_unlock(&data->lock); +		goto done; +	} -exit: +	ret = i2c_smbus_read_i2c_block_data(data->client, +		HMC5843_DATA_OUT_MSB_REGS, 3 * sizeof(__be16), +			(u8 *) data->buffer);  	mutex_unlock(&data->lock); -	return count; +	if (ret < 0) +		goto done; -} -static IIO_DEVICE_ATTR(magn_range, -			S_IWUSR | S_IRUGO, -			show_range, -			set_range, -			HMC5843_CONFIG_REG_B); +	iio_push_to_buffers_with_timestamp(indio_dev, data->buffer, +		iio_get_time_ns()); -static ssize_t show_scale(struct device *dev, -			struct device_attribute *attr, -			char *buf) -{ -	struct iio_dev *indio_dev = dev_get_drvdata(dev); -	struct hmc5843_data *data = indio_dev->dev_data; -	return strlen(strcpy(buf, regval_to_scale[data->range])); +done: +	iio_trigger_notify_done(indio_dev->trig); + +	return IRQ_HANDLED;  } -static IIO_DEVICE_ATTR(magn_scale, -			S_IRUGO, -			show_scale, -			NULL , 0); + +#define HMC5843_CHANNEL(axis, idx)					\ +	{								\ +		.type = IIO_MAGN,					\ +		.modified = 1,						\ +		.channel2 = IIO_MOD_##axis,				\ +		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),		\ +		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) |	\ +			BIT(IIO_CHAN_INFO_SAMP_FREQ),			\ +		.scan_index = idx,					\ +		.scan_type = {						\ +			.sign = 's',					\ +			.realbits = 16,					\ +			.storagebits = 16,				\ +			.endianness = IIO_BE,				\ +		},							\ +	} + +static const struct iio_chan_spec hmc5843_channels[] = { +	HMC5843_CHANNEL(X, 0), +	HMC5843_CHANNEL(Y, 1), +	HMC5843_CHANNEL(Z, 2), +	IIO_CHAN_SOFT_TIMESTAMP(3), +}; + +/* Beware: Y and Z are exchanged on HMC5883 */ +static const struct iio_chan_spec hmc5883_channels[] = { +	HMC5843_CHANNEL(X, 0), +	HMC5843_CHANNEL(Z, 1), +	HMC5843_CHANNEL(Y, 2), +	IIO_CHAN_SOFT_TIMESTAMP(3), +};  static struct attribute *hmc5843_attributes[] = {  	&iio_dev_attr_meas_conf.dev_attr.attr, -	&iio_dev_attr_operating_mode.dev_attr.attr, -	&iio_dev_attr_sampling_frequency.dev_attr.attr, -	&iio_dev_attr_magn_range.dev_attr.attr, -	&iio_dev_attr_magn_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_sampling_frequency_available.dev_attr.attr, +	&iio_dev_attr_scale_available.dev_attr.attr, +	&iio_dev_attr_sampling_frequency_available.dev_attr.attr,  	NULL  }; @@ -498,136 +485,168 @@ static const struct attribute_group hmc5843_group = {  	.attrs = hmc5843_attributes,  }; -static int hmc5843_detect(struct i2c_client *client, -			  struct i2c_board_info *info) -{ -	unsigned char id_str[HMC5843_ID_REG_LENGTH]; - -	if (client->addr != HMC5843_I2C_ADDRESS) -		return -ENODEV; - -	if (i2c_smbus_read_i2c_block_data(client, HMC5843_ID_REG_A, -				HMC5843_ID_REG_LENGTH, id_str) -			!= HMC5843_ID_REG_LENGTH) -		return -ENODEV; +static const struct hmc5843_chip_info hmc5843_chip_info_tbl[] = { +	[HMC5843_ID] = { +		.channels = hmc5843_channels, +		.regval_to_samp_freq = hmc5843_regval_to_samp_freq, +		.regval_to_nanoscale = hmc5843_regval_to_nanoscale, +	}, +	[HMC5883_ID] = { +		.channels = hmc5883_channels, +		.regval_to_samp_freq = hmc5883_regval_to_samp_freq, +		.regval_to_nanoscale = hmc5883_regval_to_nanoscale, +	}, +	[HMC5883L_ID] = { +		.channels = hmc5883_channels, +		.regval_to_samp_freq = hmc5883_regval_to_samp_freq, +		.regval_to_nanoscale = hmc5883l_regval_to_nanoscale, +	}, +}; -	if (0 != strncmp(id_str, HMC5843_ID_STRING, HMC5843_ID_REG_LENGTH)) +static int hmc5843_init(struct hmc5843_data *data) +{ +	int ret; +	u8 id[3]; + +	ret = i2c_smbus_read_i2c_block_data(data->client, HMC5843_ID_REG, +		sizeof(id), id); +	if (ret < 0) +		return ret; +	if (id[0] != 'H' || id[1] != '4' || id[2] != '3') { +		dev_err(&data->client->dev, "no HMC5843/5883/5883L sensor\n");  		return -ENODEV; +	} -	return 0; +	ret = hmc5843_set_meas_conf(data, HMC5843_MEAS_CONF_NORMAL); +	if (ret < 0) +		return ret; +	ret = hmc5843_set_samp_freq(data, HMC5843_RATE_DEFAULT); +	if (ret < 0) +		return ret; +	ret = hmc5843_set_range_gain(data, HMC5843_RANGE_GAIN_DEFAULT); +	if (ret < 0) +		return ret; +	return hmc5843_set_mode(data, HMC5843_MODE_CONVERSION_CONTINUOUS);  } -/* Called when we have found a new HMC5843. */ -static void hmc5843_init_client(struct i2c_client *client) -{ -	struct hmc5843_data *data = i2c_get_clientdata(client); -	hmc5843_set_meas_conf(client, data->meas_conf); -	hmc5843_set_rate(client, data->rate); -	hmc5843_configure(client, data->operating_mode); -	i2c_smbus_write_byte_data(client, HMC5843_CONFIG_REG_B, data->range); -	mutex_init(&data->lock); -	pr_info("HMC5843 initialized\n"); -} +static const struct iio_info hmc5843_info = { +	.attrs = &hmc5843_group, +	.read_raw = &hmc5843_read_raw, +	.write_raw = &hmc5843_write_raw, +	.write_raw_get_fmt = &hmc5843_write_raw_get_fmt, +	.driver_module = THIS_MODULE, +}; + +static const unsigned long hmc5843_scan_masks[] = {0x7, 0};  static int hmc5843_probe(struct i2c_client *client,  			 const struct i2c_device_id *id)  {  	struct hmc5843_data *data; -	int err = 0; +	struct iio_dev *indio_dev; +	int ret; -	data = kzalloc(sizeof(struct hmc5843_data), GFP_KERNEL); -	if (!data) { -		err = -ENOMEM; -		goto exit; -	} +	indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); +	if (indio_dev == NULL) +		return -ENOMEM;  	/* default settings at probe */ +	data = iio_priv(indio_dev); +	data->client = client; +	data->variant = &hmc5843_chip_info_tbl[id->driver_data]; +	mutex_init(&data->lock); -	data->meas_conf = CONF_NORMAL; -	data->range = RANGE_1_0; -	data->operating_mode = MODE_CONVERSION_CONTINUOUS; +	i2c_set_clientdata(client, indio_dev); +	indio_dev->info = &hmc5843_info; +	indio_dev->name = id->name; +	indio_dev->dev.parent = &client->dev; +	indio_dev->modes = INDIO_DIRECT_MODE; +	indio_dev->channels = data->variant->channels; +	indio_dev->num_channels = 4; +	indio_dev->available_scan_masks = hmc5843_scan_masks; -	i2c_set_clientdata(client, data); +	ret = hmc5843_init(data); +	if (ret < 0) +		return ret; -	/* Initialize the HMC5843 chip */ -	hmc5843_init_client(client); +	ret = iio_triggered_buffer_setup(indio_dev, NULL, +		hmc5843_trigger_handler, NULL); +	if (ret < 0) +		return ret; + +	ret = iio_device_register(indio_dev); +	if (ret < 0) +		goto buffer_cleanup; -	data->indio_dev = iio_allocate_device(); -	if (!data->indio_dev) { -		err = -ENOMEM; -		goto exit_free1; -	} -	data->indio_dev->attrs = &hmc5843_group; -	data->indio_dev->dev.parent = &client->dev; -	data->indio_dev->dev_data = (void *)(data); -	data->indio_dev->driver_module = THIS_MODULE; -	data->indio_dev->modes = INDIO_DIRECT_MODE; -	err = iio_device_register(data->indio_dev); -	if (err) -		goto exit_free2;  	return 0; -exit_free2: -	iio_free_device(data->indio_dev); -exit_free1: -	kfree(data); -exit: -	return err; + +buffer_cleanup: +	iio_triggered_buffer_cleanup(indio_dev); +	return ret;  }  static int hmc5843_remove(struct i2c_client *client)  { -	struct hmc5843_data *data = i2c_get_clientdata(client); -	 /*  sleep mode to save power */ -	hmc5843_configure(client, MODE_SLEEP); -	iio_device_unregister(data->indio_dev); -	kfree(i2c_get_clientdata(client)); +	struct iio_dev *indio_dev = i2c_get_clientdata(client); + +	iio_device_unregister(indio_dev); +	iio_triggered_buffer_cleanup(indio_dev); + +	/*  sleep mode to save power */ +	hmc5843_set_mode(iio_priv(indio_dev), HMC5843_MODE_SLEEP); +  	return 0;  } -static int hmc5843_suspend(struct i2c_client *client, pm_message_t mesg) +#ifdef CONFIG_PM_SLEEP +static int hmc5843_suspend(struct device *dev)  { -	hmc5843_configure(client, MODE_SLEEP); -	return 0; +	struct hmc5843_data *data = iio_priv(i2c_get_clientdata( +		to_i2c_client(dev))); + +	return hmc5843_set_mode(data, HMC5843_MODE_SLEEP);  } -static int hmc5843_resume(struct i2c_client *client) +static int hmc5843_resume(struct device *dev)  { -	struct hmc5843_data *data = i2c_get_clientdata(client); -	hmc5843_configure(client, data->operating_mode); -	return 0; +	struct hmc5843_data *data = iio_priv(i2c_get_clientdata( +		to_i2c_client(dev))); + +	return hmc5843_set_mode(data, HMC5843_MODE_CONVERSION_CONTINUOUS);  } +static SIMPLE_DEV_PM_OPS(hmc5843_pm_ops, hmc5843_suspend, hmc5843_resume); +#define HMC5843_PM_OPS (&hmc5843_pm_ops) +#else +#define HMC5843_PM_OPS NULL +#endif +  static const struct i2c_device_id hmc5843_id[] = { -	{ "hmc5843", 0 }, +	{ "hmc5843", HMC5843_ID }, +	{ "hmc5883", HMC5883_ID }, +	{ "hmc5883l", HMC5883L_ID },  	{ }  }; +MODULE_DEVICE_TABLE(i2c, hmc5843_id); + +static const struct of_device_id hmc5843_of_match[] = { +	{ .compatible = "honeywell,hmc5843" }, +	{} +}; +MODULE_DEVICE_TABLE(of, hmc5843_of_match);  static struct i2c_driver hmc5843_driver = {  	.driver = {  		.name	= "hmc5843", +		.pm	= HMC5843_PM_OPS, +		.of_match_table = hmc5843_of_match,  	},  	.id_table	= hmc5843_id,  	.probe		= hmc5843_probe,  	.remove		= hmc5843_remove, -	.detect		= hmc5843_detect, -	.address_list	= normal_i2c, -	.suspend	= hmc5843_suspend, -	.resume		= hmc5843_resume,  }; +module_i2c_driver(hmc5843_driver); -static int __init hmc5843_init(void) -{ -	return i2c_add_driver(&hmc5843_driver); -} - -static void __exit hmc5843_exit(void) -{ -	i2c_del_driver(&hmc5843_driver); -} - -MODULE_AUTHOR("Shubhrajyoti Datta <shubhrajyoti@ti.com"); -MODULE_DESCRIPTION("HMC5843 driver"); +MODULE_AUTHOR("Shubhrajyoti Datta <shubhrajyoti@ti.com>"); +MODULE_DESCRIPTION("HMC5843/5883/5883L driver");  MODULE_LICENSE("GPL"); - -module_init(hmc5843_init); -module_exit(hmc5843_exit); diff --git a/drivers/staging/iio/magnetometer/magnet.h b/drivers/staging/iio/magnetometer/magnet.h deleted file mode 100644 index 1260eb7bd41..00000000000 --- a/drivers/staging/iio/magnetometer/magnet.h +++ /dev/null @@ -1,31 +0,0 @@ - -#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_SCALE(_mode, _show, _store, _addr)		\ -	IIO_DEVICE_ATTR(magn_x_scale, _mode, _show, _store, _addr) - -#define IIO_DEV_ATTR_MAGN_Y_SCALE(_mode, _show, _store, _addr)		\ -	IIO_DEVICE_ATTR(magn_y_scale, _mode, _show, _store, _addr) - -#define IIO_DEV_ATTR_MAGN_Z_SCALE(_mode, _show, _store, _addr)		\ -	IIO_DEVICE_ATTR(magn_z_scale, _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/meter/Kconfig b/drivers/staging/iio/meter/Kconfig new file mode 100644 index 00000000000..e53274b64ae --- /dev/null +++ b/drivers/staging/iio/meter/Kconfig @@ -0,0 +1,63 @@ +# +# IIO meter drivers configuration +# +menu "Active energy metering IC" + +config ADE7753 +	tristate "Analog Devices ADE7753/6 Single-Phase Multifunction Metering IC Driver" +	depends on SPI +	help +	  Say yes here to build support for Analog Devices ADE7753 Single-Phase Multifunction +	  Metering IC with di/dt Sensor Interface. + +config ADE7754 +	tristate "Analog Devices ADE7754 Polyphase Multifunction Energy Metering IC Driver" +	depends on SPI +	help +	  Say yes here to build support for Analog Devices ADE7754 Polyphase +	  Multifunction Energy Metering IC Driver. + +config ADE7758 +	tristate "Analog Devices ADE7758 Poly Phase Multifunction Energy Metering IC Driver" +	depends on SPI +	select IIO_TRIGGER if IIO_BUFFER +	select IIO_KFIFO_BUF if IIO_BUFFER +	help +	  Say yes here to build support for Analog Devices ADE7758 Polyphase +	  Multifunction Energy Metering IC with Per Phase Information Driver. + +config ADE7759 +	tristate "Analog Devices ADE7759 Active Energy Metering IC Driver" +	depends on SPI +	help +	  Say yes here to build support for Analog Devices ADE7758 Active Energy +	  Metering IC with di/dt Sensor Interface. + +config ADE7854 +	tristate "Analog Devices ADE7854/58/68/78 Polyphase Multifunction Energy Metering IC Driver" +	depends on SPI || I2C +	help +	  Say yes here to build support for Analog Devices ADE7854/58/68/78 Polyphase +	  Multifunction Energy Metering IC Driver. + +config ADE7854_I2C +	tristate "support I2C bus connection" +	depends on ADE7854 && I2C +	default y +	help +	  Say Y here if you have ADE7854/58/68/78 hooked to an I2C bus. + +	  To compile this driver as a module, choose M here: the +	  module will be called ade7854-i2c. + +config ADE7854_SPI +	tristate "support SPI bus connection" +	depends on ADE7854 && SPI +	default y +	help +	  Say Y here if you have ADE7854/58/68/78 hooked to a SPI bus. + +	  To compile this driver as a module, choose M here: the +	  module will be called ade7854-spi. + +endmenu diff --git a/drivers/staging/iio/meter/Makefile b/drivers/staging/iio/meter/Makefile new file mode 100644 index 00000000000..de3863d6b07 --- /dev/null +++ b/drivers/staging/iio/meter/Makefile @@ -0,0 +1,15 @@ +# +# Makefile for metering ic drivers +# + +obj-$(CONFIG_ADE7753) += ade7753.o +obj-$(CONFIG_ADE7754) += ade7754.o + +ade7758-y             := ade7758_core.o +ade7758-$(CONFIG_IIO_BUFFER) += ade7758_ring.o ade7758_trigger.o +obj-$(CONFIG_ADE7758) += ade7758.o + +obj-$(CONFIG_ADE7759) += ade7759.o +obj-$(CONFIG_ADE7854) += ade7854.o +obj-$(CONFIG_ADE7854_I2C) += ade7854-i2c.o +obj-$(CONFIG_ADE7854_SPI) += ade7854-spi.o diff --git a/drivers/staging/iio/meter/ade7753.c b/drivers/staging/iio/meter/ade7753.c new file mode 100644 index 00000000000..00492cad7c5 --- /dev/null +++ b/drivers/staging/iio/meter/ade7753.c @@ -0,0 +1,550 @@ +/* + * ADE7753 Single-Phase Multifunction Metering IC with di/dt Sensor Interface + * + * Copyright 2010 Analog Devices Inc. + * + * Licensed under the GPL-2 or later. + */ + +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/delay.h> +#include <linux/mutex.h> +#include <linux/device.h> +#include <linux/kernel.h> +#include <linux/spi/spi.h> +#include <linux/slab.h> +#include <linux/sysfs.h> +#include <linux/list.h> +#include <linux/module.h> + +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> +#include "meter.h" +#include "ade7753.h" + +static int ade7753_spi_write_reg_8(struct device *dev, +				   u8 reg_address, +				   u8 val) +{ +	int ret; +	struct iio_dev *indio_dev = dev_to_iio_dev(dev); +	struct ade7753_state *st = iio_priv(indio_dev); + +	mutex_lock(&st->buf_lock); +	st->tx[0] = ADE7753_WRITE_REG(reg_address); +	st->tx[1] = val; + +	ret = spi_write(st->us, st->tx, 2); +	mutex_unlock(&st->buf_lock); + +	return ret; +} + +static int ade7753_spi_write_reg_16(struct device *dev, +		u8 reg_address, +		u16 value) +{ +	int ret; +	struct iio_dev *indio_dev = dev_to_iio_dev(dev); +	struct ade7753_state *st = iio_priv(indio_dev); + +	mutex_lock(&st->buf_lock); +	st->tx[0] = ADE7753_WRITE_REG(reg_address); +	st->tx[1] = (value >> 8) & 0xFF; +	st->tx[2] = value & 0xFF; +	ret = spi_write(st->us, st->tx, 3); +	mutex_unlock(&st->buf_lock); + +	return ret; +} + +static int ade7753_spi_read_reg_8(struct device *dev, +		u8 reg_address, +		u8 *val) +{ +	struct iio_dev *indio_dev = dev_to_iio_dev(dev); +	struct ade7753_state *st = iio_priv(indio_dev); +	ssize_t ret; + +	ret = spi_w8r8(st->us, ADE7753_READ_REG(reg_address)); +	if (ret < 0) { +		dev_err(&st->us->dev, "problem when reading 8 bit register 0x%02X", +				reg_address); +		return ret; +	} +	*val = ret; + +	return 0; +} + +static int ade7753_spi_read_reg_16(struct device *dev, +		u8 reg_address, +		u16 *val) +{ +	struct iio_dev *indio_dev = dev_to_iio_dev(dev); +	struct ade7753_state *st = iio_priv(indio_dev); +	ssize_t ret; + +	ret = spi_w8r16be(st->us, ADE7753_READ_REG(reg_address)); +	if (ret < 0) { +		dev_err(&st->us->dev, "problem when reading 16 bit register 0x%02X", +			reg_address); +		return ret; +	} + +	*val = ret; + +	return 0; +} + +static int ade7753_spi_read_reg_24(struct device *dev, +		u8 reg_address, +		u32 *val) +{ +	struct iio_dev *indio_dev = dev_to_iio_dev(dev); +	struct ade7753_state *st = iio_priv(indio_dev); +	int ret; +	struct spi_transfer xfers[] = { +		{ +			.tx_buf = st->tx, +			.bits_per_word = 8, +			.len = 1, +		}, { +			.rx_buf = st->tx, +			.bits_per_word = 8, +			.len = 3, +		} +	}; + +	mutex_lock(&st->buf_lock); +	st->tx[0] = ADE7753_READ_REG(reg_address); + +	ret = spi_sync_transfer(st->us, xfers, ARRAY_SIZE(xfers)); +	if (ret) { +		dev_err(&st->us->dev, "problem when reading 24 bit register 0x%02X", +				reg_address); +		goto error_ret; +	} +	*val = (st->rx[0] << 16) | (st->rx[1] << 8) | st->rx[2]; + +error_ret: +	mutex_unlock(&st->buf_lock); +	return ret; +} + +static ssize_t ade7753_read_8bit(struct device *dev, +		struct device_attribute *attr, +		char *buf) +{ +	int ret; +	u8 val; +	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); + +	ret = ade7753_spi_read_reg_8(dev, this_attr->address, &val); +	if (ret) +		return ret; + +	return sprintf(buf, "%u\n", val); +} + +static ssize_t ade7753_read_16bit(struct device *dev, +		struct device_attribute *attr, +		char *buf) +{ +	int ret; +	u16 val; +	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); + +	ret = ade7753_spi_read_reg_16(dev, this_attr->address, &val); +	if (ret) +		return ret; + +	return sprintf(buf, "%u\n", val); +} + +static ssize_t ade7753_read_24bit(struct device *dev, +		struct device_attribute *attr, +		char *buf) +{ +	int ret; +	u32 val; +	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); + +	ret = ade7753_spi_read_reg_24(dev, this_attr->address, &val); +	if (ret) +		return ret; + +	return sprintf(buf, "%u\n", val); +} + +static ssize_t ade7753_write_8bit(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; +	u8 val; + +	ret = kstrtou8(buf, 10, &val); +	if (ret) +		goto error_ret; +	ret = ade7753_spi_write_reg_8(dev, this_attr->address, val); + +error_ret: +	return ret ? ret : len; +} + +static ssize_t ade7753_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; +	u16 val; + +	ret = kstrtou16(buf, 10, &val); +	if (ret) +		goto error_ret; +	ret = ade7753_spi_write_reg_16(dev, this_attr->address, val); + +error_ret: +	return ret ? ret : len; +} + +static int ade7753_reset(struct device *dev) +{ +	u16 val; + +	ade7753_spi_read_reg_16(dev, ADE7753_MODE, &val); +	val |= 1 << 6; /* Software Chip Reset */ + +	return ade7753_spi_write_reg_16(dev, ADE7753_MODE, val); +} + +static IIO_DEV_ATTR_AENERGY(ade7753_read_24bit, ADE7753_AENERGY); +static IIO_DEV_ATTR_LAENERGY(ade7753_read_24bit, ADE7753_LAENERGY); +static IIO_DEV_ATTR_VAENERGY(ade7753_read_24bit, ADE7753_VAENERGY); +static IIO_DEV_ATTR_LVAENERGY(ade7753_read_24bit, ADE7753_LVAENERGY); +static IIO_DEV_ATTR_CFDEN(S_IWUSR | S_IRUGO, +		ade7753_read_16bit, +		ade7753_write_16bit, +		ADE7753_CFDEN); +static IIO_DEV_ATTR_CFNUM(S_IWUSR | S_IRUGO, +		ade7753_read_8bit, +		ade7753_write_8bit, +		ADE7753_CFNUM); +static IIO_DEV_ATTR_CHKSUM(ade7753_read_8bit, ADE7753_CHKSUM); +static IIO_DEV_ATTR_PHCAL(S_IWUSR | S_IRUGO, +		ade7753_read_16bit, +		ade7753_write_16bit, +		ADE7753_PHCAL); +static IIO_DEV_ATTR_APOS(S_IWUSR | S_IRUGO, +		ade7753_read_16bit, +		ade7753_write_16bit, +		ADE7753_APOS); +static IIO_DEV_ATTR_SAGCYC(S_IWUSR | S_IRUGO, +		ade7753_read_8bit, +		ade7753_write_8bit, +		ADE7753_SAGCYC); +static IIO_DEV_ATTR_SAGLVL(S_IWUSR | S_IRUGO, +		ade7753_read_8bit, +		ade7753_write_8bit, +		ADE7753_SAGLVL); +static IIO_DEV_ATTR_LINECYC(S_IWUSR | S_IRUGO, +		ade7753_read_8bit, +		ade7753_write_8bit, +		ADE7753_LINECYC); +static IIO_DEV_ATTR_WDIV(S_IWUSR | S_IRUGO, +		ade7753_read_8bit, +		ade7753_write_8bit, +		ADE7753_WDIV); +static IIO_DEV_ATTR_IRMS(S_IWUSR | S_IRUGO, +		ade7753_read_24bit, +		NULL, +		ADE7753_IRMS); +static IIO_DEV_ATTR_VRMS(S_IRUGO, +		ade7753_read_24bit, +		NULL, +		ADE7753_VRMS); +static IIO_DEV_ATTR_IRMSOS(S_IWUSR | S_IRUGO, +		ade7753_read_16bit, +		ade7753_write_16bit, +		ADE7753_IRMSOS); +static IIO_DEV_ATTR_VRMSOS(S_IWUSR | S_IRUGO, +		ade7753_read_16bit, +		ade7753_write_16bit, +		ADE7753_VRMSOS); +static IIO_DEV_ATTR_WGAIN(S_IWUSR | S_IRUGO, +		ade7753_read_16bit, +		ade7753_write_16bit, +		ADE7753_WGAIN); +static IIO_DEV_ATTR_VAGAIN(S_IWUSR | S_IRUGO, +		ade7753_read_16bit, +		ade7753_write_16bit, +		ADE7753_VAGAIN); +static IIO_DEV_ATTR_PGA_GAIN(S_IWUSR | S_IRUGO, +		ade7753_read_16bit, +		ade7753_write_16bit, +		ADE7753_GAIN); +static IIO_DEV_ATTR_IPKLVL(S_IWUSR | S_IRUGO, +		ade7753_read_8bit, +		ade7753_write_8bit, +		ADE7753_IPKLVL); +static IIO_DEV_ATTR_VPKLVL(S_IWUSR | S_IRUGO, +		ade7753_read_8bit, +		ade7753_write_8bit, +		ADE7753_VPKLVL); +static IIO_DEV_ATTR_IPEAK(S_IRUGO, +		ade7753_read_24bit, +		NULL, +		ADE7753_IPEAK); +static IIO_DEV_ATTR_VPEAK(S_IRUGO, +		ade7753_read_24bit, +		NULL, +		ADE7753_VPEAK); +static IIO_DEV_ATTR_VPERIOD(S_IRUGO, +		ade7753_read_16bit, +		NULL, +		ADE7753_PERIOD); +static IIO_DEV_ATTR_CH_OFF(1, S_IWUSR | S_IRUGO, +		ade7753_read_8bit, +		ade7753_write_8bit, +		ADE7753_CH1OS); +static IIO_DEV_ATTR_CH_OFF(2, S_IWUSR | S_IRUGO, +		ade7753_read_8bit, +		ade7753_write_8bit, +		ADE7753_CH2OS); + +static int ade7753_set_irq(struct device *dev, bool enable) +{ +	int ret; +	u8 irqen; +	ret = ade7753_spi_read_reg_8(dev, ADE7753_IRQEN, &irqen); +	if (ret) +		goto error_ret; + +	if (enable) +		irqen |= 1 << 3; /* Enables an interrupt when a data is +				    present in the waveform register */ +	else +		irqen &= ~(1 << 3); + +	ret = ade7753_spi_write_reg_8(dev, ADE7753_IRQEN, irqen); + +error_ret: +	return ret; +} + +/* Power down the device */ +static int ade7753_stop_device(struct device *dev) +{ +	u16 val; + +	ade7753_spi_read_reg_16(dev, ADE7753_MODE, &val); +	val |= 1 << 4;  /* AD converters can be turned off */ + +	return ade7753_spi_write_reg_16(dev, ADE7753_MODE, val); +} + +static int ade7753_initial_setup(struct iio_dev *indio_dev) +{ +	int ret; +	struct device *dev = &indio_dev->dev; +	struct ade7753_state *st = iio_priv(indio_dev); + +	/* use low spi speed for init */ +	st->us->mode = SPI_MODE_3; +	spi_setup(st->us); + +	/* Disable IRQ */ +	ret = ade7753_set_irq(dev, false); +	if (ret) { +		dev_err(dev, "disable irq failed"); +		goto err_ret; +	} + +	ade7753_reset(dev); +	msleep(ADE7753_STARTUP_DELAY); + +err_ret: +	return ret; +} + +static ssize_t ade7753_read_frequency(struct device *dev, +		struct device_attribute *attr, +		char *buf) +{ +	int ret, len = 0; +	u16 t; +	int sps; +	ret = ade7753_spi_read_reg_16(dev, ADE7753_MODE, &t); +	if (ret) +		return ret; + +	t = (t >> 11) & 0x3; +	sps = 27900 / (1 + t); + +	len = sprintf(buf, "%d\n", sps); +	return len; +} + +static ssize_t ade7753_write_frequency(struct device *dev, +		struct device_attribute *attr, +		const char *buf, +		size_t len) +{ +	struct iio_dev *indio_dev = dev_to_iio_dev(dev); +	struct ade7753_state *st = iio_priv(indio_dev); +	u16 val; +	int ret; +	u16 reg, t; + +	ret = kstrtou16(buf, 10, &val); +	if (ret) +		return ret; +	if (val == 0) +		return -EINVAL; + +	mutex_lock(&indio_dev->mlock); + +	t = (27900 / val); +	if (t > 0) +		t--; + +	if (t > 1) +		st->us->max_speed_hz = ADE7753_SPI_SLOW; +	else +		st->us->max_speed_hz = ADE7753_SPI_FAST; + +	ret = ade7753_spi_read_reg_16(dev, ADE7753_MODE, ®); +	if (ret) +		goto out; + +	reg &= ~(3 << 11); +	reg |= t << 11; + +	ret = ade7753_spi_write_reg_16(dev, ADE7753_MODE, reg); + +out: +	mutex_unlock(&indio_dev->mlock); + +	return ret ? ret : len; +} + +static IIO_DEV_ATTR_TEMP_RAW(ade7753_read_8bit); +static IIO_CONST_ATTR(in_temp_offset, "-25 C"); +static IIO_CONST_ATTR(in_temp_scale, "0.67 C"); + +static IIO_DEV_ATTR_SAMP_FREQ(S_IWUSR | S_IRUGO, +		ade7753_read_frequency, +		ade7753_write_frequency); + +static IIO_CONST_ATTR_SAMP_FREQ_AVAIL("27900 14000 7000 3500"); + +static struct attribute *ade7753_attributes[] = { +	&iio_dev_attr_in_temp_raw.dev_attr.attr, +	&iio_const_attr_in_temp_offset.dev_attr.attr, +	&iio_const_attr_in_temp_scale.dev_attr.attr, +	&iio_dev_attr_sampling_frequency.dev_attr.attr, +	&iio_const_attr_sampling_frequency_available.dev_attr.attr, +	&iio_dev_attr_phcal.dev_attr.attr, +	&iio_dev_attr_cfden.dev_attr.attr, +	&iio_dev_attr_aenergy.dev_attr.attr, +	&iio_dev_attr_laenergy.dev_attr.attr, +	&iio_dev_attr_vaenergy.dev_attr.attr, +	&iio_dev_attr_lvaenergy.dev_attr.attr, +	&iio_dev_attr_cfnum.dev_attr.attr, +	&iio_dev_attr_apos.dev_attr.attr, +	&iio_dev_attr_sagcyc.dev_attr.attr, +	&iio_dev_attr_saglvl.dev_attr.attr, +	&iio_dev_attr_linecyc.dev_attr.attr, +	&iio_dev_attr_chksum.dev_attr.attr, +	&iio_dev_attr_pga_gain.dev_attr.attr, +	&iio_dev_attr_wgain.dev_attr.attr, +	&iio_dev_attr_choff_1.dev_attr.attr, +	&iio_dev_attr_choff_2.dev_attr.attr, +	&iio_dev_attr_wdiv.dev_attr.attr, +	&iio_dev_attr_irms.dev_attr.attr, +	&iio_dev_attr_vrms.dev_attr.attr, +	&iio_dev_attr_irmsos.dev_attr.attr, +	&iio_dev_attr_vrmsos.dev_attr.attr, +	&iio_dev_attr_vagain.dev_attr.attr, +	&iio_dev_attr_ipklvl.dev_attr.attr, +	&iio_dev_attr_vpklvl.dev_attr.attr, +	&iio_dev_attr_ipeak.dev_attr.attr, +	&iio_dev_attr_vpeak.dev_attr.attr, +	&iio_dev_attr_vperiod.dev_attr.attr, +	NULL, +}; + +static const struct attribute_group ade7753_attribute_group = { +	.attrs = ade7753_attributes, +}; + +static const struct iio_info ade7753_info = { +	.attrs = &ade7753_attribute_group, +	.driver_module = THIS_MODULE, +}; + +static int ade7753_probe(struct spi_device *spi) +{ +	int ret; +	struct ade7753_state *st; +	struct iio_dev *indio_dev; + +	/* setup the industrialio driver allocated elements */ +	indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); +	if (!indio_dev) +		return -ENOMEM; +	/* this is only used for removal purposes */ +	spi_set_drvdata(spi, indio_dev); + +	st = iio_priv(indio_dev); +	st->us = spi; +	mutex_init(&st->buf_lock); + +	indio_dev->name = spi->dev.driver->name; +	indio_dev->dev.parent = &spi->dev; +	indio_dev->info = &ade7753_info; +	indio_dev->modes = INDIO_DIRECT_MODE; + +	/* Get the device into a sane initial state */ +	ret = ade7753_initial_setup(indio_dev); +	if (ret) +		return ret; + +	ret = iio_device_register(indio_dev); +	if (ret) +		return ret; + +	return 0; +} + +/* fixme, confirm ordering in this function */ +static int ade7753_remove(struct spi_device *spi) +{ +	struct iio_dev *indio_dev = spi_get_drvdata(spi); + +	iio_device_unregister(indio_dev); +	ade7753_stop_device(&indio_dev->dev); + +	return 0; +} + +static struct spi_driver ade7753_driver = { +	.driver = { +		.name = "ade7753", +		.owner = THIS_MODULE, +	}, +	.probe = ade7753_probe, +	.remove = ade7753_remove, +}; +module_spi_driver(ade7753_driver); + +MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>"); +MODULE_DESCRIPTION("Analog Devices ADE7753/6 Single-Phase Multifunction Meter"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("spi:ade7753"); diff --git a/drivers/staging/iio/meter/ade7753.h b/drivers/staging/iio/meter/ade7753.h new file mode 100644 index 00000000000..a9d93cc1c41 --- /dev/null +++ b/drivers/staging/iio/meter/ade7753.h @@ -0,0 +1,72 @@ +#ifndef _ADE7753_H +#define _ADE7753_H + +#define ADE7753_WAVEFORM   0x01 +#define ADE7753_AENERGY    0x02 +#define ADE7753_RAENERGY   0x03 +#define ADE7753_LAENERGY   0x04 +#define ADE7753_VAENERGY   0x05 +#define ADE7753_RVAENERGY  0x06 +#define ADE7753_LVAENERGY  0x07 +#define ADE7753_LVARENERGY 0x08 +#define ADE7753_MODE       0x09 +#define ADE7753_IRQEN      0x0A +#define ADE7753_STATUS     0x0B +#define ADE7753_RSTSTATUS  0x0C +#define ADE7753_CH1OS      0x0D +#define ADE7753_CH2OS      0x0E +#define ADE7753_GAIN       0x0F +#define ADE7753_PHCAL      0x10 +#define ADE7753_APOS       0x11 +#define ADE7753_WGAIN      0x12 +#define ADE7753_WDIV       0x13 +#define ADE7753_CFNUM      0x14 +#define ADE7753_CFDEN      0x15 +#define ADE7753_IRMS       0x16 +#define ADE7753_VRMS       0x17 +#define ADE7753_IRMSOS     0x18 +#define ADE7753_VRMSOS     0x19 +#define ADE7753_VAGAIN     0x1A +#define ADE7753_VADIV      0x1B +#define ADE7753_LINECYC    0x1C +#define ADE7753_ZXTOUT     0x1D +#define ADE7753_SAGCYC     0x1E +#define ADE7753_SAGLVL     0x1F +#define ADE7753_IPKLVL     0x20 +#define ADE7753_VPKLVL     0x21 +#define ADE7753_IPEAK      0x22 +#define ADE7753_RSTIPEAK   0x23 +#define ADE7753_VPEAK      0x24 +#define ADE7753_RSTVPEAK   0x25 +#define ADE7753_TEMP       0x26 +#define ADE7753_PERIOD     0x27 +#define ADE7753_TMODE      0x3D +#define ADE7753_CHKSUM     0x3E +#define ADE7753_DIEREV     0x3F + +#define ADE7753_READ_REG(a)    a +#define ADE7753_WRITE_REG(a) ((a) | 0x80) + +#define ADE7753_MAX_TX    4 +#define ADE7753_MAX_RX    4 +#define ADE7753_STARTUP_DELAY 1 + +#define ADE7753_SPI_SLOW	(u32)(300 * 1000) +#define ADE7753_SPI_BURST	(u32)(1000 * 1000) +#define ADE7753_SPI_FAST	(u32)(2000 * 1000) + +/** + * struct ade7753_state - device instance specific data + * @us:			actual spi_device + * @tx:			transmit buffer + * @rx:			receive buffer + * @buf_lock:		mutex to protect tx and rx + **/ +struct ade7753_state { +	struct spi_device	*us; +	struct mutex		buf_lock; +	u8			tx[ADE7753_MAX_TX] ____cacheline_aligned; +	u8			rx[ADE7753_MAX_RX]; +}; + +#endif diff --git a/drivers/staging/iio/meter/ade7754.c b/drivers/staging/iio/meter/ade7754.c new file mode 100644 index 00000000000..e0aa13ab365 --- /dev/null +++ b/drivers/staging/iio/meter/ade7754.c @@ -0,0 +1,572 @@ +/* + * ADE7754 Polyphase Multifunction Energy Metering IC Driver + * + * Copyright 2010 Analog Devices Inc. + * + * Licensed under the GPL-2 or later. + */ + +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/delay.h> +#include <linux/mutex.h> +#include <linux/device.h> +#include <linux/kernel.h> +#include <linux/spi/spi.h> +#include <linux/slab.h> +#include <linux/sysfs.h> +#include <linux/list.h> +#include <linux/module.h> + +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> +#include "meter.h" +#include "ade7754.h" + +static int ade7754_spi_write_reg_8(struct device *dev, +		u8 reg_address, +		u8 val) +{ +	int ret; +	struct iio_dev *indio_dev = dev_to_iio_dev(dev); +	struct ade7754_state *st = iio_priv(indio_dev); + +	mutex_lock(&st->buf_lock); +	st->tx[0] = ADE7754_WRITE_REG(reg_address); +	st->tx[1] = val; + +	ret = spi_write(st->us, st->tx, 2); +	mutex_unlock(&st->buf_lock); + +	return ret; +} + +static int ade7754_spi_write_reg_16(struct device *dev, +		u8 reg_address, +		u16 value) +{ +	int ret; +	struct iio_dev *indio_dev = dev_to_iio_dev(dev); +	struct ade7754_state *st = iio_priv(indio_dev); + +	mutex_lock(&st->buf_lock); +	st->tx[0] = ADE7754_WRITE_REG(reg_address); +	st->tx[1] = (value >> 8) & 0xFF; +	st->tx[2] = value & 0xFF; +	ret = spi_write(st->us, st->tx, 3); +	mutex_unlock(&st->buf_lock); + +	return ret; +} + +static int ade7754_spi_read_reg_8(struct device *dev, +		u8 reg_address, +		u8 *val) +{ +	struct iio_dev *indio_dev = dev_to_iio_dev(dev); +	struct ade7754_state *st = iio_priv(indio_dev); +	int ret; + +	ret = spi_w8r8(st->us, ADE7754_READ_REG(reg_address)); +	if (ret < 0) { +		dev_err(&st->us->dev, "problem when reading 8 bit register 0x%02X", +				reg_address); +		return ret; +	} +	*val = ret; + +	return 0; +} + +static int ade7754_spi_read_reg_16(struct device *dev, +		u8 reg_address, +		u16 *val) +{ +	struct iio_dev *indio_dev = dev_to_iio_dev(dev); +	struct ade7754_state *st = iio_priv(indio_dev); +	int ret; + +	ret = spi_w8r16be(st->us, ADE7754_READ_REG(reg_address)); +	if (ret < 0) { +		dev_err(&st->us->dev, "problem when reading 16 bit register 0x%02X", +			reg_address); +		return ret; +	} + +	*val = ret; + +	return 0; +} + +static int ade7754_spi_read_reg_24(struct device *dev, +		u8 reg_address, +		u32 *val) +{ +	struct iio_dev *indio_dev = dev_to_iio_dev(dev); +	struct ade7754_state *st = iio_priv(indio_dev); +	int ret; +	struct spi_transfer xfers[] = { +		{ +			.tx_buf = st->tx, +			.rx_buf = st->rx, +			.bits_per_word = 8, +			.len = 4, +		}, +	}; + +	mutex_lock(&st->buf_lock); +	st->tx[0] = ADE7754_READ_REG(reg_address); +	st->tx[1] = 0; +	st->tx[2] = 0; +	st->tx[3] = 0; + +	ret = spi_sync_transfer(st->us, xfers, ARRAY_SIZE(xfers)); +	if (ret) { +		dev_err(&st->us->dev, "problem when reading 24 bit register 0x%02X", +				reg_address); +		goto error_ret; +	} +	*val = (st->rx[1] << 16) | (st->rx[2] << 8) | st->rx[3]; + +error_ret: +	mutex_unlock(&st->buf_lock); +	return ret; +} + +static ssize_t ade7754_read_8bit(struct device *dev, +		struct device_attribute *attr, +		char *buf) +{ +	int ret; +	u8 val = 0; +	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); + +	ret = ade7754_spi_read_reg_8(dev, this_attr->address, &val); +	if (ret) +		return ret; + +	return sprintf(buf, "%u\n", val); +} + +static ssize_t ade7754_read_16bit(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 = ade7754_spi_read_reg_16(dev, this_attr->address, &val); +	if (ret) +		return ret; + +	return sprintf(buf, "%u\n", val); +} + +static ssize_t ade7754_read_24bit(struct device *dev, +		struct device_attribute *attr, +		char *buf) +{ +	int ret; +	u32 val = 0; +	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); + +	ret = ade7754_spi_read_reg_24(dev, this_attr->address, &val); +	if (ret) +		return ret; + +	return sprintf(buf, "%u\n", val & 0xFFFFFF); +} + +static ssize_t ade7754_write_8bit(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; +	u8 val; + +	ret = kstrtou8(buf, 10, &val); +	if (ret) +		goto error_ret; +	ret = ade7754_spi_write_reg_8(dev, this_attr->address, val); + +error_ret: +	return ret ? ret : len; +} + +static ssize_t ade7754_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; +	u16 val; + +	ret = kstrtou16(buf, 10, &val); +	if (ret) +		goto error_ret; +	ret = ade7754_spi_write_reg_16(dev, this_attr->address, val); + +error_ret: +	return ret ? ret : len; +} + +static int ade7754_reset(struct device *dev) +{ +	u8 val; + +	ade7754_spi_read_reg_8(dev, ADE7754_OPMODE, &val); +	val |= 1 << 6; /* Software Chip Reset */ +	return ade7754_spi_write_reg_8(dev, ADE7754_OPMODE, val); +} + +static IIO_DEV_ATTR_AENERGY(ade7754_read_24bit, ADE7754_AENERGY); +static IIO_DEV_ATTR_LAENERGY(ade7754_read_24bit, ADE7754_LAENERGY); +static IIO_DEV_ATTR_VAENERGY(ade7754_read_24bit, ADE7754_VAENERGY); +static IIO_DEV_ATTR_LVAENERGY(ade7754_read_24bit, ADE7754_LVAENERGY); +static IIO_DEV_ATTR_VPEAK(S_IWUSR | S_IRUGO, +		ade7754_read_8bit, +		ade7754_write_8bit, +		ADE7754_VPEAK); +static IIO_DEV_ATTR_IPEAK(S_IWUSR | S_IRUGO, +		ade7754_read_8bit, +		ade7754_write_8bit, +		ADE7754_VPEAK); +static IIO_DEV_ATTR_APHCAL(S_IWUSR | S_IRUGO, +		ade7754_read_8bit, +		ade7754_write_8bit, +		ADE7754_APHCAL); +static IIO_DEV_ATTR_BPHCAL(S_IWUSR | S_IRUGO, +		ade7754_read_8bit, +		ade7754_write_8bit, +		ADE7754_BPHCAL); +static IIO_DEV_ATTR_CPHCAL(S_IWUSR | S_IRUGO, +		ade7754_read_8bit, +		ade7754_write_8bit, +		ADE7754_CPHCAL); +static IIO_DEV_ATTR_AAPOS(S_IWUSR | S_IRUGO, +		ade7754_read_16bit, +		ade7754_write_16bit, +		ADE7754_AAPOS); +static IIO_DEV_ATTR_BAPOS(S_IWUSR | S_IRUGO, +		ade7754_read_16bit, +		ade7754_write_16bit, +		ADE7754_BAPOS); +static IIO_DEV_ATTR_CAPOS(S_IWUSR | S_IRUGO, +		ade7754_read_16bit, +		ade7754_write_16bit, +		ADE7754_CAPOS); +static IIO_DEV_ATTR_WDIV(S_IWUSR | S_IRUGO, +		ade7754_read_8bit, +		ade7754_write_8bit, +		ADE7754_WDIV); +static IIO_DEV_ATTR_VADIV(S_IWUSR | S_IRUGO, +		ade7754_read_8bit, +		ade7754_write_8bit, +		ADE7754_VADIV); +static IIO_DEV_ATTR_CFNUM(S_IWUSR | S_IRUGO, +		ade7754_read_16bit, +		ade7754_write_16bit, +		ADE7754_CFNUM); +static IIO_DEV_ATTR_CFDEN(S_IWUSR | S_IRUGO, +		ade7754_read_16bit, +		ade7754_write_16bit, +		ADE7754_CFDEN); +static IIO_DEV_ATTR_ACTIVE_POWER_A_GAIN(S_IWUSR | S_IRUGO, +		ade7754_read_16bit, +		ade7754_write_16bit, +		ADE7754_AAPGAIN); +static IIO_DEV_ATTR_ACTIVE_POWER_B_GAIN(S_IWUSR | S_IRUGO, +		ade7754_read_16bit, +		ade7754_write_16bit, +		ADE7754_BAPGAIN); +static IIO_DEV_ATTR_ACTIVE_POWER_C_GAIN(S_IWUSR | S_IRUGO, +		ade7754_read_16bit, +		ade7754_write_16bit, +		ADE7754_CAPGAIN); +static IIO_DEV_ATTR_AIRMS(S_IRUGO, +		ade7754_read_24bit, +		NULL, +		ADE7754_AIRMS); +static IIO_DEV_ATTR_BIRMS(S_IRUGO, +		ade7754_read_24bit, +		NULL, +		ADE7754_BIRMS); +static IIO_DEV_ATTR_CIRMS(S_IRUGO, +		ade7754_read_24bit, +		NULL, +		ADE7754_CIRMS); +static IIO_DEV_ATTR_AVRMS(S_IRUGO, +		ade7754_read_24bit, +		NULL, +		ADE7754_AVRMS); +static IIO_DEV_ATTR_BVRMS(S_IRUGO, +		ade7754_read_24bit, +		NULL, +		ADE7754_BVRMS); +static IIO_DEV_ATTR_CVRMS(S_IRUGO, +		ade7754_read_24bit, +		NULL, +		ADE7754_CVRMS); +static IIO_DEV_ATTR_AIRMSOS(S_IRUGO, +		ade7754_read_16bit, +		ade7754_write_16bit, +		ADE7754_AIRMSOS); +static IIO_DEV_ATTR_BIRMSOS(S_IRUGO, +		ade7754_read_16bit, +		ade7754_write_16bit, +		ADE7754_BIRMSOS); +static IIO_DEV_ATTR_CIRMSOS(S_IRUGO, +		ade7754_read_16bit, +		ade7754_write_16bit, +		ADE7754_CIRMSOS); +static IIO_DEV_ATTR_AVRMSOS(S_IRUGO, +		ade7754_read_16bit, +		ade7754_write_16bit, +		ADE7754_AVRMSOS); +static IIO_DEV_ATTR_BVRMSOS(S_IRUGO, +		ade7754_read_16bit, +		ade7754_write_16bit, +		ADE7754_BVRMSOS); +static IIO_DEV_ATTR_CVRMSOS(S_IRUGO, +		ade7754_read_16bit, +		ade7754_write_16bit, +		ADE7754_CVRMSOS); + +static int ade7754_set_irq(struct device *dev, bool enable) +{ +	int ret; +	u16 irqen; +	ret = ade7754_spi_read_reg_16(dev, ADE7754_IRQEN, &irqen); +	if (ret) +		goto error_ret; + +	if (enable) +		irqen |= 1 << 14; /* Enables an interrupt when a data is +				     present in the waveform register */ +	else +		irqen &= ~(1 << 14); + +	ret = ade7754_spi_write_reg_16(dev, ADE7754_IRQEN, irqen); +	if (ret) +		goto error_ret; + +error_ret: +	return ret; +} + +/* Power down the device */ +static int ade7754_stop_device(struct device *dev) +{ +	u8 val; + +	ade7754_spi_read_reg_8(dev, ADE7754_OPMODE, &val); +	val |= 7 << 3;  /* ADE7754 powered down */ +	return ade7754_spi_write_reg_8(dev, ADE7754_OPMODE, val); +} + +static int ade7754_initial_setup(struct iio_dev *indio_dev) +{ +	int ret; +	struct ade7754_state *st = iio_priv(indio_dev); +	struct device *dev = &indio_dev->dev; + +	/* use low spi speed for init */ +	st->us->mode = SPI_MODE_3; +	spi_setup(st->us); + +	/* Disable IRQ */ +	ret = ade7754_set_irq(dev, false); +	if (ret) { +		dev_err(dev, "disable irq failed"); +		goto err_ret; +	} + +	ade7754_reset(dev); +	msleep(ADE7754_STARTUP_DELAY); + +err_ret: +	return ret; +} + +static ssize_t ade7754_read_frequency(struct device *dev, +		struct device_attribute *attr, +		char *buf) +{ +	int ret; +	u8 t; +	int sps; +	ret = ade7754_spi_read_reg_8(dev, +			ADE7754_WAVMODE, +			&t); +	if (ret) +		return ret; + +	t = (t >> 3) & 0x3; +	sps = 26000 / (1 + t); + +	return sprintf(buf, "%d\n", sps); +} + +static ssize_t ade7754_write_frequency(struct device *dev, +		struct device_attribute *attr, +		const char *buf, +		size_t len) +{ +	struct iio_dev *indio_dev = dev_to_iio_dev(dev); +	struct ade7754_state *st = iio_priv(indio_dev); +	u16 val; +	int ret; +	u8 reg, t; + +	ret = kstrtou16(buf, 10, &val); +	if (ret) +		return ret; +	if (val == 0) +		return -EINVAL; + +	mutex_lock(&indio_dev->mlock); + +	t = (26000 / val); +	if (t > 0) +		t--; + +	if (t > 1) +		st->us->max_speed_hz = ADE7754_SPI_SLOW; +	else +		st->us->max_speed_hz = ADE7754_SPI_FAST; + +	ret = ade7754_spi_read_reg_8(dev, ADE7754_WAVMODE, ®); +	if (ret) +		goto out; + +	reg &= ~(3 << 3); +	reg |= t << 3; + +	ret = ade7754_spi_write_reg_8(dev, ADE7754_WAVMODE, reg); + +out: +	mutex_unlock(&indio_dev->mlock); + +	return ret ? ret : len; +} +static IIO_DEV_ATTR_TEMP_RAW(ade7754_read_8bit); +static IIO_CONST_ATTR(in_temp_offset, "129 C"); +static IIO_CONST_ATTR(in_temp_scale, "4 C"); + +static IIO_DEV_ATTR_SAMP_FREQ(S_IWUSR | S_IRUGO, +		ade7754_read_frequency, +		ade7754_write_frequency); + +static IIO_CONST_ATTR_SAMP_FREQ_AVAIL("26000 13000 65000 33000"); + +static struct attribute *ade7754_attributes[] = { +	&iio_dev_attr_in_temp_raw.dev_attr.attr, +	&iio_const_attr_in_temp_offset.dev_attr.attr, +	&iio_const_attr_in_temp_scale.dev_attr.attr, +	&iio_dev_attr_sampling_frequency.dev_attr.attr, +	&iio_const_attr_sampling_frequency_available.dev_attr.attr, +	&iio_dev_attr_aenergy.dev_attr.attr, +	&iio_dev_attr_laenergy.dev_attr.attr, +	&iio_dev_attr_vaenergy.dev_attr.attr, +	&iio_dev_attr_lvaenergy.dev_attr.attr, +	&iio_dev_attr_vpeak.dev_attr.attr, +	&iio_dev_attr_ipeak.dev_attr.attr, +	&iio_dev_attr_aphcal.dev_attr.attr, +	&iio_dev_attr_bphcal.dev_attr.attr, +	&iio_dev_attr_cphcal.dev_attr.attr, +	&iio_dev_attr_aapos.dev_attr.attr, +	&iio_dev_attr_bapos.dev_attr.attr, +	&iio_dev_attr_capos.dev_attr.attr, +	&iio_dev_attr_wdiv.dev_attr.attr, +	&iio_dev_attr_vadiv.dev_attr.attr, +	&iio_dev_attr_cfnum.dev_attr.attr, +	&iio_dev_attr_cfden.dev_attr.attr, +	&iio_dev_attr_active_power_a_gain.dev_attr.attr, +	&iio_dev_attr_active_power_b_gain.dev_attr.attr, +	&iio_dev_attr_active_power_c_gain.dev_attr.attr, +	&iio_dev_attr_airms.dev_attr.attr, +	&iio_dev_attr_birms.dev_attr.attr, +	&iio_dev_attr_cirms.dev_attr.attr, +	&iio_dev_attr_avrms.dev_attr.attr, +	&iio_dev_attr_bvrms.dev_attr.attr, +	&iio_dev_attr_cvrms.dev_attr.attr, +	&iio_dev_attr_airmsos.dev_attr.attr, +	&iio_dev_attr_birmsos.dev_attr.attr, +	&iio_dev_attr_cirmsos.dev_attr.attr, +	&iio_dev_attr_avrmsos.dev_attr.attr, +	&iio_dev_attr_bvrmsos.dev_attr.attr, +	&iio_dev_attr_cvrmsos.dev_attr.attr, +	NULL, +}; + +static const struct attribute_group ade7754_attribute_group = { +	.attrs = ade7754_attributes, +}; + +static const struct iio_info ade7754_info = { +	.attrs = &ade7754_attribute_group, +	.driver_module = THIS_MODULE, +}; + +static int ade7754_probe(struct spi_device *spi) +{ +	int ret; +	struct ade7754_state *st; +	struct iio_dev *indio_dev; + +	/* setup the industrialio driver allocated elements */ +	indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); +	if (!indio_dev) +		return -ENOMEM; +	/* this is only used for removal purposes */ +	spi_set_drvdata(spi, indio_dev); + +	st = iio_priv(indio_dev); +	st->us = spi; +	mutex_init(&st->buf_lock); + +	indio_dev->name = spi->dev.driver->name; +	indio_dev->dev.parent = &spi->dev; +	indio_dev->info = &ade7754_info; +	indio_dev->modes = INDIO_DIRECT_MODE; + +	/* Get the device into a sane initial state */ +	ret = ade7754_initial_setup(indio_dev); +	if (ret) +		return ret; +	ret = iio_device_register(indio_dev); +	if (ret) +		return ret; + +	return 0; +} + +/* fixme, confirm ordering in this function */ +static int ade7754_remove(struct spi_device *spi) +{ +	struct iio_dev *indio_dev = spi_get_drvdata(spi); + +	iio_device_unregister(indio_dev); +	ade7754_stop_device(&indio_dev->dev); + +	return 0; +} + +static struct spi_driver ade7754_driver = { +	.driver = { +		.name = "ade7754", +		.owner = THIS_MODULE, +	}, +	.probe = ade7754_probe, +	.remove = ade7754_remove, +}; +module_spi_driver(ade7754_driver); + +MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>"); +MODULE_DESCRIPTION("Analog Devices ADE7754 Polyphase Multifunction Energy Metering IC Driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("spi:ad7754"); diff --git a/drivers/staging/iio/meter/ade7754.h b/drivers/staging/iio/meter/ade7754.h new file mode 100644 index 00000000000..e42ffc387a1 --- /dev/null +++ b/drivers/staging/iio/meter/ade7754.h @@ -0,0 +1,90 @@ +#ifndef _ADE7754_H +#define _ADE7754_H + +#define ADE7754_AENERGY   0x01 +#define ADE7754_RAENERGY  0x02 +#define ADE7754_LAENERGY  0x03 +#define ADE7754_VAENERGY  0x04 +#define ADE7754_RVAENERGY 0x05 +#define ADE7754_LVAENERGY 0x06 +#define ADE7754_PERIOD    0x07 +#define ADE7754_TEMP      0x08 +#define ADE7754_WFORM     0x09 +#define ADE7754_OPMODE    0x0A +#define ADE7754_MMODE     0x0B +#define ADE7754_WAVMODE   0x0C +#define ADE7754_WATMODE   0x0D +#define ADE7754_VAMODE    0x0E +#define ADE7754_IRQEN     0x0F +#define ADE7754_STATUS    0x10 +#define ADE7754_RSTATUS   0x11 +#define ADE7754_ZXTOUT    0x12 +#define ADE7754_LINCYC    0x13 +#define ADE7754_SAGCYC    0x14 +#define ADE7754_SAGLVL    0x15 +#define ADE7754_VPEAK     0x16 +#define ADE7754_IPEAK     0x17 +#define ADE7754_GAIN      0x18 +#define ADE7754_AWG       0x19 +#define ADE7754_BWG       0x1A +#define ADE7754_CWG       0x1B +#define ADE7754_AVAG      0x1C +#define ADE7754_BVAG      0x1D +#define ADE7754_CVAG      0x1E +#define ADE7754_APHCAL    0x1F +#define ADE7754_BPHCAL    0x20 +#define ADE7754_CPHCAL    0x21 +#define ADE7754_AAPOS     0x22 +#define ADE7754_BAPOS     0x23 +#define ADE7754_CAPOS     0x24 +#define ADE7754_CFNUM     0x25 +#define ADE7754_CFDEN     0x26 +#define ADE7754_WDIV      0x27 +#define ADE7754_VADIV     0x28 +#define ADE7754_AIRMS     0x29 +#define ADE7754_BIRMS     0x2A +#define ADE7754_CIRMS     0x2B +#define ADE7754_AVRMS     0x2C +#define ADE7754_BVRMS     0x2D +#define ADE7754_CVRMS     0x2E +#define ADE7754_AIRMSOS   0x2F +#define ADE7754_BIRMSOS   0x30 +#define ADE7754_CIRMSOS   0x31 +#define ADE7754_AVRMSOS   0x32 +#define ADE7754_BVRMSOS   0x33 +#define ADE7754_CVRMSOS   0x34 +#define ADE7754_AAPGAIN   0x35 +#define ADE7754_BAPGAIN   0x36 +#define ADE7754_CAPGAIN   0x37 +#define ADE7754_AVGAIN    0x38 +#define ADE7754_BVGAIN    0x39 +#define ADE7754_CVGAIN    0x3A +#define ADE7754_CHKSUM    0x3E +#define ADE7754_VERSION   0x3F + +#define ADE7754_READ_REG(a)    a +#define ADE7754_WRITE_REG(a) ((a) | 0x80) + +#define ADE7754_MAX_TX    4 +#define ADE7754_MAX_RX    4 +#define ADE7754_STARTUP_DELAY 1 + +#define ADE7754_SPI_SLOW	(u32)(300 * 1000) +#define ADE7754_SPI_BURST	(u32)(1000 * 1000) +#define ADE7754_SPI_FAST	(u32)(2000 * 1000) + +/** + * struct ade7754_state - device instance specific data + * @us:			actual spi_device + * @buf_lock:		mutex to protect tx and rx + * @tx:			transmit buffer + * @rx:			receive buffer + **/ +struct ade7754_state { +	struct spi_device	*us; +	struct mutex		buf_lock; +	u8			tx[ADE7754_MAX_TX] ____cacheline_aligned; +	u8			rx[ADE7754_MAX_RX]; +}; + +#endif diff --git a/drivers/staging/iio/meter/ade7758.h b/drivers/staging/iio/meter/ade7758.h new file mode 100644 index 00000000000..07318203a83 --- /dev/null +++ b/drivers/staging/iio/meter/ade7758.h @@ -0,0 +1,183 @@ +/* + * ADE7758 Poly Phase Multifunction Energy Metering IC driver + * + * Copyright 2010-2011 Analog Devices Inc. + * + * Licensed under the GPL-2. + */ + +#ifndef _ADE7758_H +#define _ADE7758_H + +#define ADE7758_AWATTHR   0x01 +#define ADE7758_BWATTHR   0x02 +#define ADE7758_CWATTHR   0x03 +#define ADE7758_AVARHR    0x04 +#define ADE7758_BVARHR    0x05 +#define ADE7758_CVARHR    0x06 +#define ADE7758_AVAHR     0x07 +#define ADE7758_BVAHR     0x08 +#define ADE7758_CVAHR     0x09 +#define ADE7758_AIRMS     0x0A +#define ADE7758_BIRMS     0x0B +#define ADE7758_CIRMS     0x0C +#define ADE7758_AVRMS     0x0D +#define ADE7758_BVRMS     0x0E +#define ADE7758_CVRMS     0x0F +#define ADE7758_FREQ      0x10 +#define ADE7758_TEMP      0x11 +#define ADE7758_WFORM     0x12 +#define ADE7758_OPMODE    0x13 +#define ADE7758_MMODE     0x14 +#define ADE7758_WAVMODE   0x15 +#define ADE7758_COMPMODE  0x16 +#define ADE7758_LCYCMODE  0x17 +#define ADE7758_MASK      0x18 +#define ADE7758_STATUS    0x19 +#define ADE7758_RSTATUS   0x1A +#define ADE7758_ZXTOUT    0x1B +#define ADE7758_LINECYC   0x1C +#define ADE7758_SAGCYC    0x1D +#define ADE7758_SAGLVL    0x1E +#define ADE7758_VPINTLVL  0x1F +#define ADE7758_IPINTLVL  0x20 +#define ADE7758_VPEAK     0x21 +#define ADE7758_IPEAK     0x22 +#define ADE7758_GAIN      0x23 +#define ADE7758_AVRMSGAIN 0x24 +#define ADE7758_BVRMSGAIN 0x25 +#define ADE7758_CVRMSGAIN 0x26 +#define ADE7758_AIGAIN    0x27 +#define ADE7758_BIGAIN    0x28 +#define ADE7758_CIGAIN    0x29 +#define ADE7758_AWG       0x2A +#define ADE7758_BWG       0x2B +#define ADE7758_CWG       0x2C +#define ADE7758_AVARG     0x2D +#define ADE7758_BVARG     0x2E +#define ADE7758_CVARG     0x2F +#define ADE7758_AVAG      0x30 +#define ADE7758_BVAG      0x31 +#define ADE7758_CVAG      0x32 +#define ADE7758_AVRMSOS   0x33 +#define ADE7758_BVRMSOS   0x34 +#define ADE7758_CVRMSOS   0x35 +#define ADE7758_AIRMSOS   0x36 +#define ADE7758_BIRMSOS   0x37 +#define ADE7758_CIRMSOS   0x38 +#define ADE7758_AWAITOS   0x39 +#define ADE7758_BWAITOS   0x3A +#define ADE7758_CWAITOS   0x3B +#define ADE7758_AVAROS    0x3C +#define ADE7758_BVAROS    0x3D +#define ADE7758_CVAROS    0x3E +#define ADE7758_APHCAL    0x3F +#define ADE7758_BPHCAL    0x40 +#define ADE7758_CPHCAL    0x41 +#define ADE7758_WDIV      0x42 +#define ADE7758_VADIV     0x44 +#define ADE7758_VARDIV    0x43 +#define ADE7758_APCFNUM   0x45 +#define ADE7758_APCFDEN   0x46 +#define ADE7758_VARCFNUM  0x47 +#define ADE7758_VARCFDEN  0x48 +#define ADE7758_CHKSUM    0x7E +#define ADE7758_VERSION   0x7F + +#define ADE7758_READ_REG(a)    a +#define ADE7758_WRITE_REG(a) ((a) | 0x80) + +#define ADE7758_MAX_TX    8 +#define ADE7758_MAX_RX    4 +#define ADE7758_STARTUP_DELAY 1 + +#define AD7758_NUM_WAVSEL	5 +#define AD7758_NUM_PHSEL	3 +#define AD7758_NUM_WAVESRC	(AD7758_NUM_WAVSEL * AD7758_NUM_PHSEL) + +#define AD7758_PHASE_A		0 +#define AD7758_PHASE_B		1 +#define AD7758_PHASE_C		2 +#define AD7758_CURRENT		0 +#define AD7758_VOLTAGE		1 +#define AD7758_ACT_PWR		2 +#define AD7758_REACT_PWR	3 +#define AD7758_APP_PWR		4 +#define AD7758_WT(p, w)		(((w) << 2) | (p)) + +/** + * struct ade7758_state - device instance specific data + * @us:			actual spi_device + * @trig:		data ready trigger registered with iio + * @tx:			transmit buffer + * @rx:			receive buffer + * @buf_lock:		mutex to protect tx and rx + **/ +struct ade7758_state { +	struct spi_device	*us; +	struct iio_trigger	*trig; +	u8			*tx; +	u8			*rx; +	struct mutex		buf_lock; +	const struct iio_chan_spec *ade7758_ring_channels; +	struct spi_transfer	ring_xfer[4]; +	struct spi_message	ring_msg; +	/* +	 * DMA (thus cache coherency maintenance) requires the +	 * transfer buffers to live in their own cache lines. +	 */ +	unsigned char		rx_buf[8] ____cacheline_aligned; +	unsigned char		tx_buf[8]; + +}; +#ifdef CONFIG_IIO_BUFFER +/* At the moment triggers are only used for ring buffer + * filling. This may change! + */ + +void ade7758_remove_trigger(struct iio_dev *indio_dev); +int ade7758_probe_trigger(struct iio_dev *indio_dev); + +ssize_t ade7758_read_data_from_ring(struct device *dev, +		struct device_attribute *attr, +		char *buf); + + +int ade7758_configure_ring(struct iio_dev *indio_dev); +void ade7758_unconfigure_ring(struct iio_dev *indio_dev); + +void ade7758_uninitialize_ring(struct iio_dev *indio_dev); +int ade7758_set_irq(struct device *dev, bool enable); + +int ade7758_spi_write_reg_8(struct device *dev, +		u8 reg_address, u8 val); +int ade7758_spi_read_reg_8(struct device *dev, +		u8 reg_address, u8 *val); + +#else /* CONFIG_IIO_BUFFER */ + +static inline void ade7758_remove_trigger(struct iio_dev *indio_dev) +{ +} +static inline int ade7758_probe_trigger(struct iio_dev *indio_dev) +{ +	return 0; +} + +static int ade7758_configure_ring(struct iio_dev *indio_dev) +{ +	return 0; +} +static inline void ade7758_unconfigure_ring(struct iio_dev *indio_dev) +{ +} +static inline int ade7758_initialize_ring(struct iio_ring_buffer *ring) +{ +	return 0; +} +static inline void ade7758_uninitialize_ring(struct iio_dev *indio_dev) +{ +} +#endif /* CONFIG_IIO_BUFFER */ + +#endif diff --git a/drivers/staging/iio/meter/ade7758_core.c b/drivers/staging/iio/meter/ade7758_core.c new file mode 100644 index 00000000000..cba183e2483 --- /dev/null +++ b/drivers/staging/iio/meter/ade7758_core.c @@ -0,0 +1,958 @@ +/* + * ADE7758 Poly Phase Multifunction Energy Metering IC driver + * + * Copyright 2010-2011 Analog Devices Inc. + * + * Licensed under the GPL-2. + */ + +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/delay.h> +#include <linux/mutex.h> +#include <linux/device.h> +#include <linux/kernel.h> +#include <linux/spi/spi.h> +#include <linux/slab.h> +#include <linux/sysfs.h> +#include <linux/list.h> +#include <linux/module.h> + +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> +#include <linux/iio/buffer.h> +#include "meter.h" +#include "ade7758.h" + +int ade7758_spi_write_reg_8(struct device *dev, +		u8 reg_address, +		u8 val) +{ +	int ret; +	struct iio_dev *indio_dev = dev_to_iio_dev(dev); +	struct ade7758_state *st = iio_priv(indio_dev); + +	mutex_lock(&st->buf_lock); +	st->tx[0] = ADE7758_WRITE_REG(reg_address); +	st->tx[1] = val; + +	ret = spi_write(st->us, st->tx, 2); +	mutex_unlock(&st->buf_lock); + +	return ret; +} + +static int ade7758_spi_write_reg_16(struct device *dev, +		u8 reg_address, +		u16 value) +{ +	int ret; +	struct iio_dev *indio_dev = dev_to_iio_dev(dev); +	struct ade7758_state *st = iio_priv(indio_dev); +	struct spi_transfer xfers[] = { +		{ +			.tx_buf = st->tx, +			.bits_per_word = 8, +			.len = 3, +		} +	}; + +	mutex_lock(&st->buf_lock); +	st->tx[0] = ADE7758_WRITE_REG(reg_address); +	st->tx[1] = (value >> 8) & 0xFF; +	st->tx[2] = value & 0xFF; + +	ret = spi_sync_transfer(st->us, xfers, ARRAY_SIZE(xfers)); +	mutex_unlock(&st->buf_lock); + +	return ret; +} + +static int ade7758_spi_write_reg_24(struct device *dev, +		u8 reg_address, +		u32 value) +{ +	int ret; +	struct iio_dev *indio_dev = dev_to_iio_dev(dev); +	struct ade7758_state *st = iio_priv(indio_dev); +	struct spi_transfer xfers[] = { +		{ +			.tx_buf = st->tx, +			.bits_per_word = 8, +			.len = 4, +		} +	}; + +	mutex_lock(&st->buf_lock); +	st->tx[0] = ADE7758_WRITE_REG(reg_address); +	st->tx[1] = (value >> 16) & 0xFF; +	st->tx[2] = (value >> 8) & 0xFF; +	st->tx[3] = value & 0xFF; + +	ret = spi_sync_transfer(st->us, xfers, ARRAY_SIZE(xfers)); +	mutex_unlock(&st->buf_lock); + +	return ret; +} + +int ade7758_spi_read_reg_8(struct device *dev, +		u8 reg_address, +		u8 *val) +{ +	struct iio_dev *indio_dev = dev_to_iio_dev(dev); +	struct ade7758_state *st = iio_priv(indio_dev); +	int ret; +	struct spi_transfer xfers[] = { +		{ +			.tx_buf = st->tx, +			.bits_per_word = 8, +			.len = 1, +			.delay_usecs = 4, +		}, +		{ +			.tx_buf = &st->tx[1], +			.rx_buf = st->rx, +			.bits_per_word = 8, +			.len = 1, +		}, +	}; + +	mutex_lock(&st->buf_lock); +	st->tx[0] = ADE7758_READ_REG(reg_address); +	st->tx[1] = 0; + +	ret = spi_sync_transfer(st->us, xfers, ARRAY_SIZE(xfers)); +	if (ret) { +		dev_err(&st->us->dev, "problem when reading 8 bit register 0x%02X", +				reg_address); +		goto error_ret; +	} +	*val = st->rx[0]; + +error_ret: +	mutex_unlock(&st->buf_lock); +	return ret; +} + +static int ade7758_spi_read_reg_16(struct device *dev, +		u8 reg_address, +		u16 *val) +{ +	struct iio_dev *indio_dev = dev_to_iio_dev(dev); +	struct ade7758_state *st = iio_priv(indio_dev); +	int ret; +	struct spi_transfer xfers[] = { +		{ +			.tx_buf = st->tx, +			.bits_per_word = 8, +			.len = 1, +			.delay_usecs = 4, +		}, +		{ +			.tx_buf = &st->tx[1], +			.rx_buf = st->rx, +			.bits_per_word = 8, +			.len = 2, +		}, +	}; + + +	mutex_lock(&st->buf_lock); +	st->tx[0] = ADE7758_READ_REG(reg_address); +	st->tx[1] = 0; +	st->tx[2] = 0; + +	ret = spi_sync_transfer(st->us, xfers, ARRAY_SIZE(xfers)); +	if (ret) { +		dev_err(&st->us->dev, "problem when reading 16 bit register 0x%02X", +				reg_address); +		goto error_ret; +	} + +	*val = (st->rx[0] << 8) | st->rx[1]; + +error_ret: +	mutex_unlock(&st->buf_lock); +	return ret; +} + +static int ade7758_spi_read_reg_24(struct device *dev, +		u8 reg_address, +		u32 *val) +{ +	struct iio_dev *indio_dev = dev_to_iio_dev(dev); +	struct ade7758_state *st = iio_priv(indio_dev); +	int ret; +	struct spi_transfer xfers[] = { +		{ +			.tx_buf = st->tx, +			.bits_per_word = 8, +			.len = 1, +			.delay_usecs = 4, +		}, +		{ +			.tx_buf = &st->tx[1], +			.rx_buf = st->rx, +			.bits_per_word = 8, +			.len = 3, +		}, +	}; + +	mutex_lock(&st->buf_lock); +	st->tx[0] = ADE7758_READ_REG(reg_address); +	st->tx[1] = 0; +	st->tx[2] = 0; +	st->tx[3] = 0; + +	ret = spi_sync_transfer(st->us, xfers, ARRAY_SIZE(xfers)); +	if (ret) { +		dev_err(&st->us->dev, "problem when reading 24 bit register 0x%02X", +				reg_address); +		goto error_ret; +	} +	*val = (st->rx[0] << 16) | (st->rx[1] << 8) | st->rx[2]; + +error_ret: +	mutex_unlock(&st->buf_lock); +	return ret; +} + +static ssize_t ade7758_read_8bit(struct device *dev, +		struct device_attribute *attr, +		char *buf) +{ +	int ret; +	u8 val = 0; +	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); + +	ret = ade7758_spi_read_reg_8(dev, this_attr->address, &val); +	if (ret) +		return ret; + +	return sprintf(buf, "%u\n", val); +} + +static ssize_t ade7758_read_16bit(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 = ade7758_spi_read_reg_16(dev, this_attr->address, &val); +	if (ret) +		return ret; + +	return sprintf(buf, "%u\n", val); +} + +static ssize_t ade7758_read_24bit(struct device *dev, +		struct device_attribute *attr, +		char *buf) +{ +	int ret; +	u32 val = 0; +	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); + +	ret = ade7758_spi_read_reg_24(dev, this_attr->address, &val); +	if (ret) +		return ret; + +	return sprintf(buf, "%u\n", val & 0xFFFFFF); +} + +static ssize_t ade7758_write_8bit(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; +	u8 val; + +	ret = kstrtou8(buf, 10, &val); +	if (ret) +		goto error_ret; +	ret = ade7758_spi_write_reg_8(dev, this_attr->address, val); + +error_ret: +	return ret ? ret : len; +} + +static ssize_t ade7758_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; +	u16 val; + +	ret = kstrtou16(buf, 10, &val); +	if (ret) +		goto error_ret; +	ret = ade7758_spi_write_reg_16(dev, this_attr->address, val); + +error_ret: +	return ret ? ret : len; +} + +static int ade7758_reset(struct device *dev) +{ +	int ret; +	u8 val; +	ade7758_spi_read_reg_8(dev, +			ADE7758_OPMODE, +			&val); +	val |= 1 << 6; /* Software Chip Reset */ +	ret = ade7758_spi_write_reg_8(dev, +			ADE7758_OPMODE, +			val); + +	return ret; +} + +static IIO_DEV_ATTR_VPEAK(S_IWUSR | S_IRUGO, +		ade7758_read_8bit, +		ade7758_write_8bit, +		ADE7758_VPEAK); +static IIO_DEV_ATTR_IPEAK(S_IWUSR | S_IRUGO, +		ade7758_read_8bit, +		ade7758_write_8bit, +		ADE7758_VPEAK); +static IIO_DEV_ATTR_APHCAL(S_IWUSR | S_IRUGO, +		ade7758_read_8bit, +		ade7758_write_8bit, +		ADE7758_APHCAL); +static IIO_DEV_ATTR_BPHCAL(S_IWUSR | S_IRUGO, +		ade7758_read_8bit, +		ade7758_write_8bit, +		ADE7758_BPHCAL); +static IIO_DEV_ATTR_CPHCAL(S_IWUSR | S_IRUGO, +		ade7758_read_8bit, +		ade7758_write_8bit, +		ADE7758_CPHCAL); +static IIO_DEV_ATTR_WDIV(S_IWUSR | S_IRUGO, +		ade7758_read_8bit, +		ade7758_write_8bit, +		ADE7758_WDIV); +static IIO_DEV_ATTR_VADIV(S_IWUSR | S_IRUGO, +		ade7758_read_8bit, +		ade7758_write_8bit, +		ADE7758_VADIV); +static IIO_DEV_ATTR_AIRMS(S_IRUGO, +		ade7758_read_24bit, +		NULL, +		ADE7758_AIRMS); +static IIO_DEV_ATTR_BIRMS(S_IRUGO, +		ade7758_read_24bit, +		NULL, +		ADE7758_BIRMS); +static IIO_DEV_ATTR_CIRMS(S_IRUGO, +		ade7758_read_24bit, +		NULL, +		ADE7758_CIRMS); +static IIO_DEV_ATTR_AVRMS(S_IRUGO, +		ade7758_read_24bit, +		NULL, +		ADE7758_AVRMS); +static IIO_DEV_ATTR_BVRMS(S_IRUGO, +		ade7758_read_24bit, +		NULL, +		ADE7758_BVRMS); +static IIO_DEV_ATTR_CVRMS(S_IRUGO, +		ade7758_read_24bit, +		NULL, +		ADE7758_CVRMS); +static IIO_DEV_ATTR_AIRMSOS(S_IWUSR | S_IRUGO, +		ade7758_read_16bit, +		ade7758_write_16bit, +		ADE7758_AIRMSOS); +static IIO_DEV_ATTR_BIRMSOS(S_IWUSR | S_IRUGO, +		ade7758_read_16bit, +		ade7758_write_16bit, +		ADE7758_BIRMSOS); +static IIO_DEV_ATTR_CIRMSOS(S_IWUSR | S_IRUGO, +		ade7758_read_16bit, +		ade7758_write_16bit, +		ADE7758_CIRMSOS); +static IIO_DEV_ATTR_AVRMSOS(S_IWUSR | S_IRUGO, +		ade7758_read_16bit, +		ade7758_write_16bit, +		ADE7758_AVRMSOS); +static IIO_DEV_ATTR_BVRMSOS(S_IWUSR | S_IRUGO, +		ade7758_read_16bit, +		ade7758_write_16bit, +		ADE7758_BVRMSOS); +static IIO_DEV_ATTR_CVRMSOS(S_IWUSR | S_IRUGO, +		ade7758_read_16bit, +		ade7758_write_16bit, +		ADE7758_CVRMSOS); +static IIO_DEV_ATTR_AIGAIN(S_IWUSR | S_IRUGO, +		ade7758_read_16bit, +		ade7758_write_16bit, +		ADE7758_AIGAIN); +static IIO_DEV_ATTR_BIGAIN(S_IWUSR | S_IRUGO, +		ade7758_read_16bit, +		ade7758_write_16bit, +		ADE7758_BIGAIN); +static IIO_DEV_ATTR_CIGAIN(S_IWUSR | S_IRUGO, +		ade7758_read_16bit, +		ade7758_write_16bit, +		ADE7758_CIGAIN); +static IIO_DEV_ATTR_AVRMSGAIN(S_IWUSR | S_IRUGO, +		ade7758_read_16bit, +		ade7758_write_16bit, +		ADE7758_AVRMSGAIN); +static IIO_DEV_ATTR_BVRMSGAIN(S_IWUSR | S_IRUGO, +		ade7758_read_16bit, +		ade7758_write_16bit, +		ADE7758_BVRMSGAIN); +static IIO_DEV_ATTR_CVRMSGAIN(S_IWUSR | S_IRUGO, +		ade7758_read_16bit, +		ade7758_write_16bit, +		ADE7758_CVRMSGAIN); + +int ade7758_set_irq(struct device *dev, bool enable) +{ +	int ret; +	u32 irqen; +	ret = ade7758_spi_read_reg_24(dev, ADE7758_MASK, &irqen); +	if (ret) +		goto error_ret; + +	if (enable) +		irqen |= 1 << 16; /* Enables an interrupt when a data is +				     present in the waveform register */ +	else +		irqen &= ~(1 << 16); + +	ret = ade7758_spi_write_reg_24(dev, ADE7758_MASK, irqen); +	if (ret) +		goto error_ret; + +error_ret: +	return ret; +} + +/* Power down the device */ +static int ade7758_stop_device(struct device *dev) +{ +	int ret; +	u8 val; +	ade7758_spi_read_reg_8(dev, +			ADE7758_OPMODE, +			&val); +	val |= 7 << 3;  /* ADE7758 powered down */ +	ret = ade7758_spi_write_reg_8(dev, +			ADE7758_OPMODE, +			val); + +	return ret; +} + +static int ade7758_initial_setup(struct iio_dev *indio_dev) +{ +	struct ade7758_state *st = iio_priv(indio_dev); +	struct device *dev = &indio_dev->dev; +	int ret; + +	/* use low spi speed for init */ +	st->us->mode = SPI_MODE_1; +	spi_setup(st->us); + +	/* Disable IRQ */ +	ret = ade7758_set_irq(dev, false); +	if (ret) { +		dev_err(dev, "disable irq failed"); +		goto err_ret; +	} + +	ade7758_reset(dev); +	msleep(ADE7758_STARTUP_DELAY); + +err_ret: +	return ret; +} + +static ssize_t ade7758_read_frequency(struct device *dev, +		struct device_attribute *attr, +		char *buf) +{ +	int ret, len = 0; +	u8 t; +	int sps; +	ret = ade7758_spi_read_reg_8(dev, +			ADE7758_WAVMODE, +			&t); +	if (ret) +		return ret; + +	t = (t >> 5) & 0x3; +	sps = 26040 / (1 << t); + +	len = sprintf(buf, "%d SPS\n", sps); +	return len; +} + +static ssize_t ade7758_write_frequency(struct device *dev, +		struct device_attribute *attr, +		const char *buf, +		size_t len) +{ +	struct iio_dev *indio_dev = dev_to_iio_dev(dev); +	u16 val; +	int ret; +	u8 reg, t; + +	ret = kstrtou16(buf, 10, &val); +	if (ret) +		return ret; + +	mutex_lock(&indio_dev->mlock); + +	switch (val) { +	case 26040: +		t = 0; +		break; +	case 13020: +		t = 1; +		break; +	case 6510: +		t = 2; +		break; +	case 3255: +		t = 3; +		break; +	default: +		ret = -EINVAL; +		goto out; +	} + +	ret = ade7758_spi_read_reg_8(dev, +			ADE7758_WAVMODE, +			®); +	if (ret) +		goto out; + +	reg &= ~(5 << 3); +	reg |= t << 5; + +	ret = ade7758_spi_write_reg_8(dev, +			ADE7758_WAVMODE, +			reg); + +out: +	mutex_unlock(&indio_dev->mlock); + +	return ret ? ret : len; +} + +static IIO_DEV_ATTR_TEMP_RAW(ade7758_read_8bit); +static IIO_CONST_ATTR(in_temp_offset, "129 C"); +static IIO_CONST_ATTR(in_temp_scale, "4 C"); + +static IIO_DEV_ATTR_AWATTHR(ade7758_read_16bit, +		ADE7758_AWATTHR); +static IIO_DEV_ATTR_BWATTHR(ade7758_read_16bit, +		ADE7758_BWATTHR); +static IIO_DEV_ATTR_CWATTHR(ade7758_read_16bit, +		ADE7758_CWATTHR); +static IIO_DEV_ATTR_AVARHR(ade7758_read_16bit, +		ADE7758_AVARHR); +static IIO_DEV_ATTR_BVARHR(ade7758_read_16bit, +		ADE7758_BVARHR); +static IIO_DEV_ATTR_CVARHR(ade7758_read_16bit, +		ADE7758_CVARHR); +static IIO_DEV_ATTR_AVAHR(ade7758_read_16bit, +		ADE7758_AVAHR); +static IIO_DEV_ATTR_BVAHR(ade7758_read_16bit, +		ADE7758_BVAHR); +static IIO_DEV_ATTR_CVAHR(ade7758_read_16bit, +		ADE7758_CVAHR); + +static IIO_DEV_ATTR_SAMP_FREQ(S_IWUSR | S_IRUGO, +		ade7758_read_frequency, +		ade7758_write_frequency); + +static IIO_CONST_ATTR_SAMP_FREQ_AVAIL("26040 13020 6510 3255"); + +static struct attribute *ade7758_attributes[] = { +	&iio_dev_attr_in_temp_raw.dev_attr.attr, +	&iio_const_attr_in_temp_offset.dev_attr.attr, +	&iio_const_attr_in_temp_scale.dev_attr.attr, +	&iio_dev_attr_sampling_frequency.dev_attr.attr, +	&iio_const_attr_sampling_frequency_available.dev_attr.attr, +	&iio_dev_attr_awatthr.dev_attr.attr, +	&iio_dev_attr_bwatthr.dev_attr.attr, +	&iio_dev_attr_cwatthr.dev_attr.attr, +	&iio_dev_attr_avarhr.dev_attr.attr, +	&iio_dev_attr_bvarhr.dev_attr.attr, +	&iio_dev_attr_cvarhr.dev_attr.attr, +	&iio_dev_attr_avahr.dev_attr.attr, +	&iio_dev_attr_bvahr.dev_attr.attr, +	&iio_dev_attr_cvahr.dev_attr.attr, +	&iio_dev_attr_vpeak.dev_attr.attr, +	&iio_dev_attr_ipeak.dev_attr.attr, +	&iio_dev_attr_aphcal.dev_attr.attr, +	&iio_dev_attr_bphcal.dev_attr.attr, +	&iio_dev_attr_cphcal.dev_attr.attr, +	&iio_dev_attr_wdiv.dev_attr.attr, +	&iio_dev_attr_vadiv.dev_attr.attr, +	&iio_dev_attr_airms.dev_attr.attr, +	&iio_dev_attr_birms.dev_attr.attr, +	&iio_dev_attr_cirms.dev_attr.attr, +	&iio_dev_attr_avrms.dev_attr.attr, +	&iio_dev_attr_bvrms.dev_attr.attr, +	&iio_dev_attr_cvrms.dev_attr.attr, +	&iio_dev_attr_aigain.dev_attr.attr, +	&iio_dev_attr_bigain.dev_attr.attr, +	&iio_dev_attr_cigain.dev_attr.attr, +	&iio_dev_attr_avrmsgain.dev_attr.attr, +	&iio_dev_attr_bvrmsgain.dev_attr.attr, +	&iio_dev_attr_cvrmsgain.dev_attr.attr, +	&iio_dev_attr_airmsos.dev_attr.attr, +	&iio_dev_attr_birmsos.dev_attr.attr, +	&iio_dev_attr_cirmsos.dev_attr.attr, +	&iio_dev_attr_avrmsos.dev_attr.attr, +	&iio_dev_attr_bvrmsos.dev_attr.attr, +	&iio_dev_attr_cvrmsos.dev_attr.attr, +	NULL, +}; + +static const struct attribute_group ade7758_attribute_group = { +	.attrs = ade7758_attributes, +}; + +static const struct iio_chan_spec ade7758_channels[] = { +	{ +		.type = IIO_VOLTAGE, +		.indexed = 1, +		.channel = 0, +		.extend_name = "raw", +		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), +		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), +		.address = AD7758_WT(AD7758_PHASE_A, AD7758_VOLTAGE), +		.scan_index = 0, +		.scan_type = { +			.sign = 's', +			.realbits = 24, +			.storagebits = 32, +		}, +	}, { +		.type = IIO_CURRENT, +		.indexed = 1, +		.channel = 0, +		.extend_name = "raw", +		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), +		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), +		.address = AD7758_WT(AD7758_PHASE_A, AD7758_CURRENT), +		.scan_index = 1, +		.scan_type = { +			.sign = 's', +			.realbits = 24, +			.storagebits = 32, +		}, +	}, { +		.type = IIO_POWER, +		.indexed = 1, +		.channel = 0, +		.extend_name = "apparent_raw", +		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), +		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), +		.address = AD7758_WT(AD7758_PHASE_A, AD7758_APP_PWR), +		.scan_index = 2, +		.scan_type = { +			.sign = 's', +			.realbits = 24, +			.storagebits = 32, +		}, +	}, { +		.type = IIO_POWER, +		.indexed = 1, +		.channel = 0, +		.extend_name = "active_raw", +		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), +		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), +		.address = AD7758_WT(AD7758_PHASE_A, AD7758_ACT_PWR), +		.scan_index = 3, +		.scan_type = { +			.sign = 's', +			.realbits = 24, +			.storagebits = 32, +		}, +	}, { +		.type = IIO_POWER, +		.indexed = 1, +		.channel = 0, +		.extend_name = "reactive_raw", +		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), +		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), +		.address = AD7758_WT(AD7758_PHASE_A, AD7758_REACT_PWR), +		.scan_index = 4, +		.scan_type = { +			.sign = 's', +			.realbits = 24, +			.storagebits = 32, +		}, +	}, { +		.type = IIO_VOLTAGE, +		.indexed = 1, +		.channel = 1, +		.extend_name = "raw", +		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), +		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), +		.address = AD7758_WT(AD7758_PHASE_B, AD7758_VOLTAGE), +		.scan_index = 5, +		.scan_type = { +			.sign = 's', +			.realbits = 24, +			.storagebits = 32, +		}, +	}, { +		.type = IIO_CURRENT, +		.indexed = 1, +		.channel = 1, +		.extend_name = "raw", +		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), +		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), +		.address = AD7758_WT(AD7758_PHASE_B, AD7758_CURRENT), +		.scan_index = 6, +		.scan_type = { +			.sign = 's', +			.realbits = 24, +			.storagebits = 32, +		}, +	}, { +		.type = IIO_POWER, +		.indexed = 1, +		.channel = 1, +		.extend_name = "apparent_raw", +		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), +		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), +		.address = AD7758_WT(AD7758_PHASE_B, AD7758_APP_PWR), +		.scan_index = 7, +		.scan_type = { +			.sign = 's', +			.realbits = 24, +			.storagebits = 32, +		}, +	}, { +		.type = IIO_POWER, +		.indexed = 1, +		.channel = 1, +		.extend_name = "active_raw", +		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), +		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), +		.address = AD7758_WT(AD7758_PHASE_B, AD7758_ACT_PWR), +		.scan_index = 8, +		.scan_type = { +			.sign = 's', +			.realbits = 24, +			.storagebits = 32, +		}, +	}, { +		.type = IIO_POWER, +		.indexed = 1, +		.channel = 1, +		.extend_name = "reactive_raw", +		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), +		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), +		.address = AD7758_WT(AD7758_PHASE_B, AD7758_REACT_PWR), +		.scan_index = 9, +		.scan_type = { +			.sign = 's', +			.realbits = 24, +			.storagebits = 32, +		}, +	}, { +		.type = IIO_VOLTAGE, +		.indexed = 1, +		.channel = 2, +		.extend_name = "raw", +		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), +		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), +		.address = AD7758_WT(AD7758_PHASE_C, AD7758_VOLTAGE), +		.scan_index = 10, +		.scan_type = { +			.sign = 's', +			.realbits = 24, +			.storagebits = 32, +		}, +	}, { +		.type = IIO_CURRENT, +		.indexed = 1, +		.channel = 2, +		.extend_name = "raw", +		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), +		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), +		.address = AD7758_WT(AD7758_PHASE_C, AD7758_CURRENT), +		.scan_index = 11, +		.scan_type = { +			.sign = 's', +			.realbits = 24, +			.storagebits = 32, +		}, +	}, { +		.type = IIO_POWER, +		.indexed = 1, +		.channel = 2, +		.extend_name = "apparent_raw", +		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), +		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), +		.address = AD7758_WT(AD7758_PHASE_C, AD7758_APP_PWR), +		.scan_index = 12, +		.scan_type = { +			.sign = 's', +			.realbits = 24, +			.storagebits = 32, +		}, +	}, { +		.type = IIO_POWER, +		.indexed = 1, +		.channel = 2, +		.extend_name = "active_raw", +		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), +		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), +		.address = AD7758_WT(AD7758_PHASE_C, AD7758_ACT_PWR), +		.scan_index = 13, +		.scan_type = { +			.sign = 's', +			.realbits = 24, +			.storagebits = 32, +		}, +	}, { +		.type = IIO_POWER, +		.indexed = 1, +		.channel = 2, +		.extend_name = "reactive_raw", +		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), +		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), +		.address = AD7758_WT(AD7758_PHASE_C, AD7758_REACT_PWR), +		.scan_index = 14, +		.scan_type = { +			.sign = 's', +			.realbits = 24, +			.storagebits = 32, +		}, +	}, +	IIO_CHAN_SOFT_TIMESTAMP(15), +}; + +static const struct iio_info ade7758_info = { +	.attrs = &ade7758_attribute_group, +	.driver_module = THIS_MODULE, +}; + +static int ade7758_probe(struct spi_device *spi) +{ +	int ret; +	struct ade7758_state *st; +	struct iio_dev *indio_dev; + +	indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); +	if (!indio_dev) +		return -ENOMEM; + +	st = iio_priv(indio_dev); +	/* this is only used for removal purposes */ +	spi_set_drvdata(spi, indio_dev); + +	/* Allocate the comms buffers */ +	st->rx = kcalloc(ADE7758_MAX_RX, sizeof(*st->rx), GFP_KERNEL); +	if (!st->rx) +		return -ENOMEM; +	st->tx = kcalloc(ADE7758_MAX_TX, sizeof(*st->tx), GFP_KERNEL); +	if (st->tx == NULL) { +		ret = -ENOMEM; +		goto error_free_rx; +	} +	st->us = spi; +	st->ade7758_ring_channels = &ade7758_channels[0]; +	mutex_init(&st->buf_lock); + +	indio_dev->name = spi->dev.driver->name; +	indio_dev->dev.parent = &spi->dev; +	indio_dev->info = &ade7758_info; +	indio_dev->modes = INDIO_DIRECT_MODE; + +	ret = ade7758_configure_ring(indio_dev); +	if (ret) +		goto error_free_tx; + +	ret = iio_buffer_register(indio_dev, +				  &ade7758_channels[0], +				  ARRAY_SIZE(ade7758_channels)); +	if (ret) { +		dev_err(&spi->dev, "failed to initialize the ring\n"); +		goto error_unreg_ring_funcs; +	} + +	/* Get the device into a sane initial state */ +	ret = ade7758_initial_setup(indio_dev); +	if (ret) +		goto error_uninitialize_ring; + +	if (spi->irq) { +		ret = ade7758_probe_trigger(indio_dev); +		if (ret) +			goto error_uninitialize_ring; +	} + +	ret = iio_device_register(indio_dev); +	if (ret) +		goto error_remove_trigger; + +	return 0; + +error_remove_trigger: +	if (spi->irq) +		ade7758_remove_trigger(indio_dev); +error_uninitialize_ring: +	ade7758_uninitialize_ring(indio_dev); +error_unreg_ring_funcs: +	ade7758_unconfigure_ring(indio_dev); +error_free_tx: +	kfree(st->tx); +error_free_rx: +	kfree(st->rx); +	return ret; +} + +static int ade7758_remove(struct spi_device *spi) +{ +	struct iio_dev *indio_dev = spi_get_drvdata(spi); +	struct ade7758_state *st = iio_priv(indio_dev); + +	iio_device_unregister(indio_dev); +	ade7758_stop_device(&indio_dev->dev); +	ade7758_remove_trigger(indio_dev); +	ade7758_uninitialize_ring(indio_dev); +	ade7758_unconfigure_ring(indio_dev); +	kfree(st->tx); +	kfree(st->rx); + +	return 0; +} + +static const struct spi_device_id ade7758_id[] = { +	{"ade7758", 0}, +	{} +}; +MODULE_DEVICE_TABLE(spi, ade7758_id); + +static struct spi_driver ade7758_driver = { +	.driver = { +		.name = "ade7758", +		.owner = THIS_MODULE, +	}, +	.probe = ade7758_probe, +	.remove = ade7758_remove, +	.id_table = ade7758_id, +}; +module_spi_driver(ade7758_driver); + +MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>"); +MODULE_DESCRIPTION("Analog Devices ADE7758 Polyphase Multifunction Energy Metering IC Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/iio/meter/ade7758_ring.c b/drivers/staging/iio/meter/ade7758_ring.c new file mode 100644 index 00000000000..c0accf8cce9 --- /dev/null +++ b/drivers/staging/iio/meter/ade7758_ring.c @@ -0,0 +1,188 @@ +/* + * ADE7758 Poly Phase Multifunction Energy Metering IC driver + * + * Copyright 2010-2011 Analog Devices Inc. + * + * Licensed under the GPL-2. + */ +#include <linux/export.h> +#include <linux/interrupt.h> +#include <linux/kernel.h> +#include <linux/spi/spi.h> +#include <linux/slab.h> +#include <asm/unaligned.h> + +#include <linux/iio/iio.h> +#include <linux/iio/kfifo_buf.h> +#include <linux/iio/trigger_consumer.h> +#include "ade7758.h" + +/** + * ade7758_spi_read_burst() - read data registers + * @indio_dev: the IIO device + **/ +static int ade7758_spi_read_burst(struct iio_dev *indio_dev) +{ +	struct ade7758_state *st = iio_priv(indio_dev); +	int ret; + +	ret = spi_sync(st->us, &st->ring_msg); +	if (ret) +		dev_err(&st->us->dev, "problem when reading WFORM value\n"); + +	return ret; +} + +static int ade7758_write_waveform_type(struct device *dev, unsigned type) +{ +	int ret; +	u8 reg; + +	ret = ade7758_spi_read_reg_8(dev, +			ADE7758_WAVMODE, +			®); +	if (ret) +		goto out; + +	reg &= ~0x1F; +	reg |= type & 0x1F; + +	ret = ade7758_spi_write_reg_8(dev, +			ADE7758_WAVMODE, +			reg); +out: +	return ret; +} + +/* Whilst this makes a lot of calls to iio_sw_ring functions - it is too device + * specific to be rolled into the core. + */ +static irqreturn_t ade7758_trigger_handler(int irq, void *p) +{ +	struct iio_poll_func *pf = p; +	struct iio_dev *indio_dev = pf->indio_dev; +	struct ade7758_state *st = iio_priv(indio_dev); +	s64 dat64[2]; +	u32 *dat32 = (u32 *)dat64; + +	if (!bitmap_empty(indio_dev->active_scan_mask, indio_dev->masklength)) +		if (ade7758_spi_read_burst(indio_dev) >= 0) +			*dat32 = get_unaligned_be32(&st->rx_buf[5]) & 0xFFFFFF; + +	iio_push_to_buffers_with_timestamp(indio_dev, dat64, pf->timestamp); + +	iio_trigger_notify_done(indio_dev->trig); + +	return IRQ_HANDLED; +} + +/** + * ade7758_ring_preenable() setup the parameters of the ring before enabling + * + * The complex nature of the setting of the number of bytes per datum is due + * to this driver currently ensuring that the timestamp is stored at an 8 + * byte boundary. + **/ +static int ade7758_ring_preenable(struct iio_dev *indio_dev) +{ +	struct ade7758_state *st = iio_priv(indio_dev); +	unsigned channel; + +	if (!bitmap_empty(indio_dev->active_scan_mask, indio_dev->masklength)) +		return -EINVAL; + +	channel = find_first_bit(indio_dev->active_scan_mask, +				 indio_dev->masklength); + +	ade7758_write_waveform_type(&indio_dev->dev, +		st->ade7758_ring_channels[channel].address); + +	return 0; +} + +static const struct iio_buffer_setup_ops ade7758_ring_setup_ops = { +	.preenable = &ade7758_ring_preenable, +	.postenable = &iio_triggered_buffer_postenable, +	.predisable = &iio_triggered_buffer_predisable, +	.validate_scan_mask = &iio_validate_scan_mask_onehot, +}; + +void ade7758_unconfigure_ring(struct iio_dev *indio_dev) +{ +	iio_dealloc_pollfunc(indio_dev->pollfunc); +	iio_kfifo_free(indio_dev->buffer); +} + +int ade7758_configure_ring(struct iio_dev *indio_dev) +{ +	struct ade7758_state *st = iio_priv(indio_dev); +	struct iio_buffer *buffer; +	int ret = 0; + +	buffer = iio_kfifo_allocate(indio_dev); +	if (!buffer) { +		ret = -ENOMEM; +		return ret; +	} + +	iio_device_attach_buffer(indio_dev, buffer); + +	indio_dev->setup_ops = &ade7758_ring_setup_ops; + +	indio_dev->pollfunc = iio_alloc_pollfunc(&iio_pollfunc_store_time, +						 &ade7758_trigger_handler, +						 0, +						 indio_dev, +						 "ade7759_consumer%d", +						 indio_dev->id); +	if (indio_dev->pollfunc == NULL) { +		ret = -ENOMEM; +		goto error_iio_kfifo_free; +	} + +	indio_dev->modes |= INDIO_BUFFER_TRIGGERED; + +	st->tx_buf[0] = ADE7758_READ_REG(ADE7758_RSTATUS); +	st->tx_buf[1] = 0; +	st->tx_buf[2] = 0; +	st->tx_buf[3] = 0; +	st->tx_buf[4] = ADE7758_READ_REG(ADE7758_WFORM); +	st->tx_buf[5] = 0; +	st->tx_buf[6] = 0; +	st->tx_buf[7] = 0; + +	/* build spi ring message */ +	st->ring_xfer[0].tx_buf = &st->tx_buf[0]; +	st->ring_xfer[0].len = 1; +	st->ring_xfer[0].bits_per_word = 8; +	st->ring_xfer[0].delay_usecs = 4; +	st->ring_xfer[1].rx_buf = &st->rx_buf[1]; +	st->ring_xfer[1].len = 3; +	st->ring_xfer[1].bits_per_word = 8; +	st->ring_xfer[1].cs_change = 1; + +	st->ring_xfer[2].tx_buf = &st->tx_buf[4]; +	st->ring_xfer[2].len = 1; +	st->ring_xfer[2].bits_per_word = 8; +	st->ring_xfer[2].delay_usecs = 1; +	st->ring_xfer[3].rx_buf = &st->rx_buf[5]; +	st->ring_xfer[3].len = 3; +	st->ring_xfer[3].bits_per_word = 8; + +	spi_message_init(&st->ring_msg); +	spi_message_add_tail(&st->ring_xfer[0], &st->ring_msg); +	spi_message_add_tail(&st->ring_xfer[1], &st->ring_msg); +	spi_message_add_tail(&st->ring_xfer[2], &st->ring_msg); +	spi_message_add_tail(&st->ring_xfer[3], &st->ring_msg); + +	return 0; + +error_iio_kfifo_free: +	iio_kfifo_free(indio_dev->buffer); +	return ret; +} + +void ade7758_uninitialize_ring(struct iio_dev *indio_dev) +{ +	iio_buffer_unregister(indio_dev); +} diff --git a/drivers/staging/iio/meter/ade7758_trigger.c b/drivers/staging/iio/meter/ade7758_trigger.c new file mode 100644 index 00000000000..7a94ddd42f5 --- /dev/null +++ b/drivers/staging/iio/meter/ade7758_trigger.c @@ -0,0 +1,109 @@ +/* + * ADE7758 Poly Phase Multifunction Energy Metering IC driver + * + * Copyright 2010-2011 Analog Devices Inc. + * + * Licensed under the GPL-2. + */ + +#include <linux/interrupt.h> +#include <linux/kernel.h> +#include <linux/spi/spi.h> +#include <linux/export.h> + +#include <linux/iio/iio.h> +#include <linux/iio/trigger.h> +#include "ade7758.h" + +/** + * ade7758_data_rdy_trig_poll() the event handler for the data rdy trig + **/ +static irqreturn_t ade7758_data_rdy_trig_poll(int irq, void *private) +{ +	disable_irq_nosync(irq); +	iio_trigger_poll(private, iio_get_time_ns()); + +	return IRQ_HANDLED; +} + +/** + * ade7758_data_rdy_trigger_set_state() set datardy interrupt state + **/ +static int ade7758_data_rdy_trigger_set_state(struct iio_trigger *trig, +						bool state) +{ +	struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig); + +	dev_dbg(&indio_dev->dev, "%s (%d)\n", __func__, state); +	return ade7758_set_irq(&indio_dev->dev, state); +} + +/** + * ade7758_trig_try_reen() try renabling irq for data rdy trigger + * @trig:	the datardy trigger + **/ +static int ade7758_trig_try_reen(struct iio_trigger *trig) +{ +	struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig); +	struct ade7758_state *st = iio_priv(indio_dev); + +	enable_irq(st->us->irq); +	/* irq reenabled so success! */ +	return 0; +} + +static const struct iio_trigger_ops ade7758_trigger_ops = { +	.owner = THIS_MODULE, +	.set_trigger_state = &ade7758_data_rdy_trigger_set_state, +	.try_reenable = &ade7758_trig_try_reen, +}; + +int ade7758_probe_trigger(struct iio_dev *indio_dev) +{ +	struct ade7758_state *st = iio_priv(indio_dev); +	int ret; + +	st->trig = iio_trigger_alloc("%s-dev%d", +					spi_get_device_id(st->us)->name, +					indio_dev->id); +	if (st->trig == NULL) { +		ret = -ENOMEM; +		goto error_ret; +	} + +	ret = request_irq(st->us->irq, +			  ade7758_data_rdy_trig_poll, +			  IRQF_TRIGGER_LOW, +			  spi_get_device_id(st->us)->name, +			  st->trig); +	if (ret) +		goto error_free_trig; + +	st->trig->dev.parent = &st->us->dev; +	st->trig->ops = &ade7758_trigger_ops; +	iio_trigger_set_drvdata(st->trig, indio_dev); +	ret = iio_trigger_register(st->trig); + +	/* select default trigger */ +	indio_dev->trig = st->trig; +	if (ret) +		goto error_free_irq; + +	return 0; + +error_free_irq: +	free_irq(st->us->irq, st->trig); +error_free_trig: +	iio_trigger_free(st->trig); +error_ret: +	return ret; +} + +void ade7758_remove_trigger(struct iio_dev *indio_dev) +{ +	struct ade7758_state *st = iio_priv(indio_dev); + +	iio_trigger_unregister(st->trig); +	free_irq(st->us->irq, st->trig); +	iio_trigger_free(st->trig); +} diff --git a/drivers/staging/iio/meter/ade7759.c b/drivers/staging/iio/meter/ade7759.c new file mode 100644 index 00000000000..ea0c9debf8b --- /dev/null +++ b/drivers/staging/iio/meter/ade7759.c @@ -0,0 +1,496 @@ +/* + * ADE7759 Active Energy Metering IC with di/dt Sensor Interface Driver + * + * Copyright 2010 Analog Devices Inc. + * + * Licensed under the GPL-2 or later. + */ + +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/delay.h> +#include <linux/mutex.h> +#include <linux/device.h> +#include <linux/kernel.h> +#include <linux/spi/spi.h> +#include <linux/slab.h> +#include <linux/sysfs.h> +#include <linux/list.h> +#include <linux/module.h> + +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> +#include "meter.h" +#include "ade7759.h" + +static int ade7759_spi_write_reg_8(struct device *dev, +		u8 reg_address, +		u8 val) +{ +	int ret; +	struct iio_dev *indio_dev = dev_to_iio_dev(dev); +	struct ade7759_state *st = iio_priv(indio_dev); + +	mutex_lock(&st->buf_lock); +	st->tx[0] = ADE7759_WRITE_REG(reg_address); +	st->tx[1] = val; + +	ret = spi_write(st->us, st->tx, 2); +	mutex_unlock(&st->buf_lock); + +	return ret; +} + +static int ade7759_spi_write_reg_16(struct device *dev, +		u8 reg_address, +		u16 value) +{ +	int ret; +	struct iio_dev *indio_dev = dev_to_iio_dev(dev); +	struct ade7759_state *st = iio_priv(indio_dev); + +	mutex_lock(&st->buf_lock); +	st->tx[0] = ADE7759_WRITE_REG(reg_address); +	st->tx[1] = (value >> 8) & 0xFF; +	st->tx[2] = value & 0xFF; +	ret = spi_write(st->us, st->tx, 3); +	mutex_unlock(&st->buf_lock); + +	return ret; +} + +static int ade7759_spi_read_reg_8(struct device *dev, +		u8 reg_address, +		u8 *val) +{ +	struct iio_dev *indio_dev = dev_to_iio_dev(dev); +	struct ade7759_state *st = iio_priv(indio_dev); +	int ret; + +	ret = spi_w8r8(st->us, ADE7759_READ_REG(reg_address)); +	if (ret < 0) { +		dev_err(&st->us->dev, "problem when reading 8 bit register 0x%02X", +				reg_address); +		return ret; +	} +	*val = ret; + +	return 0; +} + +static int ade7759_spi_read_reg_16(struct device *dev, +		u8 reg_address, +		u16 *val) +{ +	struct iio_dev *indio_dev = dev_to_iio_dev(dev); +	struct ade7759_state *st = iio_priv(indio_dev); +	int ret; + +	ret = spi_w8r16be(st->us, ADE7759_READ_REG(reg_address)); +	if (ret < 0) { +		dev_err(&st->us->dev, "problem when reading 16 bit register 0x%02X", +			reg_address); +		return ret; +	} + +	*val = ret; + +	return 0; +} + +static int ade7759_spi_read_reg_40(struct device *dev, +		u8 reg_address, +		u64 *val) +{ +	struct iio_dev *indio_dev = dev_to_iio_dev(dev); +	struct ade7759_state *st = iio_priv(indio_dev); +	int ret; +	struct spi_transfer xfers[] = { +		{ +			.tx_buf = st->tx, +			.rx_buf = st->rx, +			.bits_per_word = 8, +			.len = 6, +		}, +	}; + +	mutex_lock(&st->buf_lock); +	st->tx[0] = ADE7759_READ_REG(reg_address); +	memset(&st->tx[1], 0 , 5); + +	ret = spi_sync_transfer(st->us, xfers, ARRAY_SIZE(xfers)); +	if (ret) { +		dev_err(&st->us->dev, "problem when reading 40 bit register 0x%02X", +				reg_address); +		goto error_ret; +	} +	*val = ((u64)st->rx[1] << 32) | (st->rx[2] << 24) | +		(st->rx[3] << 16) | (st->rx[4] << 8) | st->rx[5]; + +error_ret: +	mutex_unlock(&st->buf_lock); +	return ret; +} + +static ssize_t ade7759_read_8bit(struct device *dev, +		struct device_attribute *attr, +		char *buf) +{ +	int ret; +	u8 val = 0; +	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); + +	ret = ade7759_spi_read_reg_8(dev, this_attr->address, &val); +	if (ret) +		return ret; + +	return sprintf(buf, "%u\n", val); +} + +static ssize_t ade7759_read_16bit(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 = ade7759_spi_read_reg_16(dev, this_attr->address, &val); +	if (ret) +		return ret; + +	return sprintf(buf, "%u\n", val); +} + +static ssize_t ade7759_read_40bit(struct device *dev, +		struct device_attribute *attr, +		char *buf) +{ +	int ret; +	u64 val = 0; +	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); + +	ret = ade7759_spi_read_reg_40(dev, this_attr->address, &val); +	if (ret) +		return ret; + +	return sprintf(buf, "%llu\n", val); +} + +static ssize_t ade7759_write_8bit(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; +	u8 val; + +	ret = kstrtou8(buf, 10, &val); +	if (ret) +		goto error_ret; +	ret = ade7759_spi_write_reg_8(dev, this_attr->address, val); + +error_ret: +	return ret ? ret : len; +} + +static ssize_t ade7759_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; +	u16 val; + +	ret = kstrtou16(buf, 10, &val); +	if (ret) +		goto error_ret; +	ret = ade7759_spi_write_reg_16(dev, this_attr->address, val); + +error_ret: +	return ret ? ret : len; +} + +static int ade7759_reset(struct device *dev) +{ +	int ret; +	u16 val; +	ade7759_spi_read_reg_16(dev, +			ADE7759_MODE, +			&val); +	val |= 1 << 6; /* Software Chip Reset */ +	ret = ade7759_spi_write_reg_16(dev, +			ADE7759_MODE, +			val); + +	return ret; +} + +static IIO_DEV_ATTR_AENERGY(ade7759_read_40bit, ADE7759_AENERGY); +static IIO_DEV_ATTR_CFDEN(S_IWUSR | S_IRUGO, +		ade7759_read_16bit, +		ade7759_write_16bit, +		ADE7759_CFDEN); +static IIO_DEV_ATTR_CFNUM(S_IWUSR | S_IRUGO, +		ade7759_read_8bit, +		ade7759_write_8bit, +		ADE7759_CFNUM); +static IIO_DEV_ATTR_CHKSUM(ade7759_read_8bit, ADE7759_CHKSUM); +static IIO_DEV_ATTR_PHCAL(S_IWUSR | S_IRUGO, +		ade7759_read_16bit, +		ade7759_write_16bit, +		ADE7759_PHCAL); +static IIO_DEV_ATTR_APOS(S_IWUSR | S_IRUGO, +		ade7759_read_16bit, +		ade7759_write_16bit, +		ADE7759_APOS); +static IIO_DEV_ATTR_SAGCYC(S_IWUSR | S_IRUGO, +		ade7759_read_8bit, +		ade7759_write_8bit, +		ADE7759_SAGCYC); +static IIO_DEV_ATTR_SAGLVL(S_IWUSR | S_IRUGO, +		ade7759_read_8bit, +		ade7759_write_8bit, +		ADE7759_SAGLVL); +static IIO_DEV_ATTR_LINECYC(S_IWUSR | S_IRUGO, +		ade7759_read_8bit, +		ade7759_write_8bit, +		ADE7759_LINECYC); +static IIO_DEV_ATTR_LENERGY(ade7759_read_40bit, ADE7759_LENERGY); +static IIO_DEV_ATTR_PGA_GAIN(S_IWUSR | S_IRUGO, +		ade7759_read_8bit, +		ade7759_write_8bit, +		ADE7759_GAIN); +static IIO_DEV_ATTR_ACTIVE_POWER_GAIN(S_IWUSR | S_IRUGO, +		ade7759_read_16bit, +		ade7759_write_16bit, +		ADE7759_APGAIN); +static IIO_DEV_ATTR_CH_OFF(1, S_IWUSR | S_IRUGO, +		ade7759_read_8bit, +		ade7759_write_8bit, +		ADE7759_CH1OS); +static IIO_DEV_ATTR_CH_OFF(2, S_IWUSR | S_IRUGO, +		ade7759_read_8bit, +		ade7759_write_8bit, +		ADE7759_CH2OS); + +static int ade7759_set_irq(struct device *dev, bool enable) +{ +	int ret; +	u8 irqen; +	ret = ade7759_spi_read_reg_8(dev, ADE7759_IRQEN, &irqen); +	if (ret) +		goto error_ret; + +	if (enable) +		irqen |= 1 << 3; /* Enables an interrupt when a data is +				    present in the waveform register */ +	else +		irqen &= ~(1 << 3); + +	ret = ade7759_spi_write_reg_8(dev, ADE7759_IRQEN, irqen); + +error_ret: +	return ret; +} + +/* Power down the device */ +static int ade7759_stop_device(struct device *dev) +{ +	u16 val; + +	ade7759_spi_read_reg_16(dev, +			ADE7759_MODE, +			&val); +	val |= 1 << 4;  /* AD converters can be turned off */ + +	return ade7759_spi_write_reg_16(dev, ADE7759_MODE, val); +} + +static int ade7759_initial_setup(struct iio_dev *indio_dev) +{ +	int ret; +	struct ade7759_state *st = iio_priv(indio_dev); +	struct device *dev = &indio_dev->dev; + +	/* use low spi speed for init */ +	st->us->mode = SPI_MODE_3; +	spi_setup(st->us); + +	/* Disable IRQ */ +	ret = ade7759_set_irq(dev, false); +	if (ret) { +		dev_err(dev, "disable irq failed"); +		goto err_ret; +	} + +	ade7759_reset(dev); +	msleep(ADE7759_STARTUP_DELAY); + +err_ret: +	return ret; +} + +static ssize_t ade7759_read_frequency(struct device *dev, +		struct device_attribute *attr, +		char *buf) +{ +	int ret; +	u16 t; +	int sps; +	ret = ade7759_spi_read_reg_16(dev, +			ADE7759_MODE, +			&t); +	if (ret) +		return ret; + +	t = (t >> 3) & 0x3; +	sps = 27900 / (1 + t); + +	return sprintf(buf, "%d\n", sps); +} + +static ssize_t ade7759_write_frequency(struct device *dev, +		struct device_attribute *attr, +		const char *buf, +		size_t len) +{ +	struct iio_dev *indio_dev = dev_to_iio_dev(dev); +	struct ade7759_state *st = iio_priv(indio_dev); +	u16 val; +	int ret; +	u16 reg, t; + +	ret = kstrtou16(buf, 10, &val); +	if (ret) +		return ret; +	if (val == 0) +		return -EINVAL; + +	mutex_lock(&indio_dev->mlock); + +	t = (27900 / val); +	if (t > 0) +		t--; + +	if (t > 1) +		st->us->max_speed_hz = ADE7759_SPI_SLOW; +	else +		st->us->max_speed_hz = ADE7759_SPI_FAST; + +	ret = ade7759_spi_read_reg_16(dev, ADE7759_MODE, ®); +	if (ret) +		goto out; + +	reg &= ~(3 << 13); +	reg |= t << 13; + +	ret = ade7759_spi_write_reg_16(dev, ADE7759_MODE, reg); + +out: +	mutex_unlock(&indio_dev->mlock); + +	return ret ? ret : len; +} +static IIO_DEV_ATTR_TEMP_RAW(ade7759_read_8bit); +static IIO_CONST_ATTR(in_temp_offset, "70 C"); +static IIO_CONST_ATTR(in_temp_scale, "1 C"); + +static IIO_DEV_ATTR_SAMP_FREQ(S_IWUSR | S_IRUGO, +		ade7759_read_frequency, +		ade7759_write_frequency); + +static IIO_CONST_ATTR_SAMP_FREQ_AVAIL("27900 14000 7000 3500"); + +static struct attribute *ade7759_attributes[] = { +	&iio_dev_attr_in_temp_raw.dev_attr.attr, +	&iio_const_attr_in_temp_offset.dev_attr.attr, +	&iio_const_attr_in_temp_scale.dev_attr.attr, +	&iio_dev_attr_sampling_frequency.dev_attr.attr, +	&iio_const_attr_sampling_frequency_available.dev_attr.attr, +	&iio_dev_attr_phcal.dev_attr.attr, +	&iio_dev_attr_cfden.dev_attr.attr, +	&iio_dev_attr_aenergy.dev_attr.attr, +	&iio_dev_attr_cfnum.dev_attr.attr, +	&iio_dev_attr_apos.dev_attr.attr, +	&iio_dev_attr_sagcyc.dev_attr.attr, +	&iio_dev_attr_saglvl.dev_attr.attr, +	&iio_dev_attr_linecyc.dev_attr.attr, +	&iio_dev_attr_lenergy.dev_attr.attr, +	&iio_dev_attr_chksum.dev_attr.attr, +	&iio_dev_attr_pga_gain.dev_attr.attr, +	&iio_dev_attr_active_power_gain.dev_attr.attr, +	&iio_dev_attr_choff_1.dev_attr.attr, +	&iio_dev_attr_choff_2.dev_attr.attr, +	NULL, +}; + +static const struct attribute_group ade7759_attribute_group = { +	.attrs = ade7759_attributes, +}; + +static const struct iio_info ade7759_info = { +	.attrs = &ade7759_attribute_group, +	.driver_module = THIS_MODULE, +}; + +static int ade7759_probe(struct spi_device *spi) +{ +	int ret; +	struct ade7759_state *st; +	struct iio_dev *indio_dev; + +	/* setup the industrialio driver allocated elements */ +	indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); +	if (!indio_dev) +		return -ENOMEM; +	/* this is only used for removal purposes */ +	spi_set_drvdata(spi, indio_dev); + +	st = iio_priv(indio_dev); +	st->us = spi; +	mutex_init(&st->buf_lock); +	indio_dev->name = spi->dev.driver->name; +	indio_dev->dev.parent = &spi->dev; +	indio_dev->info = &ade7759_info; +	indio_dev->modes = INDIO_DIRECT_MODE; + +	/* Get the device into a sane initial state */ +	ret = ade7759_initial_setup(indio_dev); +	if (ret) +		return ret; + +	ret = iio_device_register(indio_dev); +	if (ret) +		return ret; + +	return 0; +} + +/* fixme, confirm ordering in this function */ +static int ade7759_remove(struct spi_device *spi) +{ +	struct iio_dev *indio_dev = spi_get_drvdata(spi); + +	iio_device_unregister(indio_dev); +	ade7759_stop_device(&indio_dev->dev); + +	return 0; +} + +static struct spi_driver ade7759_driver = { +	.driver = { +		.name = "ade7759", +		.owner = THIS_MODULE, +	}, +	.probe = ade7759_probe, +	.remove = ade7759_remove, +}; +module_spi_driver(ade7759_driver); + +MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>"); +MODULE_DESCRIPTION("Analog Devices ADE7759 Active Energy Metering IC Driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("spi:ad7759"); diff --git a/drivers/staging/iio/meter/ade7759.h b/drivers/staging/iio/meter/ade7759.h new file mode 100644 index 00000000000..f9ff1f8e737 --- /dev/null +++ b/drivers/staging/iio/meter/ade7759.h @@ -0,0 +1,53 @@ +#ifndef _ADE7759_H +#define _ADE7759_H + +#define ADE7759_WAVEFORM  0x01 +#define ADE7759_AENERGY   0x02 +#define ADE7759_RSTENERGY 0x03 +#define ADE7759_STATUS    0x04 +#define ADE7759_RSTSTATUS 0x05 +#define ADE7759_MODE      0x06 +#define ADE7759_CFDEN     0x07 +#define ADE7759_CH1OS     0x08 +#define ADE7759_CH2OS     0x09 +#define ADE7759_GAIN      0x0A +#define ADE7759_APGAIN    0x0B +#define ADE7759_PHCAL     0x0C +#define ADE7759_APOS      0x0D +#define ADE7759_ZXTOUT    0x0E +#define ADE7759_SAGCYC    0x0F +#define ADE7759_IRQEN     0x10 +#define ADE7759_SAGLVL    0x11 +#define ADE7759_TEMP      0x12 +#define ADE7759_LINECYC   0x13 +#define ADE7759_LENERGY   0x14 +#define ADE7759_CFNUM     0x15 +#define ADE7759_CHKSUM    0x1E +#define ADE7759_DIEREV    0x1F + +#define ADE7759_READ_REG(a)    a +#define ADE7759_WRITE_REG(a) ((a) | 0x80) + +#define ADE7759_MAX_TX    6 +#define ADE7759_MAX_RX    6 +#define ADE7759_STARTUP_DELAY 1 + +#define ADE7759_SPI_SLOW	(u32)(300 * 1000) +#define ADE7759_SPI_BURST	(u32)(1000 * 1000) +#define ADE7759_SPI_FAST	(u32)(2000 * 1000) + +/** + * struct ade7759_state - device instance specific data + * @us:			actual spi_device + * @buf_lock:		mutex to protect tx and rx + * @tx:			transmit buffer + * @rx:			receive buffer + **/ +struct ade7759_state { +	struct spi_device	*us; +	struct mutex		buf_lock; +	u8			tx[ADE7759_MAX_TX] ____cacheline_aligned; +	u8			rx[ADE7759_MAX_RX]; +}; + +#endif diff --git a/drivers/staging/iio/meter/ade7854-i2c.c b/drivers/staging/iio/meter/ade7854-i2c.c new file mode 100644 index 00000000000..5b33c7f1aa9 --- /dev/null +++ b/drivers/staging/iio/meter/ade7854-i2c.c @@ -0,0 +1,258 @@ +/* + * ADE7854/58/68/78 Polyphase Multifunction Energy Metering IC Driver (I2C Bus) + * + * Copyright 2010 Analog Devices Inc. + * + * Licensed under the GPL-2 or later. + */ + +#include <linux/device.h> +#include <linux/kernel.h> +#include <linux/i2c.h> +#include <linux/slab.h> +#include <linux/module.h> + +#include <linux/iio/iio.h> +#include "ade7854.h" + +static int ade7854_i2c_write_reg_8(struct device *dev, +		u16 reg_address, +		u8 value) +{ +	int ret; +	struct iio_dev *indio_dev = dev_to_iio_dev(dev); +	struct ade7854_state *st = iio_priv(indio_dev); + +	mutex_lock(&st->buf_lock); +	st->tx[0] = (reg_address >> 8) & 0xFF; +	st->tx[1] = reg_address & 0xFF; +	st->tx[2] = value; + +	ret = i2c_master_send(st->i2c, st->tx, 3); +	mutex_unlock(&st->buf_lock); + +	return ret; +} + +static int ade7854_i2c_write_reg_16(struct device *dev, +		u16 reg_address, +		u16 value) +{ +	int ret; +	struct iio_dev *indio_dev = dev_to_iio_dev(dev); +	struct ade7854_state *st = iio_priv(indio_dev); + +	mutex_lock(&st->buf_lock); +	st->tx[0] = (reg_address >> 8) & 0xFF; +	st->tx[1] = reg_address & 0xFF; +	st->tx[2] = (value >> 8) & 0xFF; +	st->tx[3] = value & 0xFF; + +	ret = i2c_master_send(st->i2c, st->tx, 4); +	mutex_unlock(&st->buf_lock); + +	return ret; +} + +static int ade7854_i2c_write_reg_24(struct device *dev, +		u16 reg_address, +		u32 value) +{ +	int ret; +	struct iio_dev *indio_dev = dev_to_iio_dev(dev); +	struct ade7854_state *st = iio_priv(indio_dev); + +	mutex_lock(&st->buf_lock); +	st->tx[0] = (reg_address >> 8) & 0xFF; +	st->tx[1] = reg_address & 0xFF; +	st->tx[2] = (value >> 16) & 0xFF; +	st->tx[3] = (value >> 8) & 0xFF; +	st->tx[4] = value & 0xFF; + +	ret = i2c_master_send(st->i2c, st->tx, 5); +	mutex_unlock(&st->buf_lock); + +	return ret; +} + +static int ade7854_i2c_write_reg_32(struct device *dev, +		u16 reg_address, +		u32 value) +{ +	int ret; +	struct iio_dev *indio_dev = dev_to_iio_dev(dev); +	struct ade7854_state *st = iio_priv(indio_dev); + +	mutex_lock(&st->buf_lock); +	st->tx[0] = (reg_address >> 8) & 0xFF; +	st->tx[1] = reg_address & 0xFF; +	st->tx[2] = (value >> 24) & 0xFF; +	st->tx[3] = (value >> 16) & 0xFF; +	st->tx[4] = (value >> 8) & 0xFF; +	st->tx[5] = value & 0xFF; + +	ret = i2c_master_send(st->i2c, st->tx, 6); +	mutex_unlock(&st->buf_lock); + +	return ret; +} + +static int ade7854_i2c_read_reg_8(struct device *dev, +		u16 reg_address, +		u8 *val) +{ +	struct iio_dev *indio_dev = dev_to_iio_dev(dev); +	struct ade7854_state *st = iio_priv(indio_dev); +	int ret; + +	mutex_lock(&st->buf_lock); +	st->tx[0] = (reg_address >> 8) & 0xFF; +	st->tx[1] = reg_address & 0xFF; + +	ret = i2c_master_send(st->i2c, st->tx, 2); +	if (ret) +		goto out; + +	ret = i2c_master_recv(st->i2c, st->rx, 1); +	if (ret) +		goto out; + +	*val = st->rx[0]; +out: +	mutex_unlock(&st->buf_lock); +	return ret; +} + +static int ade7854_i2c_read_reg_16(struct device *dev, +		u16 reg_address, +		u16 *val) +{ +	struct iio_dev *indio_dev = dev_to_iio_dev(dev); +	struct ade7854_state *st = iio_priv(indio_dev); +	int ret; + +	mutex_lock(&st->buf_lock); +	st->tx[0] = (reg_address >> 8) & 0xFF; +	st->tx[1] = reg_address & 0xFF; + +	ret = i2c_master_send(st->i2c, st->tx, 2); +	if (ret) +		goto out; + +	ret = i2c_master_recv(st->i2c, st->rx, 2); +	if (ret) +		goto out; + +	*val = (st->rx[0] << 8) | st->rx[1]; +out: +	mutex_unlock(&st->buf_lock); +	return ret; +} + +static int ade7854_i2c_read_reg_24(struct device *dev, +		u16 reg_address, +		u32 *val) +{ +	struct iio_dev *indio_dev = dev_to_iio_dev(dev); +	struct ade7854_state *st = iio_priv(indio_dev); +	int ret; + +	mutex_lock(&st->buf_lock); +	st->tx[0] = (reg_address >> 8) & 0xFF; +	st->tx[1] = reg_address & 0xFF; + +	ret = i2c_master_send(st->i2c, st->tx, 2); +	if (ret) +		goto out; + +	ret = i2c_master_recv(st->i2c, st->rx, 3); +	if (ret) +		goto out; + +	*val = (st->rx[0] << 16) | (st->rx[1] << 8) | st->rx[2]; +out: +	mutex_unlock(&st->buf_lock); +	return ret; +} + +static int ade7854_i2c_read_reg_32(struct device *dev, +		u16 reg_address, +		u32 *val) +{ +	struct iio_dev *indio_dev = dev_to_iio_dev(dev); +	struct ade7854_state *st = iio_priv(indio_dev); +	int ret; + +	mutex_lock(&st->buf_lock); +	st->tx[0] = (reg_address >> 8) & 0xFF; +	st->tx[1] = reg_address & 0xFF; + +	ret = i2c_master_send(st->i2c, st->tx, 2); +	if (ret) +		goto out; + +	ret = i2c_master_recv(st->i2c, st->rx, 3); +	if (ret) +		goto out; + +	*val = (st->rx[0] << 24) | (st->rx[1] << 16) | (st->rx[2] << 8) | st->rx[3]; +out: +	mutex_unlock(&st->buf_lock); +	return ret; +} + +static int ade7854_i2c_probe(struct i2c_client *client, +		const struct i2c_device_id *id) +{ +	int ret; +	struct ade7854_state *st; +	struct iio_dev *indio_dev; + +	indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*st)); +	if (indio_dev == NULL) +		return -ENOMEM; +	st = iio_priv(indio_dev); +	i2c_set_clientdata(client, indio_dev); +	st->read_reg_8 = ade7854_i2c_read_reg_8; +	st->read_reg_16 = ade7854_i2c_read_reg_16; +	st->read_reg_24 = ade7854_i2c_read_reg_24; +	st->read_reg_32 = ade7854_i2c_read_reg_32; +	st->write_reg_8 = ade7854_i2c_write_reg_8; +	st->write_reg_16 = ade7854_i2c_write_reg_16; +	st->write_reg_24 = ade7854_i2c_write_reg_24; +	st->write_reg_32 = ade7854_i2c_write_reg_32; +	st->i2c = client; +	st->irq = client->irq; + +	ret = ade7854_probe(indio_dev, &client->dev); + +	return ret; +} + +static int ade7854_i2c_remove(struct i2c_client *client) +{ +	return ade7854_remove(i2c_get_clientdata(client)); +} + +static const struct i2c_device_id ade7854_id[] = { +	{ "ade7854", 0 }, +	{ "ade7858", 0 }, +	{ "ade7868", 0 }, +	{ "ade7878", 0 }, +	{ } +}; +MODULE_DEVICE_TABLE(i2c, ade7854_id); + +static struct i2c_driver ade7854_i2c_driver = { +	.driver = { +		.name = "ade7854", +	}, +	.probe    = ade7854_i2c_probe, +	.remove   = ade7854_i2c_remove, +	.id_table = ade7854_id, +}; +module_i2c_driver(ade7854_i2c_driver); + +MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>"); +MODULE_DESCRIPTION("Analog Devices ADE7854/58/68/78 Polyphase Multifunction Energy Metering IC I2C Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/iio/meter/ade7854-spi.c b/drivers/staging/iio/meter/ade7854-spi.c new file mode 100644 index 00000000000..94f73bbbc0f --- /dev/null +++ b/drivers/staging/iio/meter/ade7854-spi.c @@ -0,0 +1,331 @@ +/* + * ADE7854/58/68/78 Polyphase Multifunction Energy Metering IC Driver (SPI Bus) + * + * Copyright 2010 Analog Devices Inc. + * + * Licensed under the GPL-2 or later. + */ + +#include <linux/device.h> +#include <linux/kernel.h> +#include <linux/spi/spi.h> +#include <linux/slab.h> +#include <linux/module.h> + +#include <linux/iio/iio.h> +#include "ade7854.h" + +static int ade7854_spi_write_reg_8(struct device *dev, +		u16 reg_address, +		u8 value) +{ +	int ret; +	struct iio_dev *indio_dev = dev_to_iio_dev(dev); +	struct ade7854_state *st = iio_priv(indio_dev); +	struct spi_transfer xfer = { +		.tx_buf = st->tx, +		.bits_per_word = 8, +		.len = 4, +	}; + +	mutex_lock(&st->buf_lock); +	st->tx[0] = ADE7854_WRITE_REG; +	st->tx[1] = (reg_address >> 8) & 0xFF; +	st->tx[2] = reg_address & 0xFF; +	st->tx[3] = value & 0xFF; + +	ret = spi_sync_transfer(st->spi, &xfer, 1); +	mutex_unlock(&st->buf_lock); + +	return ret; +} + +static int ade7854_spi_write_reg_16(struct device *dev, +		u16 reg_address, +		u16 value) +{ +	int ret; +	struct iio_dev *indio_dev = dev_to_iio_dev(dev); +	struct ade7854_state *st = iio_priv(indio_dev); +	struct spi_transfer xfer = { +		.tx_buf = st->tx, +		.bits_per_word = 8, +		.len = 5, +	}; + +	mutex_lock(&st->buf_lock); +	st->tx[0] = ADE7854_WRITE_REG; +	st->tx[1] = (reg_address >> 8) & 0xFF; +	st->tx[2] = reg_address & 0xFF; +	st->tx[3] = (value >> 8) & 0xFF; +	st->tx[4] = value & 0xFF; + +	ret = spi_sync_transfer(st->spi, &xfer, 1); +	mutex_unlock(&st->buf_lock); + +	return ret; +} + +static int ade7854_spi_write_reg_24(struct device *dev, +		u16 reg_address, +		u32 value) +{ +	int ret; +	struct iio_dev *indio_dev = dev_to_iio_dev(dev); +	struct ade7854_state *st = iio_priv(indio_dev); +	struct spi_transfer xfer = { +		.tx_buf = st->tx, +		.bits_per_word = 8, +		.len = 6, +	}; + +	mutex_lock(&st->buf_lock); +	st->tx[0] = ADE7854_WRITE_REG; +	st->tx[1] = (reg_address >> 8) & 0xFF; +	st->tx[2] = reg_address & 0xFF; +	st->tx[3] = (value >> 16) & 0xFF; +	st->tx[4] = (value >> 8) & 0xFF; +	st->tx[5] = value & 0xFF; + +	ret = spi_sync_transfer(st->spi, &xfer, 1); +	mutex_unlock(&st->buf_lock); + +	return ret; +} + +static int ade7854_spi_write_reg_32(struct device *dev, +		u16 reg_address, +		u32 value) +{ +	int ret; +	struct iio_dev *indio_dev = dev_to_iio_dev(dev); +	struct ade7854_state *st = iio_priv(indio_dev); +	struct spi_transfer xfer = { +		.tx_buf = st->tx, +		.bits_per_word = 8, +		.len = 7, +	}; + +	mutex_lock(&st->buf_lock); +	st->tx[0] = ADE7854_WRITE_REG; +	st->tx[1] = (reg_address >> 8) & 0xFF; +	st->tx[2] = reg_address & 0xFF; +	st->tx[3] = (value >> 24) & 0xFF; +	st->tx[4] = (value >> 16) & 0xFF; +	st->tx[5] = (value >> 8) & 0xFF; +	st->tx[6] = value & 0xFF; + +	ret = spi_sync_transfer(st->spi, &xfer, 1); +	mutex_unlock(&st->buf_lock); + +	return ret; +} + +static int ade7854_spi_read_reg_8(struct device *dev, +		u16 reg_address, +		u8 *val) +{ +	struct iio_dev *indio_dev = dev_to_iio_dev(dev); +	struct ade7854_state *st = iio_priv(indio_dev); +	int ret; +	struct spi_transfer xfers[] = { +		{ +			.tx_buf = st->tx, +			.bits_per_word = 8, +			.len = 3, +		}, { +			.rx_buf = st->rx, +			.bits_per_word = 8, +			.len = 1, +		} +	}; + +	mutex_lock(&st->buf_lock); + +	st->tx[0] = ADE7854_READ_REG; +	st->tx[1] = (reg_address >> 8) & 0xFF; +	st->tx[2] = reg_address & 0xFF; + +	ret = spi_sync_transfer(st->spi, xfers, ARRAY_SIZE(xfers)); +	if (ret) { +		dev_err(&st->spi->dev, "problem when reading 8 bit register 0x%02X", +				reg_address); +		goto error_ret; +	} +	*val = st->rx[0]; + +error_ret: +	mutex_unlock(&st->buf_lock); +	return ret; +} + +static int ade7854_spi_read_reg_16(struct device *dev, +		u16 reg_address, +		u16 *val) +{ +	struct iio_dev *indio_dev = dev_to_iio_dev(dev); +	struct ade7854_state *st = iio_priv(indio_dev); +	int ret; +	struct spi_transfer xfers[] = { +		{ +			.tx_buf = st->tx, +			.bits_per_word = 8, +			.len = 3, +		}, { +			.rx_buf = st->rx, +			.bits_per_word = 8, +			.len = 2, +		} +	}; + +	mutex_lock(&st->buf_lock); +	st->tx[0] = ADE7854_READ_REG; +	st->tx[1] = (reg_address >> 8) & 0xFF; +	st->tx[2] = reg_address & 0xFF; + +	ret = spi_sync_transfer(st->spi, xfers, ARRAY_SIZE(xfers)); +	if (ret) { +		dev_err(&st->spi->dev, "problem when reading 16 bit register 0x%02X", +				reg_address); +		goto error_ret; +	} +	*val = be16_to_cpup((const __be16 *)st->rx); + +error_ret: +	mutex_unlock(&st->buf_lock); +	return ret; +} + +static int ade7854_spi_read_reg_24(struct device *dev, +		u16 reg_address, +		u32 *val) +{ +	struct iio_dev *indio_dev = dev_to_iio_dev(dev); +	struct ade7854_state *st = iio_priv(indio_dev); +	int ret; +	struct spi_transfer xfers[] = { +		{ +			.tx_buf = st->tx, +			.bits_per_word = 8, +			.len = 3, +		}, { +			.rx_buf = st->rx, +			.bits_per_word = 8, +			.len = 3, +		} +	}; + +	mutex_lock(&st->buf_lock); + +	st->tx[0] = ADE7854_READ_REG; +	st->tx[1] = (reg_address >> 8) & 0xFF; +	st->tx[2] = reg_address & 0xFF; + +	ret = spi_sync_transfer(st->spi, xfers, ARRAY_SIZE(xfers)); +	if (ret) { +		dev_err(&st->spi->dev, "problem when reading 24 bit register 0x%02X", +				reg_address); +		goto error_ret; +	} +	*val = (st->rx[0] << 16) | (st->rx[1] << 8) | st->rx[2]; + +error_ret: +	mutex_unlock(&st->buf_lock); +	return ret; +} + +static int ade7854_spi_read_reg_32(struct device *dev, +		u16 reg_address, +		u32 *val) +{ +	struct iio_dev *indio_dev = dev_to_iio_dev(dev); +	struct ade7854_state *st = iio_priv(indio_dev); +	int ret; +	struct spi_transfer xfers[] = { +		{ +			.tx_buf = st->tx, +			.bits_per_word = 8, +			.len = 3, +		}, { +			.rx_buf = st->rx, +			.bits_per_word = 8, +			.len = 4, +		} +	}; + +	mutex_lock(&st->buf_lock); + +	st->tx[0] = ADE7854_READ_REG; +	st->tx[1] = (reg_address >> 8) & 0xFF; +	st->tx[2] = reg_address & 0xFF; + +	ret = spi_sync_transfer(st->spi, xfers, ARRAY_SIZE(xfers)); +	if (ret) { +		dev_err(&st->spi->dev, "problem when reading 32 bit register 0x%02X", +				reg_address); +		goto error_ret; +	} +	*val = be32_to_cpup((const __be32 *)st->rx); + +error_ret: +	mutex_unlock(&st->buf_lock); +	return ret; +} + +static int ade7854_spi_probe(struct spi_device *spi) +{ +	int ret; +	struct ade7854_state *st; +	struct iio_dev *indio_dev; + +	indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); +	if (indio_dev == NULL) +		return -ENOMEM; +	st = iio_priv(indio_dev); +	spi_set_drvdata(spi, indio_dev); +	st->read_reg_8 = ade7854_spi_read_reg_8; +	st->read_reg_16 = ade7854_spi_read_reg_16; +	st->read_reg_24 = ade7854_spi_read_reg_24; +	st->read_reg_32 = ade7854_spi_read_reg_32; +	st->write_reg_8 = ade7854_spi_write_reg_8; +	st->write_reg_16 = ade7854_spi_write_reg_16; +	st->write_reg_24 = ade7854_spi_write_reg_24; +	st->write_reg_32 = ade7854_spi_write_reg_32; +	st->irq = spi->irq; +	st->spi = spi; + + +	ret = ade7854_probe(indio_dev, &spi->dev); + +	return ret; +} + +static int ade7854_spi_remove(struct spi_device *spi) +{ +	ade7854_remove(spi_get_drvdata(spi)); + +	return 0; +} +static const struct spi_device_id ade7854_id[] = { +	{ "ade7854", 0 }, +	{ "ade7858", 0 }, +	{ "ade7868", 0 }, +	{ "ade7878", 0 }, +	{ } +}; +MODULE_DEVICE_TABLE(spi, ade7854_id); + +static struct spi_driver ade7854_driver = { +	.driver = { +		.name = "ade7854", +		.owner = THIS_MODULE, +	}, +	.probe = ade7854_spi_probe, +	.remove = ade7854_spi_remove, +	.id_table = ade7854_id, +}; +module_spi_driver(ade7854_driver); + +MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>"); +MODULE_DESCRIPTION("Analog Devices ADE7854/58/68/78 SPI Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/iio/meter/ade7854.c b/drivers/staging/iio/meter/ade7854.c new file mode 100644 index 00000000000..d620bbd603a --- /dev/null +++ b/drivers/staging/iio/meter/ade7854.c @@ -0,0 +1,578 @@ +/* + * ADE7854/58/68/78 Polyphase Multifunction Energy Metering IC Driver + * + * Copyright 2010 Analog Devices Inc. + * + * Licensed under the GPL-2 or later. + */ + +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/delay.h> +#include <linux/mutex.h> +#include <linux/device.h> +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/sysfs.h> +#include <linux/list.h> +#include <linux/module.h> + +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> +#include "meter.h" +#include "ade7854.h" + +static ssize_t ade7854_read_8bit(struct device *dev, +		struct device_attribute *attr, +		char *buf) +{ +	int ret; +	u8 val = 0; +	struct iio_dev *indio_dev = dev_to_iio_dev(dev); +	struct ade7854_state *st = iio_priv(indio_dev); +	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); + +	ret = st->read_reg_8(dev, this_attr->address, &val); +	if (ret) +		return ret; + +	return sprintf(buf, "%u\n", val); +} + +static ssize_t ade7854_read_16bit(struct device *dev, +		struct device_attribute *attr, +		char *buf) +{ +	int ret; +	u16 val = 0; +	struct iio_dev *indio_dev = dev_to_iio_dev(dev); +	struct ade7854_state *st = iio_priv(indio_dev); +	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); + +	ret = st->read_reg_16(dev, this_attr->address, &val); +	if (ret) +		return ret; + +	return sprintf(buf, "%u\n", val); +} + +static ssize_t ade7854_read_24bit(struct device *dev, +		struct device_attribute *attr, +		char *buf) +{ +	int ret; +	u32 val; +	struct iio_dev *indio_dev = dev_to_iio_dev(dev); +	struct ade7854_state *st = iio_priv(indio_dev); +	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); + +	ret = st->read_reg_24(dev, this_attr->address, &val); +	if (ret) +		return ret; + +	return sprintf(buf, "%u\n", val); +} + +static ssize_t ade7854_read_32bit(struct device *dev, +		struct device_attribute *attr, +		char *buf) +{ +	int ret; +	u32 val = 0; +	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); +	struct iio_dev *indio_dev = dev_to_iio_dev(dev); +	struct ade7854_state *st = iio_priv(indio_dev); + +	ret = st->read_reg_32(dev, this_attr->address, &val); +	if (ret) +		return ret; + +	return sprintf(buf, "%u\n", val); +} + +static ssize_t ade7854_write_8bit(struct device *dev, +		struct device_attribute *attr, +		const char *buf, +		size_t len) +{ +	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); +	struct iio_dev *indio_dev = dev_to_iio_dev(dev); +	struct ade7854_state *st = iio_priv(indio_dev); + +	int ret; +	u8 val; + +	ret = kstrtou8(buf, 10, &val); +	if (ret) +		goto error_ret; +	ret = st->write_reg_8(dev, this_attr->address, val); + +error_ret: +	return ret ? ret : len; +} + +static ssize_t ade7854_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); +	struct iio_dev *indio_dev = dev_to_iio_dev(dev); +	struct ade7854_state *st = iio_priv(indio_dev); + +	int ret; +	u16 val; + +	ret = kstrtou16(buf, 10, &val); +	if (ret) +		goto error_ret; +	ret = st->write_reg_16(dev, this_attr->address, val); + +error_ret: +	return ret ? ret : len; +} + +static ssize_t ade7854_write_24bit(struct device *dev, +		struct device_attribute *attr, +		const char *buf, +		size_t len) +{ +	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); +	struct iio_dev *indio_dev = dev_to_iio_dev(dev); +	struct ade7854_state *st = iio_priv(indio_dev); + +	int ret; +	u32 val; + +	ret = kstrtou32(buf, 10, &val); +	if (ret) +		goto error_ret; +	ret = st->write_reg_24(dev, this_attr->address, val); + +error_ret: +	return ret ? ret : len; +} + +static ssize_t ade7854_write_32bit(struct device *dev, +		struct device_attribute *attr, +		const char *buf, +		size_t len) +{ +	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); +	struct iio_dev *indio_dev = dev_to_iio_dev(dev); +	struct ade7854_state *st = iio_priv(indio_dev); + +	int ret; +	u32 val; + +	ret = kstrtou32(buf, 10, &val); +	if (ret) +		goto error_ret; +	ret = st->write_reg_32(dev, this_attr->address, val); + +error_ret: +	return ret ? ret : len; +} + +static int ade7854_reset(struct device *dev) +{ +	struct iio_dev *indio_dev = dev_to_iio_dev(dev); +	struct ade7854_state *st = iio_priv(indio_dev); +	u16 val; + +	st->read_reg_16(dev, ADE7854_CONFIG, &val); +	val |= 1 << 7; /* Software Chip Reset */ + +	return st->write_reg_16(dev, ADE7854_CONFIG, val); +} + +static IIO_DEV_ATTR_AIGAIN(S_IWUSR | S_IRUGO, +		ade7854_read_24bit, +		ade7854_write_24bit, +		ADE7854_AIGAIN); +static IIO_DEV_ATTR_BIGAIN(S_IWUSR | S_IRUGO, +		ade7854_read_24bit, +		ade7854_write_24bit, +		ADE7854_BIGAIN); +static IIO_DEV_ATTR_CIGAIN(S_IWUSR | S_IRUGO, +		ade7854_read_24bit, +		ade7854_write_24bit, +		ADE7854_CIGAIN); +static IIO_DEV_ATTR_NIGAIN(S_IWUSR | S_IRUGO, +		ade7854_read_24bit, +		ade7854_write_24bit, +		ADE7854_NIGAIN); +static IIO_DEV_ATTR_AVGAIN(S_IWUSR | S_IRUGO, +		ade7854_read_24bit, +		ade7854_write_24bit, +		ADE7854_AVGAIN); +static IIO_DEV_ATTR_BVGAIN(S_IWUSR | S_IRUGO, +		ade7854_read_24bit, +		ade7854_write_24bit, +		ADE7854_BVGAIN); +static IIO_DEV_ATTR_CVGAIN(S_IWUSR | S_IRUGO, +		ade7854_read_24bit, +		ade7854_write_24bit, +		ADE7854_CVGAIN); +static IIO_DEV_ATTR_APPARENT_POWER_A_GAIN(S_IWUSR | S_IRUGO, +		ade7854_read_24bit, +		ade7854_write_24bit, +		ADE7854_AVAGAIN); +static IIO_DEV_ATTR_APPARENT_POWER_B_GAIN(S_IWUSR | S_IRUGO, +		ade7854_read_24bit, +		ade7854_write_24bit, +		ADE7854_BVAGAIN); +static IIO_DEV_ATTR_APPARENT_POWER_C_GAIN(S_IWUSR | S_IRUGO, +		ade7854_read_24bit, +		ade7854_write_24bit, +		ADE7854_CVAGAIN); +static IIO_DEV_ATTR_ACTIVE_POWER_A_OFFSET(S_IWUSR | S_IRUGO, +		ade7854_read_24bit, +		ade7854_write_24bit, +		ADE7854_AWATTOS); +static IIO_DEV_ATTR_ACTIVE_POWER_B_OFFSET(S_IWUSR | S_IRUGO, +		ade7854_read_24bit, +		ade7854_write_24bit, +		ADE7854_BWATTOS); +static IIO_DEV_ATTR_ACTIVE_POWER_C_OFFSET(S_IWUSR | S_IRUGO, +		ade7854_read_24bit, +		ade7854_write_24bit, +		ADE7854_CWATTOS); +static IIO_DEV_ATTR_REACTIVE_POWER_A_GAIN(S_IWUSR | S_IRUGO, +		ade7854_read_24bit, +		ade7854_write_24bit, +		ADE7854_AVARGAIN); +static IIO_DEV_ATTR_REACTIVE_POWER_B_GAIN(S_IWUSR | S_IRUGO, +		ade7854_read_24bit, +		ade7854_write_24bit, +		ADE7854_BVARGAIN); +static IIO_DEV_ATTR_REACTIVE_POWER_C_GAIN(S_IWUSR | S_IRUGO, +		ade7854_read_24bit, +		ade7854_write_24bit, +		ADE7854_CVARGAIN); +static IIO_DEV_ATTR_REACTIVE_POWER_A_OFFSET(S_IWUSR | S_IRUGO, +		ade7854_read_24bit, +		ade7854_write_24bit, +		ADE7854_AVAROS); +static IIO_DEV_ATTR_REACTIVE_POWER_B_OFFSET(S_IWUSR | S_IRUGO, +		ade7854_read_24bit, +		ade7854_write_24bit, +		ADE7854_BVAROS); +static IIO_DEV_ATTR_REACTIVE_POWER_C_OFFSET(S_IWUSR | S_IRUGO, +		ade7854_read_24bit, +		ade7854_write_24bit, +		ADE7854_CVAROS); +static IIO_DEV_ATTR_VPEAK(S_IWUSR | S_IRUGO, +		ade7854_read_32bit, +		ade7854_write_32bit, +		ADE7854_VPEAK); +static IIO_DEV_ATTR_IPEAK(S_IWUSR | S_IRUGO, +		ade7854_read_32bit, +		ade7854_write_32bit, +		ADE7854_VPEAK); +static IIO_DEV_ATTR_APHCAL(S_IWUSR | S_IRUGO, +		ade7854_read_16bit, +		ade7854_write_16bit, +		ADE7854_APHCAL); +static IIO_DEV_ATTR_BPHCAL(S_IWUSR | S_IRUGO, +		ade7854_read_16bit, +		ade7854_write_16bit, +		ADE7854_BPHCAL); +static IIO_DEV_ATTR_CPHCAL(S_IWUSR | S_IRUGO, +		ade7854_read_16bit, +		ade7854_write_16bit, +		ADE7854_CPHCAL); +static IIO_DEV_ATTR_CF1DEN(S_IWUSR | S_IRUGO, +		ade7854_read_16bit, +		ade7854_write_16bit, +		ADE7854_CF1DEN); +static IIO_DEV_ATTR_CF2DEN(S_IWUSR | S_IRUGO, +		ade7854_read_16bit, +		ade7854_write_16bit, +		ADE7854_CF2DEN); +static IIO_DEV_ATTR_CF3DEN(S_IWUSR | S_IRUGO, +		ade7854_read_16bit, +		ade7854_write_16bit, +		ADE7854_CF3DEN); +static IIO_DEV_ATTR_LINECYC(S_IWUSR | S_IRUGO, +		ade7854_read_16bit, +		ade7854_write_16bit, +		ADE7854_LINECYC); +static IIO_DEV_ATTR_SAGCYC(S_IWUSR | S_IRUGO, +		ade7854_read_8bit, +		ade7854_write_8bit, +		ADE7854_SAGCYC); +static IIO_DEV_ATTR_CFCYC(S_IWUSR | S_IRUGO, +		ade7854_read_8bit, +		ade7854_write_8bit, +		ADE7854_CFCYC); +static IIO_DEV_ATTR_PEAKCYC(S_IWUSR | S_IRUGO, +		ade7854_read_8bit, +		ade7854_write_8bit, +		ADE7854_PEAKCYC); +static IIO_DEV_ATTR_CHKSUM(ade7854_read_24bit, +		ADE7854_CHECKSUM); +static IIO_DEV_ATTR_ANGLE0(ade7854_read_24bit, +		ADE7854_ANGLE0); +static IIO_DEV_ATTR_ANGLE1(ade7854_read_24bit, +		ADE7854_ANGLE1); +static IIO_DEV_ATTR_ANGLE2(ade7854_read_24bit, +		ADE7854_ANGLE2); +static IIO_DEV_ATTR_AIRMS(S_IRUGO, +		ade7854_read_24bit, +		NULL, +		ADE7854_AIRMS); +static IIO_DEV_ATTR_BIRMS(S_IRUGO, +		ade7854_read_24bit, +		NULL, +		ADE7854_BIRMS); +static IIO_DEV_ATTR_CIRMS(S_IRUGO, +		ade7854_read_24bit, +		NULL, +		ADE7854_CIRMS); +static IIO_DEV_ATTR_NIRMS(S_IRUGO, +		ade7854_read_24bit, +		NULL, +		ADE7854_NIRMS); +static IIO_DEV_ATTR_AVRMS(S_IRUGO, +		ade7854_read_24bit, +		NULL, +		ADE7854_AVRMS); +static IIO_DEV_ATTR_BVRMS(S_IRUGO, +		ade7854_read_24bit, +		NULL, +		ADE7854_BVRMS); +static IIO_DEV_ATTR_CVRMS(S_IRUGO, +		ade7854_read_24bit, +		NULL, +		ADE7854_CVRMS); +static IIO_DEV_ATTR_AIRMSOS(S_IRUGO, +		ade7854_read_16bit, +		ade7854_write_16bit, +		ADE7854_AIRMSOS); +static IIO_DEV_ATTR_BIRMSOS(S_IRUGO, +		ade7854_read_16bit, +		ade7854_write_16bit, +		ADE7854_BIRMSOS); +static IIO_DEV_ATTR_CIRMSOS(S_IRUGO, +		ade7854_read_16bit, +		ade7854_write_16bit, +		ADE7854_CIRMSOS); +static IIO_DEV_ATTR_AVRMSOS(S_IRUGO, +		ade7854_read_16bit, +		ade7854_write_16bit, +		ADE7854_AVRMSOS); +static IIO_DEV_ATTR_BVRMSOS(S_IRUGO, +		ade7854_read_16bit, +		ade7854_write_16bit, +		ADE7854_BVRMSOS); +static IIO_DEV_ATTR_CVRMSOS(S_IRUGO, +		ade7854_read_16bit, +		ade7854_write_16bit, +		ADE7854_CVRMSOS); +static IIO_DEV_ATTR_VOLT_A(ade7854_read_24bit, +		ADE7854_VAWV); +static IIO_DEV_ATTR_VOLT_B(ade7854_read_24bit, +		ADE7854_VBWV); +static IIO_DEV_ATTR_VOLT_C(ade7854_read_24bit, +		ADE7854_VCWV); +static IIO_DEV_ATTR_CURRENT_A(ade7854_read_24bit, +		ADE7854_IAWV); +static IIO_DEV_ATTR_CURRENT_B(ade7854_read_24bit, +		ADE7854_IBWV); +static IIO_DEV_ATTR_CURRENT_C(ade7854_read_24bit, +		ADE7854_ICWV); +static IIO_DEV_ATTR_AWATTHR(ade7854_read_32bit, +		ADE7854_AWATTHR); +static IIO_DEV_ATTR_BWATTHR(ade7854_read_32bit, +		ADE7854_BWATTHR); +static IIO_DEV_ATTR_CWATTHR(ade7854_read_32bit, +		ADE7854_CWATTHR); +static IIO_DEV_ATTR_AFWATTHR(ade7854_read_32bit, +		ADE7854_AFWATTHR); +static IIO_DEV_ATTR_BFWATTHR(ade7854_read_32bit, +		ADE7854_BFWATTHR); +static IIO_DEV_ATTR_CFWATTHR(ade7854_read_32bit, +		ADE7854_CFWATTHR); +static IIO_DEV_ATTR_AVARHR(ade7854_read_32bit, +		ADE7854_AVARHR); +static IIO_DEV_ATTR_BVARHR(ade7854_read_32bit, +		ADE7854_BVARHR); +static IIO_DEV_ATTR_CVARHR(ade7854_read_32bit, +		ADE7854_CVARHR); +static IIO_DEV_ATTR_AVAHR(ade7854_read_32bit, +		ADE7854_AVAHR); +static IIO_DEV_ATTR_BVAHR(ade7854_read_32bit, +		ADE7854_BVAHR); +static IIO_DEV_ATTR_CVAHR(ade7854_read_32bit, +		ADE7854_CVAHR); + +static int ade7854_set_irq(struct device *dev, bool enable) +{ +	struct iio_dev *indio_dev = dev_to_iio_dev(dev); +	struct ade7854_state *st = iio_priv(indio_dev); + +	int ret; +	u32 irqen; + +	ret = st->read_reg_32(dev, ADE7854_MASK0, &irqen); +	if (ret) +		goto error_ret; + +	if (enable) +		irqen |= 1 << 17; /* 1: interrupt enabled when all periodical +				     (at 8 kHz rate) DSP computations finish. */ +	else +		irqen &= ~(1 << 17); + +	ret = st->write_reg_32(dev, ADE7854_MASK0, irqen); +	if (ret) +		goto error_ret; + +error_ret: +	return ret; +} + +static int ade7854_initial_setup(struct iio_dev *indio_dev) +{ +	int ret; +	struct device *dev = &indio_dev->dev; + +	/* Disable IRQ */ +	ret = ade7854_set_irq(dev, false); +	if (ret) { +		dev_err(dev, "disable irq failed"); +		goto err_ret; +	} + +	ade7854_reset(dev); +	msleep(ADE7854_STARTUP_DELAY); + +err_ret: +	return ret; +} + +static IIO_CONST_ATTR_SAMP_FREQ_AVAIL("8000"); + +static IIO_CONST_ATTR(name, "ade7854"); + +static struct attribute *ade7854_attributes[] = { +	&iio_dev_attr_aigain.dev_attr.attr, +	&iio_dev_attr_bigain.dev_attr.attr, +	&iio_dev_attr_cigain.dev_attr.attr, +	&iio_dev_attr_nigain.dev_attr.attr, +	&iio_dev_attr_avgain.dev_attr.attr, +	&iio_dev_attr_bvgain.dev_attr.attr, +	&iio_dev_attr_cvgain.dev_attr.attr, +	&iio_dev_attr_linecyc.dev_attr.attr, +	&iio_dev_attr_sagcyc.dev_attr.attr, +	&iio_dev_attr_cfcyc.dev_attr.attr, +	&iio_dev_attr_peakcyc.dev_attr.attr, +	&iio_dev_attr_chksum.dev_attr.attr, +	&iio_dev_attr_apparent_power_a_gain.dev_attr.attr, +	&iio_dev_attr_apparent_power_b_gain.dev_attr.attr, +	&iio_dev_attr_apparent_power_c_gain.dev_attr.attr, +	&iio_dev_attr_active_power_a_offset.dev_attr.attr, +	&iio_dev_attr_active_power_b_offset.dev_attr.attr, +	&iio_dev_attr_active_power_c_offset.dev_attr.attr, +	&iio_dev_attr_reactive_power_a_gain.dev_attr.attr, +	&iio_dev_attr_reactive_power_b_gain.dev_attr.attr, +	&iio_dev_attr_reactive_power_c_gain.dev_attr.attr, +	&iio_dev_attr_reactive_power_a_offset.dev_attr.attr, +	&iio_dev_attr_reactive_power_b_offset.dev_attr.attr, +	&iio_dev_attr_reactive_power_c_offset.dev_attr.attr, +	&iio_dev_attr_awatthr.dev_attr.attr, +	&iio_dev_attr_bwatthr.dev_attr.attr, +	&iio_dev_attr_cwatthr.dev_attr.attr, +	&iio_dev_attr_afwatthr.dev_attr.attr, +	&iio_dev_attr_bfwatthr.dev_attr.attr, +	&iio_dev_attr_cfwatthr.dev_attr.attr, +	&iio_dev_attr_avarhr.dev_attr.attr, +	&iio_dev_attr_bvarhr.dev_attr.attr, +	&iio_dev_attr_cvarhr.dev_attr.attr, +	&iio_dev_attr_angle0.dev_attr.attr, +	&iio_dev_attr_angle1.dev_attr.attr, +	&iio_dev_attr_angle2.dev_attr.attr, +	&iio_dev_attr_avahr.dev_attr.attr, +	&iio_dev_attr_bvahr.dev_attr.attr, +	&iio_dev_attr_cvahr.dev_attr.attr, +	&iio_const_attr_sampling_frequency_available.dev_attr.attr, +	&iio_const_attr_name.dev_attr.attr, +	&iio_dev_attr_vpeak.dev_attr.attr, +	&iio_dev_attr_ipeak.dev_attr.attr, +	&iio_dev_attr_aphcal.dev_attr.attr, +	&iio_dev_attr_bphcal.dev_attr.attr, +	&iio_dev_attr_cphcal.dev_attr.attr, +	&iio_dev_attr_cf1den.dev_attr.attr, +	&iio_dev_attr_cf2den.dev_attr.attr, +	&iio_dev_attr_cf3den.dev_attr.attr, +	&iio_dev_attr_airms.dev_attr.attr, +	&iio_dev_attr_birms.dev_attr.attr, +	&iio_dev_attr_cirms.dev_attr.attr, +	&iio_dev_attr_nirms.dev_attr.attr, +	&iio_dev_attr_avrms.dev_attr.attr, +	&iio_dev_attr_bvrms.dev_attr.attr, +	&iio_dev_attr_cvrms.dev_attr.attr, +	&iio_dev_attr_airmsos.dev_attr.attr, +	&iio_dev_attr_birmsos.dev_attr.attr, +	&iio_dev_attr_cirmsos.dev_attr.attr, +	&iio_dev_attr_avrmsos.dev_attr.attr, +	&iio_dev_attr_bvrmsos.dev_attr.attr, +	&iio_dev_attr_cvrmsos.dev_attr.attr, +	&iio_dev_attr_volt_a.dev_attr.attr, +	&iio_dev_attr_volt_b.dev_attr.attr, +	&iio_dev_attr_volt_c.dev_attr.attr, +	&iio_dev_attr_current_a.dev_attr.attr, +	&iio_dev_attr_current_b.dev_attr.attr, +	&iio_dev_attr_current_c.dev_attr.attr, +	NULL, +}; + +static const struct attribute_group ade7854_attribute_group = { +	.attrs = ade7854_attributes, +}; + +static const struct iio_info ade7854_info = { +	.attrs = &ade7854_attribute_group, +	.driver_module = THIS_MODULE, +}; + +int ade7854_probe(struct iio_dev *indio_dev, struct device *dev) +{ +	int ret; +	struct ade7854_state *st = iio_priv(indio_dev); +	/* setup the industrialio driver allocated elements */ +	mutex_init(&st->buf_lock); + +	indio_dev->dev.parent = dev; +	indio_dev->info = &ade7854_info; +	indio_dev->modes = INDIO_DIRECT_MODE; + +	ret = iio_device_register(indio_dev); +	if (ret) +		return ret; + +	/* Get the device into a sane initial state */ +	ret = ade7854_initial_setup(indio_dev); +	if (ret) +		goto error_unreg_dev; + +	return 0; + +error_unreg_dev: +	iio_device_unregister(indio_dev); +	return ret; +} +EXPORT_SYMBOL(ade7854_probe); + +int ade7854_remove(struct iio_dev *indio_dev) +{ +	iio_device_unregister(indio_dev); + +	return 0; +} +EXPORT_SYMBOL(ade7854_remove); + +MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>"); +MODULE_DESCRIPTION("Analog Devices ADE7854/58/68/78 Polyphase Energy Meter"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/iio/meter/ade7854.h b/drivers/staging/iio/meter/ade7854.h new file mode 100644 index 00000000000..06534577f6c --- /dev/null +++ b/drivers/staging/iio/meter/ade7854.h @@ -0,0 +1,174 @@ +#ifndef _ADE7854_H +#define _ADE7854_H + +#define ADE7854_AIGAIN    0x4380 +#define ADE7854_AVGAIN    0x4381 +#define ADE7854_BIGAIN    0x4382 +#define ADE7854_BVGAIN    0x4383 +#define ADE7854_CIGAIN    0x4384 +#define ADE7854_CVGAIN    0x4385 +#define ADE7854_NIGAIN    0x4386 +#define ADE7854_AIRMSOS   0x4387 +#define ADE7854_AVRMSOS   0x4388 +#define ADE7854_BIRMSOS   0x4389 +#define ADE7854_BVRMSOS   0x438A +#define ADE7854_CIRMSOS   0x438B +#define ADE7854_CVRMSOS   0x438C +#define ADE7854_NIRMSOS   0x438D +#define ADE7854_AVAGAIN   0x438E +#define ADE7854_BVAGAIN   0x438F +#define ADE7854_CVAGAIN   0x4390 +#define ADE7854_AWGAIN    0x4391 +#define ADE7854_AWATTOS   0x4392 +#define ADE7854_BWGAIN    0x4393 +#define ADE7854_BWATTOS   0x4394 +#define ADE7854_CWGAIN    0x4395 +#define ADE7854_CWATTOS   0x4396 +#define ADE7854_AVARGAIN  0x4397 +#define ADE7854_AVAROS    0x4398 +#define ADE7854_BVARGAIN  0x4399 +#define ADE7854_BVAROS    0x439A +#define ADE7854_CVARGAIN  0x439B +#define ADE7854_CVAROS    0x439C +#define ADE7854_AFWGAIN   0x439D +#define ADE7854_AFWATTOS  0x439E +#define ADE7854_BFWGAIN   0x439F +#define ADE7854_BFWATTOS  0x43A0 +#define ADE7854_CFWGAIN   0x43A1 +#define ADE7854_CFWATTOS  0x43A2 +#define ADE7854_AFVARGAIN 0x43A3 +#define ADE7854_AFVAROS   0x43A4 +#define ADE7854_BFVARGAIN 0x43A5 +#define ADE7854_BFVAROS   0x43A6 +#define ADE7854_CFVARGAIN 0x43A7 +#define ADE7854_CFVAROS   0x43A8 +#define ADE7854_VATHR1    0x43A9 +#define ADE7854_VATHR0    0x43AA +#define ADE7854_WTHR1     0x43AB +#define ADE7854_WTHR0     0x43AC +#define ADE7854_VARTHR1   0x43AD +#define ADE7854_VARTHR0   0x43AE +#define ADE7854_RSV       0x43AF +#define ADE7854_VANOLOAD  0x43B0 +#define ADE7854_APNOLOAD  0x43B1 +#define ADE7854_VARNOLOAD 0x43B2 +#define ADE7854_VLEVEL    0x43B3 +#define ADE7854_DICOEFF   0x43B5 +#define ADE7854_HPFDIS    0x43B6 +#define ADE7854_ISUMLVL   0x43B8 +#define ADE7854_ISUM      0x43BF +#define ADE7854_AIRMS     0x43C0 +#define ADE7854_AVRMS     0x43C1 +#define ADE7854_BIRMS     0x43C2 +#define ADE7854_BVRMS     0x43C3 +#define ADE7854_CIRMS     0x43C4 +#define ADE7854_CVRMS     0x43C5 +#define ADE7854_NIRMS     0x43C6 +#define ADE7854_RUN       0xE228 +#define ADE7854_AWATTHR   0xE400 +#define ADE7854_BWATTHR   0xE401 +#define ADE7854_CWATTHR   0xE402 +#define ADE7854_AFWATTHR  0xE403 +#define ADE7854_BFWATTHR  0xE404 +#define ADE7854_CFWATTHR  0xE405 +#define ADE7854_AVARHR    0xE406 +#define ADE7854_BVARHR    0xE407 +#define ADE7854_CVARHR    0xE408 +#define ADE7854_AFVARHR   0xE409 +#define ADE7854_BFVARHR   0xE40A +#define ADE7854_CFVARHR   0xE40B +#define ADE7854_AVAHR     0xE40C +#define ADE7854_BVAHR     0xE40D +#define ADE7854_CVAHR     0xE40E +#define ADE7854_IPEAK     0xE500 +#define ADE7854_VPEAK     0xE501 +#define ADE7854_STATUS0   0xE502 +#define ADE7854_STATUS1   0xE503 +#define ADE7854_OILVL     0xE507 +#define ADE7854_OVLVL     0xE508 +#define ADE7854_SAGLVL    0xE509 +#define ADE7854_MASK0     0xE50A +#define ADE7854_MASK1     0xE50B +#define ADE7854_IAWV      0xE50C +#define ADE7854_IBWV      0xE50D +#define ADE7854_ICWV      0xE50E +#define ADE7854_VAWV      0xE510 +#define ADE7854_VBWV      0xE511 +#define ADE7854_VCWV      0xE512 +#define ADE7854_AWATT     0xE513 +#define ADE7854_BWATT     0xE514 +#define ADE7854_CWATT     0xE515 +#define ADE7854_AVA       0xE519 +#define ADE7854_BVA       0xE51A +#define ADE7854_CVA       0xE51B +#define ADE7854_CHECKSUM  0xE51F +#define ADE7854_VNOM      0xE520 +#define ADE7854_PHSTATUS  0xE600 +#define ADE7854_ANGLE0    0xE601 +#define ADE7854_ANGLE1    0xE602 +#define ADE7854_ANGLE2    0xE603 +#define ADE7854_PERIOD    0xE607 +#define ADE7854_PHNOLOAD  0xE608 +#define ADE7854_LINECYC   0xE60C +#define ADE7854_ZXTOUT    0xE60D +#define ADE7854_COMPMODE  0xE60E +#define ADE7854_GAIN      0xE60F +#define ADE7854_CFMODE    0xE610 +#define ADE7854_CF1DEN    0xE611 +#define ADE7854_CF2DEN    0xE612 +#define ADE7854_CF3DEN    0xE613 +#define ADE7854_APHCAL    0xE614 +#define ADE7854_BPHCAL    0xE615 +#define ADE7854_CPHCAL    0xE616 +#define ADE7854_PHSIGN    0xE617 +#define ADE7854_CONFIG    0xE618 +#define ADE7854_MMODE     0xE700 +#define ADE7854_ACCMODE   0xE701 +#define ADE7854_LCYCMODE  0xE702 +#define ADE7854_PEAKCYC   0xE703 +#define ADE7854_SAGCYC    0xE704 +#define ADE7854_CFCYC     0xE705 +#define ADE7854_HSDC_CFG  0xE706 +#define ADE7854_CONFIG2   0xEC01 + +#define ADE7854_READ_REG   0x1 +#define ADE7854_WRITE_REG  0x0 + +#define ADE7854_MAX_TX    7 +#define ADE7854_MAX_RX    7 +#define ADE7854_STARTUP_DELAY 1 + +#define ADE7854_SPI_SLOW	(u32)(300 * 1000) +#define ADE7854_SPI_BURST	(u32)(1000 * 1000) +#define ADE7854_SPI_FAST	(u32)(2000 * 1000) + +/** + * struct ade7854_state - device instance specific data + * @spi:			actual spi_device + * @indio_dev:		industrial I/O device structure + * @buf_lock:		mutex to protect tx and rx + * @tx:			transmit buffer + * @rx:			receive buffer + **/ +struct ade7854_state { +	struct spi_device	*spi; +	struct i2c_client	*i2c; +	int			(*read_reg_8) (struct device *, u16, u8 *); +	int			(*read_reg_16) (struct device *, u16, u16 *); +	int			(*read_reg_24) (struct device *, u16, u32 *); +	int			(*read_reg_32) (struct device *, u16, u32 *); +	int			(*write_reg_8) (struct device *, u16, u8); +	int			(*write_reg_16) (struct device *, u16, u16); +	int			(*write_reg_24) (struct device *, u16, u32); +	int			(*write_reg_32) (struct device *, u16, u32); +	int			irq; +	struct mutex		buf_lock; +	u8			tx[ADE7854_MAX_TX] ____cacheline_aligned; +	u8			rx[ADE7854_MAX_RX]; + +}; + +extern int ade7854_probe(struct iio_dev *indio_dev, struct device *dev); +extern int ade7854_remove(struct iio_dev *indio_dev); + +#endif diff --git a/drivers/staging/iio/meter/meter.h b/drivers/staging/iio/meter/meter.h new file mode 100644 index 00000000000..23e1b5f480a --- /dev/null +++ b/drivers/staging/iio/meter/meter.h @@ -0,0 +1,396 @@ +#include <linux/iio/sysfs.h> + +/* metering ic types of attribute */ + +#define IIO_DEV_ATTR_CURRENT_A_OFFSET(_mode, _show, _store, _addr)	\ +	IIO_DEVICE_ATTR(current_a_offset, _mode, _show, _store, _addr) + +#define IIO_DEV_ATTR_CURRENT_B_OFFSET(_mode, _show, _store, _addr)	\ +	IIO_DEVICE_ATTR(current_b_offset, _mode, _show, _store, _addr) + +#define IIO_DEV_ATTR_CURRENT_C_OFFSET(_mode, _show, _store, _addr)	\ +	IIO_DEVICE_ATTR(current_c_offset, _mode, _show, _store, _addr) + +#define IIO_DEV_ATTR_VOLT_A_OFFSET(_mode, _show, _store, _addr)      \ +	IIO_DEVICE_ATTR(volt_a_offset, _mode, _show, _store, _addr) + +#define IIO_DEV_ATTR_VOLT_B_OFFSET(_mode, _show, _store, _addr)      \ +	IIO_DEVICE_ATTR(volt_b_offset, _mode, _show, _store, _addr) + +#define IIO_DEV_ATTR_VOLT_C_OFFSET(_mode, _show, _store, _addr)      \ +	IIO_DEVICE_ATTR(volt_c_offset, _mode, _show, _store, _addr) + +#define IIO_DEV_ATTR_REACTIVE_POWER_A_OFFSET(_mode, _show, _store, _addr)                \ +	IIO_DEVICE_ATTR(reactive_power_a_offset, _mode, _show, _store, _addr) + +#define IIO_DEV_ATTR_REACTIVE_POWER_B_OFFSET(_mode, _show, _store, _addr)                \ +	IIO_DEVICE_ATTR(reactive_power_b_offset, _mode, _show, _store, _addr) + +#define IIO_DEV_ATTR_REACTIVE_POWER_C_OFFSET(_mode, _show, _store, _addr)                \ +	IIO_DEVICE_ATTR(reactive_power_c_offset, _mode, _show, _store, _addr) + +#define IIO_DEV_ATTR_ACTIVE_POWER_A_OFFSET(_mode, _show, _store, _addr)                \ +	IIO_DEVICE_ATTR(active_power_a_offset, _mode, _show, _store, _addr) + +#define IIO_DEV_ATTR_ACTIVE_POWER_B_OFFSET(_mode, _show, _store, _addr)                \ +	IIO_DEVICE_ATTR(active_power_b_offset, _mode, _show, _store, _addr) + +#define IIO_DEV_ATTR_ACTIVE_POWER_C_OFFSET(_mode, _show, _store, _addr)                \ +	IIO_DEVICE_ATTR(active_power_c_offset, _mode, _show, _store, _addr) + +#define IIO_DEV_ATTR_CURRENT_A_GAIN(_mode, _show, _store, _addr)		\ +	IIO_DEVICE_ATTR(current_a_gain, _mode, _show, _store, _addr) + +#define IIO_DEV_ATTR_CURRENT_B_GAIN(_mode, _show, _store, _addr)		\ +	IIO_DEVICE_ATTR(current_b_gain, _mode, _show, _store, _addr) + +#define IIO_DEV_ATTR_CURRENT_C_GAIN(_mode, _show, _store, _addr)		\ +	IIO_DEVICE_ATTR(current_c_gain, _mode, _show, _store, _addr) + +#define IIO_DEV_ATTR_APPARENT_POWER_A_GAIN(_mode, _show, _store, _addr)                \ +	IIO_DEVICE_ATTR(apparent_power_a_gain, _mode, _show, _store, _addr) + +#define IIO_DEV_ATTR_APPARENT_POWER_B_GAIN(_mode, _show, _store, _addr)                \ +	IIO_DEVICE_ATTR(apparent_power_b_gain, _mode, _show, _store, _addr) + +#define IIO_DEV_ATTR_APPARENT_POWER_C_GAIN(_mode, _show, _store, _addr)                \ +	IIO_DEVICE_ATTR(apparent_power_c_gain, _mode, _show, _store, _addr) + +#define IIO_DEV_ATTR_ACTIVE_POWER_GAIN(_mode, _show, _store, _addr)                \ +	IIO_DEVICE_ATTR(active_power_gain, _mode, _show, _store, _addr) + +#define IIO_DEV_ATTR_ACTIVE_POWER_A_GAIN(_mode, _show, _store, _addr)                \ +	IIO_DEVICE_ATTR(active_power_a_gain, _mode, _show, _store, _addr) + +#define IIO_DEV_ATTR_ACTIVE_POWER_B_GAIN(_mode, _show, _store, _addr)                \ +	IIO_DEVICE_ATTR(active_power_b_gain, _mode, _show, _store, _addr) + +#define IIO_DEV_ATTR_ACTIVE_POWER_C_GAIN(_mode, _show, _store, _addr)                \ +	IIO_DEVICE_ATTR(active_power_c_gain, _mode, _show, _store, _addr) + +#define IIO_DEV_ATTR_REACTIVE_POWER_A_GAIN(_mode, _show, _store, _addr)                \ +	IIO_DEVICE_ATTR(reactive_power_a_gain, _mode, _show, _store, _addr) + +#define IIO_DEV_ATTR_REACTIVE_POWER_B_GAIN(_mode, _show, _store, _addr)                \ +	IIO_DEVICE_ATTR(reactive_power_b_gain, _mode, _show, _store, _addr) + +#define IIO_DEV_ATTR_REACTIVE_POWER_C_GAIN(_mode, _show, _store, _addr)                \ +	IIO_DEVICE_ATTR(reactive_power_c_gain, _mode, _show, _store, _addr) + +#define IIO_DEV_ATTR_CURRENT_A(_show, _addr)			\ +	IIO_DEVICE_ATTR(current_a, S_IRUGO, _show, NULL, _addr) + +#define IIO_DEV_ATTR_CURRENT_B(_show, _addr)			\ +	IIO_DEVICE_ATTR(current_b, S_IRUGO, _show, NULL, _addr) + +#define IIO_DEV_ATTR_CURRENT_C(_show, _addr)			\ +	IIO_DEVICE_ATTR(current_c, S_IRUGO, _show, NULL, _addr) + +#define IIO_DEV_ATTR_VOLT_A(_show, _addr)			\ +	IIO_DEVICE_ATTR(volt_a, S_IRUGO, _show, NULL, _addr) + +#define IIO_DEV_ATTR_VOLT_B(_show, _addr)			\ +	IIO_DEVICE_ATTR(volt_b, S_IRUGO, _show, NULL, _addr) + +#define IIO_DEV_ATTR_VOLT_C(_show, _addr)			\ +	IIO_DEVICE_ATTR(volt_c, S_IRUGO, _show, NULL, _addr) + +#define IIO_DEV_ATTR_AENERGY(_show, _addr)			\ +	IIO_DEVICE_ATTR(aenergy, S_IRUGO, _show, NULL, _addr) + +#define IIO_DEV_ATTR_LENERGY(_show, _addr)			\ +	IIO_DEVICE_ATTR(lenergy, S_IRUGO, _show, NULL, _addr) + +#define IIO_DEV_ATTR_RAENERGY(_show, _addr)			\ +	IIO_DEVICE_ATTR(raenergy, S_IRUGO, _show, NULL, _addr) + +#define IIO_DEV_ATTR_LAENERGY(_show, _addr)			\ +	IIO_DEVICE_ATTR(laenergy, S_IRUGO, _show, NULL, _addr) + +#define IIO_DEV_ATTR_VAENERGY(_show, _addr)			\ +	IIO_DEVICE_ATTR(vaenergy, S_IRUGO, _show, NULL, _addr) + +#define IIO_DEV_ATTR_LVAENERGY(_show, _addr)			\ +	IIO_DEVICE_ATTR(lvaenergy, S_IRUGO, _show, NULL, _addr) + +#define IIO_DEV_ATTR_RVAENERGY(_show, _addr)			\ +	IIO_DEVICE_ATTR(rvaenergy, S_IRUGO, _show, NULL, _addr) + +#define IIO_DEV_ATTR_LVARENERGY(_show, _addr)			\ +	IIO_DEVICE_ATTR(lvarenergy, S_IRUGO, _show, NULL, _addr) + +#define IIO_DEV_ATTR_CHKSUM(_show, _addr)                       \ +	IIO_DEVICE_ATTR(chksum, S_IRUGO, _show, NULL, _addr) + +#define IIO_DEV_ATTR_ANGLE0(_show, _addr)                       \ +	IIO_DEVICE_ATTR(angle0, S_IRUGO, _show, NULL, _addr) + +#define IIO_DEV_ATTR_ANGLE1(_show, _addr)                       \ +	IIO_DEVICE_ATTR(angle1, S_IRUGO, _show, NULL, _addr) + +#define IIO_DEV_ATTR_ANGLE2(_show, _addr)                       \ +	IIO_DEVICE_ATTR(angle2, S_IRUGO, _show, NULL, _addr) + +#define IIO_DEV_ATTR_AWATTHR(_show, _addr)			\ +	IIO_DEVICE_ATTR(awatthr, S_IRUGO, _show, NULL, _addr) + +#define IIO_DEV_ATTR_BWATTHR(_show, _addr)			\ +	IIO_DEVICE_ATTR(bwatthr, S_IRUGO, _show, NULL, _addr) + +#define IIO_DEV_ATTR_CWATTHR(_show, _addr)			\ +	IIO_DEVICE_ATTR(cwatthr, S_IRUGO, _show, NULL, _addr) + +#define IIO_DEV_ATTR_AFWATTHR(_show, _addr)			\ +	IIO_DEVICE_ATTR(afwatthr, S_IRUGO, _show, NULL, _addr) + +#define IIO_DEV_ATTR_BFWATTHR(_show, _addr)			\ +	IIO_DEVICE_ATTR(bfwatthr, S_IRUGO, _show, NULL, _addr) + +#define IIO_DEV_ATTR_CFWATTHR(_show, _addr)			\ +	IIO_DEVICE_ATTR(cfwatthr, S_IRUGO, _show, NULL, _addr) + +#define IIO_DEV_ATTR_AVARHR(_show, _addr)			\ +	IIO_DEVICE_ATTR(avarhr, S_IRUGO, _show, NULL, _addr) + +#define IIO_DEV_ATTR_BVARHR(_show, _addr)			\ +	IIO_DEVICE_ATTR(bvarhr, S_IRUGO, _show, NULL, _addr) + +#define IIO_DEV_ATTR_CVARHR(_show, _addr)			\ +	IIO_DEVICE_ATTR(cvarhr, S_IRUGO, _show, NULL, _addr) + +#define IIO_DEV_ATTR_AVAHR(_show, _addr)			\ +	IIO_DEVICE_ATTR(avahr, S_IRUGO, _show, NULL, _addr) + +#define IIO_DEV_ATTR_BVAHR(_show, _addr)			\ +	IIO_DEVICE_ATTR(bvahr, S_IRUGO, _show, NULL, _addr) + +#define IIO_DEV_ATTR_CVAHR(_show, _addr)			\ +	IIO_DEVICE_ATTR(cvahr, S_IRUGO, _show, NULL, _addr) + +#define IIO_DEV_ATTR_IOS(_mode, _show, _store, _addr)                \ +	IIO_DEVICE_ATTR(ios, _mode, _show, _store, _addr) + +#define IIO_DEV_ATTR_VOS(_mode, _show, _store, _addr)                \ +	IIO_DEVICE_ATTR(vos, _mode, _show, _store, _addr) + +#define IIO_DEV_ATTR_PHCAL(_mode, _show, _store, _addr)                \ +	IIO_DEVICE_ATTR(phcal, _mode, _show, _store, _addr) + +#define IIO_DEV_ATTR_APHCAL(_mode, _show, _store, _addr)                \ +	IIO_DEVICE_ATTR(aphcal, _mode, _show, _store, _addr) + +#define IIO_DEV_ATTR_BPHCAL(_mode, _show, _store, _addr)                \ +	IIO_DEVICE_ATTR(bphcal, _mode, _show, _store, _addr) + +#define IIO_DEV_ATTR_CPHCAL(_mode, _show, _store, _addr)                \ +	IIO_DEVICE_ATTR(cphcal, _mode, _show, _store, _addr) + +#define IIO_DEV_ATTR_APOS(_mode, _show, _store, _addr)                \ +	IIO_DEVICE_ATTR(apos, _mode, _show, _store, _addr) + +#define IIO_DEV_ATTR_AAPOS(_mode, _show, _store, _addr)                \ +	IIO_DEVICE_ATTR(aapos, _mode, _show, _store, _addr) + +#define IIO_DEV_ATTR_BAPOS(_mode, _show, _store, _addr)                \ +	IIO_DEVICE_ATTR(bapos, _mode, _show, _store, _addr) + +#define IIO_DEV_ATTR_CAPOS(_mode, _show, _store, _addr)                \ +	IIO_DEVICE_ATTR(capos, _mode, _show, _store, _addr) + +#define IIO_DEV_ATTR_AVRMSGAIN(_mode, _show, _store, _addr)                \ +	IIO_DEVICE_ATTR(avrmsgain, _mode, _show, _store, _addr) + +#define IIO_DEV_ATTR_BVRMSGAIN(_mode, _show, _store, _addr)                \ +	IIO_DEVICE_ATTR(bvrmsgain, _mode, _show, _store, _addr) + +#define IIO_DEV_ATTR_CVRMSGAIN(_mode, _show, _store, _addr)                \ +	IIO_DEVICE_ATTR(cvrmsgain, _mode, _show, _store, _addr) + +#define IIO_DEV_ATTR_AIGAIN(_mode, _show, _store, _addr)                \ +	IIO_DEVICE_ATTR(aigain, _mode, _show, _store, _addr) + +#define IIO_DEV_ATTR_BIGAIN(_mode, _show, _store, _addr)                \ +	IIO_DEVICE_ATTR(bigain, _mode, _show, _store, _addr) + +#define IIO_DEV_ATTR_CIGAIN(_mode, _show, _store, _addr)                \ +	IIO_DEVICE_ATTR(cigain, _mode, _show, _store, _addr) + +#define IIO_DEV_ATTR_NIGAIN(_mode, _show, _store, _addr)                \ +	IIO_DEVICE_ATTR(nigain, _mode, _show, _store, _addr) + +#define IIO_DEV_ATTR_AVGAIN(_mode, _show, _store, _addr)                \ +	IIO_DEVICE_ATTR(avgain, _mode, _show, _store, _addr) + +#define IIO_DEV_ATTR_BVGAIN(_mode, _show, _store, _addr)                \ +	IIO_DEVICE_ATTR(bvgain, _mode, _show, _store, _addr) + +#define IIO_DEV_ATTR_CVGAIN(_mode, _show, _store, _addr)                \ +	IIO_DEVICE_ATTR(cvgain, _mode, _show, _store, _addr) + +#define IIO_DEV_ATTR_WGAIN(_mode, _show, _store, _addr)                \ +	IIO_DEVICE_ATTR(wgain, _mode, _show, _store, _addr) + +#define IIO_DEV_ATTR_WDIV(_mode, _show, _store, _addr)                \ +	IIO_DEVICE_ATTR(wdiv, _mode, _show, _store, _addr) + +#define IIO_DEV_ATTR_CFNUM(_mode, _show, _store, _addr)                \ +	IIO_DEVICE_ATTR(cfnum, _mode, _show, _store, _addr) + +#define IIO_DEV_ATTR_CFDEN(_mode, _show, _store, _addr)                \ +	IIO_DEVICE_ATTR(cfden, _mode, _show, _store, _addr) + +#define IIO_DEV_ATTR_CF1DEN(_mode, _show, _store, _addr)                \ +	IIO_DEVICE_ATTR(cf1den, _mode, _show, _store, _addr) + +#define IIO_DEV_ATTR_CF2DEN(_mode, _show, _store, _addr)                \ +	IIO_DEVICE_ATTR(cf2den, _mode, _show, _store, _addr) + +#define IIO_DEV_ATTR_CF3DEN(_mode, _show, _store, _addr)                \ +	IIO_DEVICE_ATTR(cf3den, _mode, _show, _store, _addr) + +#define IIO_DEV_ATTR_IRMS(_mode, _show, _store, _addr)                \ +	IIO_DEVICE_ATTR(irms, _mode, _show, _store, _addr) + +#define IIO_DEV_ATTR_VRMS(_mode, _show, _store, _addr)                \ +	IIO_DEVICE_ATTR(vrms, _mode, _show, _store, _addr) + +#define IIO_DEV_ATTR_AIRMS(_mode, _show, _store, _addr)                \ +	IIO_DEVICE_ATTR(airms, _mode, _show, _store, _addr) + +#define IIO_DEV_ATTR_BIRMS(_mode, _show, _store, _addr)                \ +	IIO_DEVICE_ATTR(birms, _mode, _show, _store, _addr) + +#define IIO_DEV_ATTR_CIRMS(_mode, _show, _store, _addr)                \ +	IIO_DEVICE_ATTR(cirms, _mode, _show, _store, _addr) + +#define IIO_DEV_ATTR_NIRMS(_mode, _show, _store, _addr)                \ +	IIO_DEVICE_ATTR(nirms, _mode, _show, _store, _addr) + +#define IIO_DEV_ATTR_AVRMS(_mode, _show, _store, _addr)                \ +	IIO_DEVICE_ATTR(avrms, _mode, _show, _store, _addr) + +#define IIO_DEV_ATTR_BVRMS(_mode, _show, _store, _addr)                \ +	IIO_DEVICE_ATTR(bvrms, _mode, _show, _store, _addr) + +#define IIO_DEV_ATTR_CVRMS(_mode, _show, _store, _addr)                \ +	IIO_DEVICE_ATTR(cvrms, _mode, _show, _store, _addr) + +#define IIO_DEV_ATTR_IRMSOS(_mode, _show, _store, _addr)                \ +	IIO_DEVICE_ATTR(irmsos, _mode, _show, _store, _addr) + +#define IIO_DEV_ATTR_VRMSOS(_mode, _show, _store, _addr)                \ +	IIO_DEVICE_ATTR(vrmsos, _mode, _show, _store, _addr) + +#define IIO_DEV_ATTR_AIRMSOS(_mode, _show, _store, _addr)                \ +	IIO_DEVICE_ATTR(airmsos, _mode, _show, _store, _addr) + +#define IIO_DEV_ATTR_BIRMSOS(_mode, _show, _store, _addr)                \ +	IIO_DEVICE_ATTR(birmsos, _mode, _show, _store, _addr) + +#define IIO_DEV_ATTR_CIRMSOS(_mode, _show, _store, _addr)                \ +	IIO_DEVICE_ATTR(cirmsos, _mode, _show, _store, _addr) + +#define IIO_DEV_ATTR_AVRMSOS(_mode, _show, _store, _addr)                \ +	IIO_DEVICE_ATTR(avrmsos, _mode, _show, _store, _addr) + +#define IIO_DEV_ATTR_BVRMSOS(_mode, _show, _store, _addr)                \ +	IIO_DEVICE_ATTR(bvrmsos, _mode, _show, _store, _addr) + +#define IIO_DEV_ATTR_CVRMSOS(_mode, _show, _store, _addr)                \ +	IIO_DEVICE_ATTR(cvrmsos, _mode, _show, _store, _addr) + +#define IIO_DEV_ATTR_VAGAIN(_mode, _show, _store, _addr)                \ +	IIO_DEVICE_ATTR(vagain, _mode, _show, _store, _addr) + +#define IIO_DEV_ATTR_PGA_GAIN(_mode, _show, _store, _addr)                \ +	IIO_DEVICE_ATTR(pga_gain, _mode, _show, _store, _addr) + +#define IIO_DEV_ATTR_VADIV(_mode, _show, _store, _addr)                \ +	IIO_DEVICE_ATTR(vadiv, _mode, _show, _store, _addr) + +#define IIO_DEV_ATTR_LINECYC(_mode, _show, _store, _addr)                \ +	IIO_DEVICE_ATTR(linecyc, _mode, _show, _store, _addr) + +#define IIO_DEV_ATTR_SAGCYC(_mode, _show, _store, _addr)                \ +	IIO_DEVICE_ATTR(sagcyc, _mode, _show, _store, _addr) + +#define IIO_DEV_ATTR_CFCYC(_mode, _show, _store, _addr)                \ +	IIO_DEVICE_ATTR(cfcyc, _mode, _show, _store, _addr) + +#define IIO_DEV_ATTR_PEAKCYC(_mode, _show, _store, _addr)                \ +	IIO_DEVICE_ATTR(peakcyc, _mode, _show, _store, _addr) + +#define IIO_DEV_ATTR_SAGLVL(_mode, _show, _store, _addr)                \ +	IIO_DEVICE_ATTR(saglvl, _mode, _show, _store, _addr) + +#define IIO_DEV_ATTR_IPKLVL(_mode, _show, _store, _addr)                \ +	IIO_DEVICE_ATTR(ipklvl, _mode, _show, _store, _addr) + +#define IIO_DEV_ATTR_VPKLVL(_mode, _show, _store, _addr)                \ +	IIO_DEVICE_ATTR(vpklvl, _mode, _show, _store, _addr) + +#define IIO_DEV_ATTR_IPEAK(_mode, _show, _store, _addr)			\ +	IIO_DEVICE_ATTR(ipeak, _mode, _show, _store, _addr) + +#define IIO_DEV_ATTR_RIPEAK(_mode, _show, _store, _addr)			\ +	IIO_DEVICE_ATTR(ripeak, _mode, _show, _store, _addr) + +#define IIO_DEV_ATTR_VPEAK(_mode, _show, _store, _addr)			\ +	IIO_DEVICE_ATTR(vpeak, _mode, _show, _store, _addr) + +#define IIO_DEV_ATTR_RVPEAK(_mode, _show, _store, _addr)			\ +	IIO_DEVICE_ATTR(rvpeak, _mode, _show, _store, _addr) + +#define IIO_DEV_ATTR_VPERIOD(_mode, _show, _store, _addr)			\ +	IIO_DEVICE_ATTR(vperiod, _mode, _show, _store, _addr) + +#define IIO_DEV_ATTR_CH_OFF(_num, _mode, _show, _store, _addr)			\ +  IIO_DEVICE_ATTR(choff_##_num, _mode, _show, _store, _addr) + +/* active energy register, AENERGY, is more than half full */ +#define IIO_EVENT_ATTR_AENERGY_HALF_FULL(_evlist, _show, _store, _mask) \ +	IIO_EVENT_ATTR_SH(aenergy_half_full, _evlist, _show, _store, _mask) + +/* a SAG on the line voltage */ +#define IIO_EVENT_ATTR_LINE_VOLT_SAG(_evlist, _show, _store, _mask) \ +	IIO_EVENT_ATTR_SH(line_volt_sag, _evlist, _show, _store, _mask) + +/* + * Indicates the end of energy accumulation over an integer number + * of half line cycles + */ +#define IIO_EVENT_ATTR_CYCEND(_evlist, _show, _store, _mask) \ +	IIO_EVENT_ATTR_SH(cycend, _evlist, _show, _store, _mask) + +/* on the rising and falling edge of the voltage waveform */ +#define IIO_EVENT_ATTR_ZERO_CROSS(_evlist, _show, _store, _mask) \ +	IIO_EVENT_ATTR_SH(zero_cross, _evlist, _show, _store, _mask) + +/* the active energy register has overflowed */ +#define IIO_EVENT_ATTR_AENERGY_OVERFLOW(_evlist, _show, _store, _mask) \ +	IIO_EVENT_ATTR_SH(aenergy_overflow, _evlist, _show, _store, _mask) + +/* the apparent energy register has overflowed */ +#define IIO_EVENT_ATTR_VAENERGY_OVERFLOW(_evlist, _show, _store, _mask) \ +	IIO_EVENT_ATTR_SH(vaenergy_overflow, _evlist, _show, _store, _mask) + +/* the active energy register, VAENERGY, is more than half full */ +#define IIO_EVENT_ATTR_VAENERGY_HALF_FULL(_evlist, _show, _store, _mask) \ +	IIO_EVENT_ATTR_SH(vaenergy_half_full, _evlist, _show, _store, _mask) + +/* the power has gone from negative to positive */ +#define IIO_EVENT_ATTR_PPOS(_evlist, _show, _store, _mask) \ +	IIO_EVENT_ATTR_SH(ppos, _evlist, _show, _store, _mask) + +/* the power has gone from positive to negative */ +#define IIO_EVENT_ATTR_PNEG(_evlist, _show, _store, _mask) \ +	IIO_EVENT_ATTR_SH(pneg, _evlist, _show, _store, _mask) + +/* waveform sample from Channel 1 has exceeded the IPKLVL value */ +#define IIO_EVENT_ATTR_IPKLVL_EXC(_evlist, _show, _store, _mask) \ +	IIO_EVENT_ATTR_SH(ipklvl_exc, _evlist, _show, _store, _mask) + +/* waveform sample from Channel 2 has exceeded the VPKLVL value */ +#define IIO_EVENT_ATTR_VPKLVL_EXC(_evlist, _show, _store, _mask) \ +	IIO_EVENT_ATTR_SH(vpklvl_exc, _evlist, _show, _store, _mask) + diff --git a/drivers/staging/iio/resolver/Kconfig b/drivers/staging/iio/resolver/Kconfig new file mode 100644 index 00000000000..ce360f16321 --- /dev/null +++ b/drivers/staging/iio/resolver/Kconfig @@ -0,0 +1,30 @@ +# +# Resolver/Synchro drivers +# +menu "Resolver to digital converters" + +config AD2S90 +	tristate "Analog Devices ad2s90 driver" +	depends on SPI +	help +	  Say yes here to build support for Analog Devices spi resolver +	  to digital converters, ad2s90, provides direct access via sysfs. + +config AD2S1200 +	tristate "Analog Devices ad2s1200/ad2s1205 driver" +	depends on SPI +	depends on GPIOLIB +	help +	  Say yes here to build support for Analog Devices spi resolver +	  to digital converters, ad2s1200 and ad2s1205, provides direct access +	  via sysfs. + +config AD2S1210 +	tristate "Analog Devices ad2s1210 driver" +	depends on SPI +	depends on GPIOLIB +	help +	  Say yes here to build support for Analog Devices spi resolver +	  to digital converters, ad2s1210, provides direct access via sysfs. + +endmenu diff --git a/drivers/staging/iio/resolver/Makefile b/drivers/staging/iio/resolver/Makefile new file mode 100644 index 00000000000..14375e444eb --- /dev/null +++ b/drivers/staging/iio/resolver/Makefile @@ -0,0 +1,7 @@ +# +# Makefile for Resolver/Synchro drivers +# + +obj-$(CONFIG_AD2S90) += ad2s90.o +obj-$(CONFIG_AD2S1200) += ad2s1200.o +obj-$(CONFIG_AD2S1210) += ad2s1210.o diff --git a/drivers/staging/iio/resolver/ad2s1200.c b/drivers/staging/iio/resolver/ad2s1200.c new file mode 100644 index 00000000000..017d2f8379b --- /dev/null +++ b/drivers/staging/iio/resolver/ad2s1200.c @@ -0,0 +1,166 @@ +/* + * ad2s1200.c simple support for the ADI Resolver to Digital Converters: + * AD2S1200/1205 + * + * Copyright (c) 2010-2010 Analog Devices Inc. + * + * 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/types.h> +#include <linux/mutex.h> +#include <linux/device.h> +#include <linux/spi/spi.h> +#include <linux/slab.h> +#include <linux/sysfs.h> +#include <linux/delay.h> +#include <linux/gpio.h> +#include <linux/module.h> + +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> + +#define DRV_NAME "ad2s1200" + +/* input pin sample and rdvel is controlled by driver */ +#define AD2S1200_PN	2 + +/* input clock on serial interface */ +#define AD2S1200_HZ	8192000 +/* clock period in nano second */ +#define AD2S1200_TSCLK	(1000000000/AD2S1200_HZ) + +struct ad2s1200_state { +	struct mutex lock; +	struct spi_device *sdev; +	int sample; +	int rdvel; +	u8 rx[2] ____cacheline_aligned; +}; + +static int ad2s1200_read_raw(struct iio_dev *indio_dev, +			   struct iio_chan_spec const *chan, +			   int *val, +			   int *val2, +			   long m) +{ +	int ret = 0; +	s16 vel; +	struct ad2s1200_state *st = iio_priv(indio_dev); + +	mutex_lock(&st->lock); +	gpio_set_value(st->sample, 0); +	/* delay (6 * AD2S1200_TSCLK + 20) nano seconds */ +	udelay(1); +	gpio_set_value(st->sample, 1); +	gpio_set_value(st->rdvel, !!(chan->type == IIO_ANGL)); +	ret = spi_read(st->sdev, st->rx, 2); +	if (ret < 0) { +		mutex_unlock(&st->lock); +		return ret; +	} + +	switch (chan->type) { +	case IIO_ANGL: +		*val = (((u16)(st->rx[0])) << 4) | ((st->rx[1] & 0xF0) >> 4); +		break; +	case IIO_ANGL_VEL: +		vel = (((s16)(st->rx[0])) << 4) | ((st->rx[1] & 0xF0) >> 4); +		vel = (vel << 4) >> 4; +		*val = vel; +		break; +	default: +		mutex_unlock(&st->lock); +		return -EINVAL; +	} +	/* delay (2 * AD2S1200_TSCLK + 20) ns for sample pulse */ +	udelay(1); +	mutex_unlock(&st->lock); +	return IIO_VAL_INT; +} + +static const struct iio_chan_spec ad2s1200_channels[] = { +	{ +		.type = IIO_ANGL, +		.indexed = 1, +		.channel = 0, +		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), +	}, { +		.type = IIO_ANGL_VEL, +		.indexed = 1, +		.channel = 0, +		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), +	} +}; + +static const struct iio_info ad2s1200_info = { +	.read_raw = &ad2s1200_read_raw, +	.driver_module = THIS_MODULE, +}; + +static int ad2s1200_probe(struct spi_device *spi) +{ +	struct ad2s1200_state *st; +	struct iio_dev *indio_dev; +	int pn, ret = 0; +	unsigned short *pins = spi->dev.platform_data; + +	for (pn = 0; pn < AD2S1200_PN; pn++) { +		ret = devm_gpio_request_one(&spi->dev, pins[pn], GPIOF_DIR_OUT, +					    DRV_NAME); +		if (ret) { +			dev_err(&spi->dev, "request gpio pin %d failed\n", +							pins[pn]); +			return ret; +		} +	} +	indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); +	if (!indio_dev) +		return -ENOMEM; +	spi_set_drvdata(spi, indio_dev); +	st = iio_priv(indio_dev); +	mutex_init(&st->lock); +	st->sdev = spi; +	st->sample = pins[0]; +	st->rdvel = pins[1]; + +	indio_dev->dev.parent = &spi->dev; +	indio_dev->info = &ad2s1200_info; +	indio_dev->modes = INDIO_DIRECT_MODE; +	indio_dev->channels = ad2s1200_channels; +	indio_dev->num_channels = ARRAY_SIZE(ad2s1200_channels); +	indio_dev->name = spi_get_device_id(spi)->name; + +	ret = devm_iio_device_register(&spi->dev, indio_dev); +	if (ret) +		return ret; + +	spi->max_speed_hz = AD2S1200_HZ; +	spi->mode = SPI_MODE_3; +	spi_setup(spi); + +	return 0; +} + +static const struct spi_device_id ad2s1200_id[] = { +	{ "ad2s1200" }, +	{ "ad2s1205" }, +	{} +}; +MODULE_DEVICE_TABLE(spi, ad2s1200_id); + +static struct spi_driver ad2s1200_driver = { +	.driver = { +		.name = DRV_NAME, +		.owner = THIS_MODULE, +	}, +	.probe = ad2s1200_probe, +	.id_table = ad2s1200_id, +}; +module_spi_driver(ad2s1200_driver); + +MODULE_AUTHOR("Graff Yang <graff.yang@gmail.com>"); +MODULE_DESCRIPTION("Analog Devices AD2S1200/1205 Resolver to Digital SPI driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/iio/resolver/ad2s1210.c b/drivers/staging/iio/resolver/ad2s1210.c new file mode 100644 index 00000000000..7fbaba41c87 --- /dev/null +++ b/drivers/staging/iio/resolver/ad2s1210.c @@ -0,0 +1,742 @@ +/* + * ad2s1210.c support for the ADI Resolver to Digital Converters: AD2S1210 + * + * Copyright (c) 2010-2010 Analog Devices Inc. + * + * 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/types.h> +#include <linux/mutex.h> +#include <linux/device.h> +#include <linux/spi/spi.h> +#include <linux/slab.h> +#include <linux/sysfs.h> +#include <linux/delay.h> +#include <linux/gpio.h> +#include <linux/module.h> + +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> +#include "ad2s1210.h" + +#define DRV_NAME "ad2s1210" + +#define AD2S1210_DEF_CONTROL		0x7E + +#define AD2S1210_MSB_IS_HIGH		0x80 +#define AD2S1210_MSB_IS_LOW		0x7F +#define AD2S1210_PHASE_LOCK_RANGE_44	0x20 +#define AD2S1210_ENABLE_HYSTERESIS	0x10 +#define AD2S1210_SET_ENRES1		0x08 +#define AD2S1210_SET_ENRES0		0x04 +#define AD2S1210_SET_RES1		0x02 +#define AD2S1210_SET_RES0		0x01 + +#define AD2S1210_SET_ENRESOLUTION	(AD2S1210_SET_ENRES1 |	\ +					 AD2S1210_SET_ENRES0) +#define AD2S1210_SET_RESOLUTION		(AD2S1210_SET_RES1 | AD2S1210_SET_RES0) + +#define AD2S1210_REG_POSITION		0x80 +#define AD2S1210_REG_VELOCITY		0x82 +#define AD2S1210_REG_LOS_THRD		0x88 +#define AD2S1210_REG_DOS_OVR_THRD	0x89 +#define AD2S1210_REG_DOS_MIS_THRD	0x8A +#define AD2S1210_REG_DOS_RST_MAX_THRD	0x8B +#define AD2S1210_REG_DOS_RST_MIN_THRD	0x8C +#define AD2S1210_REG_LOT_HIGH_THRD	0x8D +#define AD2S1210_REG_LOT_LOW_THRD	0x8E +#define AD2S1210_REG_EXCIT_FREQ		0x91 +#define AD2S1210_REG_CONTROL		0x92 +#define AD2S1210_REG_SOFT_RESET		0xF0 +#define AD2S1210_REG_FAULT		0xFF + +/* pin SAMPLE, A0, A1, RES0, RES1, is controlled by driver */ +#define AD2S1210_SAA		3 +#define AD2S1210_PN		(AD2S1210_SAA + AD2S1210_RES) + +#define AD2S1210_MIN_CLKIN	6144000 +#define AD2S1210_MAX_CLKIN	10240000 +#define AD2S1210_MIN_EXCIT	2000 +#define AD2S1210_MAX_EXCIT	20000 +#define AD2S1210_MIN_FCW	0x4 +#define AD2S1210_MAX_FCW	0x50 + +/* default input clock on serial interface */ +#define AD2S1210_DEF_CLKIN	8192000 +/* clock period in nano second */ +#define AD2S1210_DEF_TCK	(1000000000/AD2S1210_DEF_CLKIN) +#define AD2S1210_DEF_EXCIT	10000 + +enum ad2s1210_mode { +	MOD_POS = 0, +	MOD_VEL, +	MOD_CONFIG, +	MOD_RESERVED, +}; + +static const unsigned int ad2s1210_resolution_value[] = { 10, 12, 14, 16 }; + +struct ad2s1210_state { +	const struct ad2s1210_platform_data *pdata; +	struct mutex lock; +	struct spi_device *sdev; +	unsigned int fclkin; +	unsigned int fexcit; +	bool hysteresis; +	bool old_data; +	u8 resolution; +	enum ad2s1210_mode mode; +	u8 rx[2] ____cacheline_aligned; +	u8 tx[2] ____cacheline_aligned; +}; + +static const int ad2s1210_mode_vals[4][2] = { +	[MOD_POS] = { 0, 0 }, +	[MOD_VEL] = { 0, 1 }, +	[MOD_CONFIG] = { 1, 0 }, +}; +static inline void ad2s1210_set_mode(enum ad2s1210_mode mode, +				     struct ad2s1210_state *st) +{ +	gpio_set_value(st->pdata->a[0], ad2s1210_mode_vals[mode][0]); +	gpio_set_value(st->pdata->a[1], ad2s1210_mode_vals[mode][1]); +	st->mode = mode; +} + +/* write 1 bytes (address or data) to the chip */ +static int ad2s1210_config_write(struct ad2s1210_state *st, u8 data) +{ +	int ret; + +	ad2s1210_set_mode(MOD_CONFIG, st); +	st->tx[0] = data; +	ret = spi_write(st->sdev, st->tx, 1); +	if (ret < 0) +		return ret; +	st->old_data = true; + +	return 0; +} + +/* read value from one of the registers */ +static int ad2s1210_config_read(struct ad2s1210_state *st, +		       unsigned char address) +{ +	struct spi_transfer xfer = { +		.len = 2, +		.rx_buf = st->rx, +		.tx_buf = st->tx, +	}; +	int ret = 0; + +	ad2s1210_set_mode(MOD_CONFIG, st); +	st->tx[0] = address | AD2S1210_MSB_IS_HIGH; +	st->tx[1] = AD2S1210_REG_FAULT; +	ret = spi_sync_transfer(st->sdev, &xfer, 1); +	if (ret < 0) +		return ret; +	st->old_data = true; + +	return st->rx[1]; +} + +static inline +int ad2s1210_update_frequency_control_word(struct ad2s1210_state *st) +{ +	int ret; +	unsigned char fcw; + +	fcw = (unsigned char)(st->fexcit * (1 << 15) / st->fclkin); +	if (fcw < AD2S1210_MIN_FCW || fcw > AD2S1210_MAX_FCW) { +		pr_err("ad2s1210: FCW out of range\n"); +		return -ERANGE; +	} + +	ret = ad2s1210_config_write(st, AD2S1210_REG_EXCIT_FREQ); +	if (ret < 0) +		return ret; + +	return ad2s1210_config_write(st, fcw); +} + +static unsigned char ad2s1210_read_resolution_pin(struct ad2s1210_state *st) +{ +	return ad2s1210_resolution_value[ +		(gpio_get_value(st->pdata->res[0]) << 1) | +		gpio_get_value(st->pdata->res[1])]; +} + +static const int ad2s1210_res_pins[4][2] = { +	{ 0, 0 }, {0, 1}, {1, 0}, {1, 1} +}; + +static inline void ad2s1210_set_resolution_pin(struct ad2s1210_state *st) +{ +	gpio_set_value(st->pdata->res[0], +		       ad2s1210_res_pins[(st->resolution - 10)/2][0]); +	gpio_set_value(st->pdata->res[1], +		       ad2s1210_res_pins[(st->resolution - 10)/2][1]); +} + +static inline int ad2s1210_soft_reset(struct ad2s1210_state *st) +{ +	int ret; + +	ret = ad2s1210_config_write(st, AD2S1210_REG_SOFT_RESET); +	if (ret < 0) +		return ret; + +	return ad2s1210_config_write(st, 0x0); +} + +static ssize_t ad2s1210_show_fclkin(struct device *dev, +				    struct device_attribute *attr, +				    char *buf) +{ +	struct ad2s1210_state *st = iio_priv(dev_to_iio_dev(dev)); +	return sprintf(buf, "%d\n", st->fclkin); +} + +static ssize_t ad2s1210_store_fclkin(struct device *dev, +				     struct device_attribute *attr, +				     const char *buf, +				     size_t len) +{ +	struct ad2s1210_state *st = iio_priv(dev_to_iio_dev(dev)); +	unsigned int fclkin; +	int ret; + +	ret = kstrtouint(buf, 10, &fclkin); +	if (ret) +		return ret; +	if (fclkin < AD2S1210_MIN_CLKIN || fclkin > AD2S1210_MAX_CLKIN) { +		pr_err("ad2s1210: fclkin out of range\n"); +		return -EINVAL; +	} + +	mutex_lock(&st->lock); +	st->fclkin = fclkin; + +	ret = ad2s1210_update_frequency_control_word(st); +	if (ret < 0) +		goto error_ret; +	ret = ad2s1210_soft_reset(st); +error_ret: +	mutex_unlock(&st->lock); + +	return ret < 0 ? ret : len; +} + +static ssize_t ad2s1210_show_fexcit(struct device *dev, +				    struct device_attribute *attr, +				    char *buf) +{ +	struct ad2s1210_state *st = iio_priv(dev_to_iio_dev(dev)); +	return sprintf(buf, "%d\n", st->fexcit); +} + +static ssize_t ad2s1210_store_fexcit(struct device *dev, +				     struct device_attribute *attr, +				     const char *buf, size_t len) +{ +	struct ad2s1210_state *st = iio_priv(dev_to_iio_dev(dev)); +	unsigned int fexcit; +	int ret; + +	ret = kstrtouint(buf, 10, &fexcit); +	if (ret < 0) +		return ret; +	if (fexcit < AD2S1210_MIN_EXCIT || fexcit > AD2S1210_MAX_EXCIT) { +		pr_err("ad2s1210: excitation frequency out of range\n"); +		return -EINVAL; +	} +	mutex_lock(&st->lock); +	st->fexcit = fexcit; +	ret = ad2s1210_update_frequency_control_word(st); +	if (ret < 0) +		goto error_ret; +	ret = ad2s1210_soft_reset(st); +error_ret: +	mutex_unlock(&st->lock); + +	return ret < 0 ? ret : len; +} + +static ssize_t ad2s1210_show_control(struct device *dev, +				     struct device_attribute *attr, +				     char *buf) +{ +	struct ad2s1210_state *st = iio_priv(dev_to_iio_dev(dev)); +	int ret; +	mutex_lock(&st->lock); +	ret = ad2s1210_config_read(st, AD2S1210_REG_CONTROL); +	mutex_unlock(&st->lock); +	return ret < 0 ? ret : sprintf(buf, "0x%x\n", ret); +} + +static ssize_t ad2s1210_store_control(struct device *dev, +			struct device_attribute *attr, +			const char *buf, size_t len) +{ +	struct ad2s1210_state *st = iio_priv(dev_to_iio_dev(dev)); +	unsigned char udata; +	unsigned char data; +	int ret; + +	ret = kstrtou8(buf, 16, &udata); +	if (ret) +		return -EINVAL; + +	mutex_lock(&st->lock); +	ret = ad2s1210_config_write(st, AD2S1210_REG_CONTROL); +	if (ret < 0) +		goto error_ret; +	data = udata & AD2S1210_MSB_IS_LOW; +	ret = ad2s1210_config_write(st, data); +	if (ret < 0) +		goto error_ret; + +	ret = ad2s1210_config_read(st, AD2S1210_REG_CONTROL); +	if (ret < 0) +		goto error_ret; +	if (ret & AD2S1210_MSB_IS_HIGH) { +		ret = -EIO; +		pr_err("ad2s1210: write control register fail\n"); +		goto error_ret; +	} +	st->resolution +		= ad2s1210_resolution_value[data & AD2S1210_SET_RESOLUTION]; +	if (st->pdata->gpioin) { +		data = ad2s1210_read_resolution_pin(st); +		if (data != st->resolution) +			pr_warn("ad2s1210: resolution settings not match\n"); +	} else +		ad2s1210_set_resolution_pin(st); + +	ret = len; +	st->hysteresis = !!(data & AD2S1210_ENABLE_HYSTERESIS); + +error_ret: +	mutex_unlock(&st->lock); +	return ret; +} + +static ssize_t ad2s1210_show_resolution(struct device *dev, +			struct device_attribute *attr, char *buf) +{ +	struct ad2s1210_state *st = iio_priv(dev_to_iio_dev(dev)); +	return sprintf(buf, "%d\n", st->resolution); +} + +static ssize_t ad2s1210_store_resolution(struct device *dev, +			struct device_attribute *attr, +			const char *buf, size_t len) +{ +	struct ad2s1210_state *st = iio_priv(dev_to_iio_dev(dev)); +	unsigned char data; +	unsigned char udata; +	int ret; + +	ret = kstrtou8(buf, 10, &udata); +	if (ret || udata < 10 || udata > 16) { +		pr_err("ad2s1210: resolution out of range\n"); +		return -EINVAL; +	} +	mutex_lock(&st->lock); +	ret = ad2s1210_config_read(st, AD2S1210_REG_CONTROL); +	if (ret < 0) +		goto error_ret; +	data = ret; +	data &= ~AD2S1210_SET_RESOLUTION; +	data |= (udata - 10) >> 1; +	ret = ad2s1210_config_write(st, AD2S1210_REG_CONTROL); +	if (ret < 0) +		goto error_ret; +	ret = ad2s1210_config_write(st, data & AD2S1210_MSB_IS_LOW); +	if (ret < 0) +		goto error_ret; +	ret = ad2s1210_config_read(st, AD2S1210_REG_CONTROL); +	if (ret < 0) +		goto error_ret; +	data = ret; +	if (data & AD2S1210_MSB_IS_HIGH) { +		ret = -EIO; +		pr_err("ad2s1210: setting resolution fail\n"); +		goto error_ret; +	} +	st->resolution +		= ad2s1210_resolution_value[data & AD2S1210_SET_RESOLUTION]; +	if (st->pdata->gpioin) { +		data = ad2s1210_read_resolution_pin(st); +		if (data != st->resolution) +			pr_warn("ad2s1210: resolution settings not match\n"); +	} else +		ad2s1210_set_resolution_pin(st); +	ret = len; +error_ret: +	mutex_unlock(&st->lock); +	return ret; +} + +/* read the fault register since last sample */ +static ssize_t ad2s1210_show_fault(struct device *dev, +			struct device_attribute *attr, char *buf) +{ +	struct ad2s1210_state *st = iio_priv(dev_to_iio_dev(dev)); +	int ret; + +	mutex_lock(&st->lock); +	ret = ad2s1210_config_read(st, AD2S1210_REG_FAULT); +	mutex_unlock(&st->lock); + +	return ret ? ret : sprintf(buf, "0x%x\n", ret); +} + +static ssize_t ad2s1210_clear_fault(struct device *dev, +				    struct device_attribute *attr, +				    const char *buf, +				    size_t len) +{ +	struct ad2s1210_state *st = iio_priv(dev_to_iio_dev(dev)); +	int ret; + +	mutex_lock(&st->lock); +	gpio_set_value(st->pdata->sample, 0); +	/* delay (2 * tck + 20) nano seconds */ +	udelay(1); +	gpio_set_value(st->pdata->sample, 1); +	ret = ad2s1210_config_read(st, AD2S1210_REG_FAULT); +	if (ret < 0) +		goto error_ret; +	gpio_set_value(st->pdata->sample, 0); +	gpio_set_value(st->pdata->sample, 1); +error_ret: +	mutex_unlock(&st->lock); + +	return ret < 0 ? ret : len; +} + +static ssize_t ad2s1210_show_reg(struct device *dev, +				 struct device_attribute *attr, +				 char *buf) +{ +	struct ad2s1210_state *st = iio_priv(dev_to_iio_dev(dev)); +	struct iio_dev_attr *iattr = to_iio_dev_attr(attr); +	int ret; + +	mutex_lock(&st->lock); +	ret = ad2s1210_config_read(st, iattr->address); +	mutex_unlock(&st->lock); + +	return ret < 0 ? ret : sprintf(buf, "%d\n", ret); +} + +static ssize_t ad2s1210_store_reg(struct device *dev, +		struct device_attribute *attr, const char *buf, size_t len) +{ +	struct ad2s1210_state *st = iio_priv(dev_to_iio_dev(dev)); +	unsigned char data; +	int ret; +	struct iio_dev_attr *iattr = to_iio_dev_attr(attr); + +	ret = kstrtou8(buf, 10, &data); +	if (ret) +		return -EINVAL; +	mutex_lock(&st->lock); +	ret = ad2s1210_config_write(st, iattr->address); +	if (ret < 0) +		goto error_ret; +	ret = ad2s1210_config_write(st, data & AD2S1210_MSB_IS_LOW); +error_ret: +	mutex_unlock(&st->lock); +	return ret < 0 ? ret : len; +} + +static int ad2s1210_read_raw(struct iio_dev *indio_dev, +			     struct iio_chan_spec const *chan, +			     int *val, +			     int *val2, +			     long m) +{ +	struct ad2s1210_state *st = iio_priv(indio_dev); +	bool negative; +	int ret = 0; +	u16 pos; +	s16 vel; + +	mutex_lock(&st->lock); +	gpio_set_value(st->pdata->sample, 0); +	/* delay (6 * tck + 20) nano seconds */ +	udelay(1); + +	switch (chan->type) { +	case IIO_ANGL: +		ad2s1210_set_mode(MOD_POS, st); +		break; +	case IIO_ANGL_VEL: +		ad2s1210_set_mode(MOD_VEL, st); +		break; +	default: +	       ret = -EINVAL; +	       break; +	} +	if (ret < 0) +		goto error_ret; +	ret = spi_read(st->sdev, st->rx, 2); +	if (ret < 0) +		goto error_ret; + +	switch (chan->type) { +	case IIO_ANGL: +		pos = be16_to_cpup((u16 *)st->rx); +		if (st->hysteresis) +			pos >>= 16 - st->resolution; +		*val = pos; +		ret = IIO_VAL_INT; +		break; +	case IIO_ANGL_VEL: +		negative = st->rx[0] & 0x80; +		vel = be16_to_cpup((s16 *)st->rx); +		vel >>= 16 - st->resolution; +		if (vel & 0x8000) { +			negative = (0xffff >> st->resolution) << st->resolution; +			vel |= negative; +		} +		*val = vel; +		ret = IIO_VAL_INT; +		break; +	default: +		mutex_unlock(&st->lock); +		return -EINVAL; +	} + +error_ret: +	gpio_set_value(st->pdata->sample, 1); +	/* delay (2 * tck + 20) nano seconds */ +	udelay(1); +	mutex_unlock(&st->lock); +	return ret; +} + +static IIO_DEVICE_ATTR(fclkin, S_IRUGO | S_IWUSR, +		       ad2s1210_show_fclkin, ad2s1210_store_fclkin, 0); +static IIO_DEVICE_ATTR(fexcit, S_IRUGO | S_IWUSR, +		       ad2s1210_show_fexcit,	ad2s1210_store_fexcit, 0); +static IIO_DEVICE_ATTR(control, S_IRUGO | S_IWUSR, +		       ad2s1210_show_control, ad2s1210_store_control, 0); +static IIO_DEVICE_ATTR(bits, S_IRUGO | S_IWUSR, +		       ad2s1210_show_resolution, ad2s1210_store_resolution, 0); +static IIO_DEVICE_ATTR(fault, S_IRUGO | S_IWUSR, +		       ad2s1210_show_fault, ad2s1210_clear_fault, 0); + +static IIO_DEVICE_ATTR(los_thrd, S_IRUGO | S_IWUSR, +		       ad2s1210_show_reg, ad2s1210_store_reg, +		       AD2S1210_REG_LOS_THRD); +static IIO_DEVICE_ATTR(dos_ovr_thrd, S_IRUGO | S_IWUSR, +		       ad2s1210_show_reg, ad2s1210_store_reg, +		       AD2S1210_REG_DOS_OVR_THRD); +static IIO_DEVICE_ATTR(dos_mis_thrd, S_IRUGO | S_IWUSR, +		       ad2s1210_show_reg, ad2s1210_store_reg, +		       AD2S1210_REG_DOS_MIS_THRD); +static IIO_DEVICE_ATTR(dos_rst_max_thrd, S_IRUGO | S_IWUSR, +		       ad2s1210_show_reg, ad2s1210_store_reg, +		       AD2S1210_REG_DOS_RST_MAX_THRD); +static IIO_DEVICE_ATTR(dos_rst_min_thrd, S_IRUGO | S_IWUSR, +		       ad2s1210_show_reg, ad2s1210_store_reg, +		       AD2S1210_REG_DOS_RST_MIN_THRD); +static IIO_DEVICE_ATTR(lot_high_thrd, S_IRUGO | S_IWUSR, +		       ad2s1210_show_reg, ad2s1210_store_reg, +		       AD2S1210_REG_LOT_HIGH_THRD); +static IIO_DEVICE_ATTR(lot_low_thrd, S_IRUGO | S_IWUSR, +		       ad2s1210_show_reg, ad2s1210_store_reg, +		       AD2S1210_REG_LOT_LOW_THRD); + + +static const struct iio_chan_spec ad2s1210_channels[] = { +	{ +		.type = IIO_ANGL, +		.indexed = 1, +		.channel = 0, +		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), +	}, { +		.type = IIO_ANGL_VEL, +		.indexed = 1, +		.channel = 0, +		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), +	} +}; + +static struct attribute *ad2s1210_attributes[] = { +	&iio_dev_attr_fclkin.dev_attr.attr, +	&iio_dev_attr_fexcit.dev_attr.attr, +	&iio_dev_attr_control.dev_attr.attr, +	&iio_dev_attr_bits.dev_attr.attr, +	&iio_dev_attr_fault.dev_attr.attr, +	&iio_dev_attr_los_thrd.dev_attr.attr, +	&iio_dev_attr_dos_ovr_thrd.dev_attr.attr, +	&iio_dev_attr_dos_mis_thrd.dev_attr.attr, +	&iio_dev_attr_dos_rst_max_thrd.dev_attr.attr, +	&iio_dev_attr_dos_rst_min_thrd.dev_attr.attr, +	&iio_dev_attr_lot_high_thrd.dev_attr.attr, +	&iio_dev_attr_lot_low_thrd.dev_attr.attr, +	NULL, +}; + +static const struct attribute_group ad2s1210_attribute_group = { +	.attrs = ad2s1210_attributes, +}; + +static int ad2s1210_initial(struct ad2s1210_state *st) +{ +	unsigned char data; +	int ret; + +	mutex_lock(&st->lock); +	if (st->pdata->gpioin) +		st->resolution = ad2s1210_read_resolution_pin(st); +	else +		ad2s1210_set_resolution_pin(st); + +	ret = ad2s1210_config_write(st, AD2S1210_REG_CONTROL); +	if (ret < 0) +		goto error_ret; +	data = AD2S1210_DEF_CONTROL & ~(AD2S1210_SET_RESOLUTION); +	data |= (st->resolution - 10) >> 1; +	ret = ad2s1210_config_write(st, data); +	if (ret < 0) +		goto error_ret; +	ret = ad2s1210_config_read(st, AD2S1210_REG_CONTROL); +	if (ret < 0) +		goto error_ret; + +	if (ret & AD2S1210_MSB_IS_HIGH) { +		ret = -EIO; +		goto error_ret; +	} + +	ret = ad2s1210_update_frequency_control_word(st); +	if (ret < 0) +		goto error_ret; +	ret = ad2s1210_soft_reset(st); +error_ret: +	mutex_unlock(&st->lock); +	return ret; +} + +static const struct iio_info ad2s1210_info = { +	.read_raw = &ad2s1210_read_raw, +	.attrs = &ad2s1210_attribute_group, +	.driver_module = THIS_MODULE, +}; + +static int ad2s1210_setup_gpios(struct ad2s1210_state *st) +{ +	unsigned long flags = st->pdata->gpioin ? GPIOF_DIR_IN : GPIOF_DIR_OUT; +	struct gpio ad2s1210_gpios[] = { +		{ st->pdata->sample, GPIOF_DIR_IN, "sample" }, +		{ st->pdata->a[0], flags, "a0" }, +		{ st->pdata->a[1], flags, "a1" }, +		{ st->pdata->res[0], flags, "res0" }, +		{ st->pdata->res[0], flags, "res1" }, +	}; + +	return gpio_request_array(ad2s1210_gpios, ARRAY_SIZE(ad2s1210_gpios)); +} + +static void ad2s1210_free_gpios(struct ad2s1210_state *st) +{ +	unsigned long flags = st->pdata->gpioin ? GPIOF_DIR_IN : GPIOF_DIR_OUT; +	struct gpio ad2s1210_gpios[] = { +		{ st->pdata->sample, GPIOF_DIR_IN, "sample" }, +		{ st->pdata->a[0], flags, "a0" }, +		{ st->pdata->a[1], flags, "a1" }, +		{ st->pdata->res[0], flags, "res0" }, +		{ st->pdata->res[0], flags, "res1" }, +	}; + +	gpio_free_array(ad2s1210_gpios, ARRAY_SIZE(ad2s1210_gpios)); +} + +static int ad2s1210_probe(struct spi_device *spi) +{ +	struct iio_dev *indio_dev; +	struct ad2s1210_state *st; +	int ret; + +	if (spi->dev.platform_data == NULL) +		return -EINVAL; + +	indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); +	if (!indio_dev) +		return -ENOMEM; +	st = iio_priv(indio_dev); +	st->pdata = spi->dev.platform_data; +	ret = ad2s1210_setup_gpios(st); +	if (ret < 0) +		return ret; + +	spi_set_drvdata(spi, indio_dev); + +	mutex_init(&st->lock); +	st->sdev = spi; +	st->hysteresis = true; +	st->mode = MOD_CONFIG; +	st->resolution = 12; +	st->fexcit = AD2S1210_DEF_EXCIT; + +	indio_dev->dev.parent = &spi->dev; +	indio_dev->info = &ad2s1210_info; +	indio_dev->modes = INDIO_DIRECT_MODE; +	indio_dev->channels = ad2s1210_channels; +	indio_dev->num_channels = ARRAY_SIZE(ad2s1210_channels); +	indio_dev->name = spi_get_device_id(spi)->name; + +	ret = iio_device_register(indio_dev); +	if (ret) +		goto error_free_gpios; + +	st->fclkin = spi->max_speed_hz; +	spi->mode = SPI_MODE_3; +	spi_setup(spi); +	ad2s1210_initial(st); + +	return 0; + +error_free_gpios: +	ad2s1210_free_gpios(st); +	return ret; +} + +static int ad2s1210_remove(struct spi_device *spi) +{ +	struct iio_dev *indio_dev = spi_get_drvdata(spi); + +	iio_device_unregister(indio_dev); +	ad2s1210_free_gpios(iio_priv(indio_dev)); + +	return 0; +} + +static const struct spi_device_id ad2s1210_id[] = { +	{ "ad2s1210" }, +	{} +}; +MODULE_DEVICE_TABLE(spi, ad2s1210_id); + +static struct spi_driver ad2s1210_driver = { +	.driver = { +		.name = DRV_NAME, +		.owner = THIS_MODULE, +	}, +	.probe = ad2s1210_probe, +	.remove = ad2s1210_remove, +	.id_table = ad2s1210_id, +}; +module_spi_driver(ad2s1210_driver); + +MODULE_AUTHOR("Graff Yang <graff.yang@gmail.com>"); +MODULE_DESCRIPTION("Analog Devices AD2S1210 Resolver to Digital SPI driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/iio/resolver/ad2s1210.h b/drivers/staging/iio/resolver/ad2s1210.h new file mode 100644 index 00000000000..aec0bdca16a --- /dev/null +++ b/drivers/staging/iio/resolver/ad2s1210.h @@ -0,0 +1,17 @@ +/* + * ad2s1210.h plaform data for the ADI Resolver to Digital Converters: + * AD2S1210 + * + * Copyright (c) 2010-2010 Analog Devices Inc. + * + * 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. + */ + +struct ad2s1210_platform_data { +	unsigned sample; +	unsigned a[2]; +	unsigned res[2]; +	bool gpioin; +}; diff --git a/drivers/staging/iio/resolver/ad2s90.c b/drivers/staging/iio/resolver/ad2s90.c new file mode 100644 index 00000000000..e24c5890652 --- /dev/null +++ b/drivers/staging/iio/resolver/ad2s90.c @@ -0,0 +1,120 @@ +/* + * ad2s90.c simple support for the ADI Resolver to Digital Converters: AD2S90 + * + * Copyright (c) 2010-2010 Analog Devices Inc. + * + * 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/types.h> +#include <linux/mutex.h> +#include <linux/device.h> +#include <linux/spi/spi.h> +#include <linux/slab.h> +#include <linux/sysfs.h> +#include <linux/module.h> + +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> + +struct ad2s90_state { +	struct mutex lock; +	struct spi_device *sdev; +	u8 rx[2] ____cacheline_aligned; +}; + +static int ad2s90_read_raw(struct iio_dev *indio_dev, +			   struct iio_chan_spec const *chan, +			   int *val, +			   int *val2, +			   long m) +{ +	int ret; +	struct ad2s90_state *st = iio_priv(indio_dev); + +	mutex_lock(&st->lock); +	ret = spi_read(st->sdev, st->rx, 2); +	if (ret) +		goto error_ret; +	*val = (((u16)(st->rx[0])) << 4) | ((st->rx[1] & 0xF0) >> 4); + +error_ret: +	mutex_unlock(&st->lock); + +	return IIO_VAL_INT; +} + +static const struct iio_info ad2s90_info = { +	.read_raw = &ad2s90_read_raw, +	.driver_module = THIS_MODULE, +}; + +static const struct iio_chan_spec ad2s90_chan = { +	.type = IIO_ANGL, +	.indexed = 1, +	.channel = 0, +	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), +}; + +static int ad2s90_probe(struct spi_device *spi) +{ +	struct iio_dev *indio_dev; +	struct ad2s90_state *st; +	int ret = 0; + +	indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); +	if (!indio_dev) +		return -ENOMEM; +	st = iio_priv(indio_dev); +	spi_set_drvdata(spi, indio_dev); + +	mutex_init(&st->lock); +	st->sdev = spi; +	indio_dev->dev.parent = &spi->dev; +	indio_dev->info = &ad2s90_info; +	indio_dev->modes = INDIO_DIRECT_MODE; +	indio_dev->channels = &ad2s90_chan; +	indio_dev->num_channels = 1; +	indio_dev->name = spi_get_device_id(spi)->name; + +	ret = iio_device_register(indio_dev); +	if (ret) +		return ret; + +	/* need 600ns between CS and the first falling edge of SCLK */ +	spi->max_speed_hz = 830000; +	spi->mode = SPI_MODE_3; +	spi_setup(spi); + +	return 0; +} + +static int ad2s90_remove(struct spi_device *spi) +{ +	iio_device_unregister(spi_get_drvdata(spi)); + +	return 0; +} + +static const struct spi_device_id ad2s90_id[] = { +	{ "ad2s90" }, +	{} +}; +MODULE_DEVICE_TABLE(spi, ad2s90_id); + +static struct spi_driver ad2s90_driver = { +	.driver = { +		.name = "ad2s90", +		.owner = THIS_MODULE, +	}, +	.probe = ad2s90_probe, +	.remove = ad2s90_remove, +	.id_table = ad2s90_id, +}; +module_spi_driver(ad2s90_driver); + +MODULE_AUTHOR("Graff Yang <graff.yang@gmail.com>"); +MODULE_DESCRIPTION("Analog Devices AD2S90 Resolver to Digital SPI driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/iio/ring_generic.h b/drivers/staging/iio/ring_generic.h deleted file mode 100644 index 8ecb1895cec..00000000000 --- a/drivers/staging/iio/ring_generic.h +++ /dev/null @@ -1,489 +0,0 @@ -/* The industrial I/O core - generic ring buffer interfaces. - * - * Copyright (c) 2008 Jonathan Cameron - * - * 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 _IIO_RING_GENERIC_H_ -#define _IIO_RING_GENERIC_H_ -#include "iio.h" - -#ifdef CONFIG_IIO_RING_BUFFER - -struct iio_ring_buffer; - -/** - * iio_push_ring_event() - ring buffer specific push to event chrdev - * @ring_buf:		ring buffer that is the event source - * @event_code:		event indentification code - * @timestamp:		time of event - **/ -int iio_push_ring_event(struct iio_ring_buffer *ring_buf, -			int event_code, -			s64 timestamp); -/** - * iio_push_or_escallate_ring_event() -	escalate or add as appropriate - * @ring_buf:		ring buffer that is the event source - * @event_code:		event indentification code - * @timestamp:		time of event - * - * Typical usecase is to escalate a 50% ring full to 75% full if noone has yet - * read the first event. Clearly the 50% full is no longer of interest in - * typical use case. - **/ -int iio_push_or_escallate_ring_event(struct iio_ring_buffer *ring_buf, -				     int event_code, -				     s64 timestamp); - -/** - * struct iio_ring_access_funcs - access functions for ring buffers. - * @mark_in_use:	reference counting, typically to prevent module removal - * @unmark_in_use:	reduce reference count when no longer using ring buffer - * @store_to:		actually store stuff to the ring buffer - * @read_last:		get the last element stored - * @rip_lots:		try to get a specified number of elements (must exist) - * @mark_param_change:	notify ring that some relevant parameter has changed - *			Often this means the underlying storage may need to - *			change. - * @request_update:	if a parameter change has been marked, update underlying - *			storage. - * @get_bytes_per_datum:get current bytes per datum - * @set_bytes_per_datum:set number of bytes per datum - * @get_length:		get number of datums in ring - * @set_length:		set number of datums in ring - * @is_enabled:		query if ring is currently being used - * @enable:		enable the ring - * - * The purpose of this structure is to make the ring buffer element - * modular as event for a given driver, different usecases may require - * different ring designs (space efficiency vs speed for example). - * - * It is worth noting that a given ring implementation may only support a small - * proportion of these functions.  The core code 'should' cope fine with any of - * them not existing. - **/ -struct iio_ring_access_funcs { -	void (*mark_in_use)(struct iio_ring_buffer *ring); -	void (*unmark_in_use)(struct iio_ring_buffer *ring); - -	int (*store_to)(struct iio_ring_buffer *ring, u8 *data, s64 timestamp); -	int (*read_last)(struct iio_ring_buffer *ring, u8 *data); -	int (*rip_lots)(struct iio_ring_buffer *ring, -			size_t count, -			u8 **data, -			int *dead_offset); - -	int (*mark_param_change)(struct iio_ring_buffer *ring); -	int (*request_update)(struct iio_ring_buffer *ring); - -	int (*get_bytes_per_datum)(struct iio_ring_buffer *ring); -	int (*set_bytes_per_datum)(struct iio_ring_buffer *ring, size_t bpd); -	int (*get_length)(struct iio_ring_buffer *ring); -	int (*set_length)(struct iio_ring_buffer *ring, int length); - -	int (*is_enabled)(struct iio_ring_buffer *ring); -	int (*enable)(struct iio_ring_buffer *ring); -}; - -/** - * struct iio_ring_buffer - general ring buffer structure - * @dev:		ring buffer device struct - * @access_dev:		system device struct for the chrdev - * @indio_dev:		industrial I/O device structure - * @owner:		module that owns the ring buffer (for ref counting) - * @id:			unique id number - * @access_id:		device id number - * @length:		[DEVICE] number of datums in ring - * @bytes_per_datum:	[DEVICE] size of individual datum including timestamp - * @bpe:		[DEVICE] size of individual channel value - * @loopcount:		[INTERN] number of times the ring has looped - * @scan_el_attrs:	[DRIVER] control of scan elements if that scan mode - *			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 - * @scan_timestamp:	[INTERN] does the scan mode include a timestamp - * @access_handler:	[INTERN] chrdev access handling - * @ev_int:		[INTERN] chrdev interface for the event chrdev - * @shared_ev_pointer:	[INTERN] the shared event pointer to allow escalation of - *			events - * @access:		[DRIVER] ring access functions associated with the - *			implementation. - * @preenable:		[DRIVER] function to run prior to marking ring enabled - * @postenable:		[DRIVER] function to run after marking ring enabled - * @predisable:		[DRIVER] function to run prior to marking ring disabled - * @postdisable:	[DRIVER] function to run after marking ring disabled -  **/ -struct iio_ring_buffer { -	struct device dev; -	struct device access_dev; -	struct iio_dev *indio_dev; -	struct module *owner; -	int				id; -	int				access_id; -	int				length; -	int				bytes_per_datum; -	int				bpe; -	int				loopcount; -	struct attribute_group		*scan_el_attrs; -	int				scan_count; -	u32				scan_mask; -	bool				scan_timestamp; -	struct iio_handler		access_handler; -	struct iio_event_interface	ev_int; -	struct iio_shared_ev_pointer	shared_ev_pointer; -	struct iio_ring_access_funcs	access; -	int				(*preenable)(struct iio_dev *); -	int				(*postenable)(struct iio_dev *); -	int				(*predisable)(struct iio_dev *); -	int				(*postdisable)(struct iio_dev *); - -}; - -/** - * iio_ring_buffer_init() - Initialize the buffer structure - * @ring: buffer to be initialized - * @dev_info: the iio device the buffer is assocated with - **/ -void iio_ring_buffer_init(struct iio_ring_buffer *ring, -			  struct iio_dev *dev_info); - -/** - * __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_update_ring_buffer(struct iio_ring_buffer *ring, -					    int bytes_per_datum, int length) -{ -	ring->bytes_per_datum = bytes_per_datum; -	ring->length = length; -	ring->loopcount = 0; -} - -/** - * struct iio_scan_el - an individual element of a scan - * @dev_attr:		control attribute (if directly controllable) - * @number:		unique identifier of element (used for bit mask) - * @label:		useful data for the scan el (often reg address) - * @set_state:		for some devices datardy signals are generated - *			for any enabled lines.  This allows unwanted lines - *			to be disabled and hence not get in the way. - **/ -struct iio_scan_el { -	struct device_attribute		dev_attr; -	unsigned int			number; -	unsigned int			label; - -	int (*set_state)(struct iio_scan_el *scanel, -			 struct iio_dev *dev_info, -			 bool state); -}; - -#define to_iio_scan_el(_dev_attr)				\ -	container_of(_dev_attr, struct iio_scan_el, dev_attr); - -/** - * iio_scan_el_store() - sysfs scan element selection interface - * @dev: the target device - * @attr: the device attribute that is being processed - * @buf: input from userspace - * @len: length of input - * - * A generic function used to enable various scan elements.  In some - * devices explicit read commands for each channel mean this is merely - * a software switch.  In others this must actively disable the channel. - * Complexities occur when this interacts with data ready type triggers - * which may not reset unless every channel that is enabled is explicitly - * read. - **/ -ssize_t iio_scan_el_store(struct device *dev, struct device_attribute *attr, -			  const char *buf, size_t len); -/** - * iio_scan_el_show() -	sysfs interface to query whether a scan element - *			is enabled or not - * @dev: the target device - * @attr: the device attribute that is being processed - * @buf: output buffer - **/ -ssize_t iio_scan_el_show(struct device *dev, struct device_attribute *attr, -			 char *buf); - -/** - * iio_scan_el_ts_store() - sysfs interface to set whether a timestamp is included - *			    in the scan. - **/ -ssize_t iio_scan_el_ts_store(struct device *dev, struct device_attribute *attr, -			     const char *buf, size_t len); -/** - * iio_scan_el_ts_show() - sysfs interface to query if a timestamp is included - *			   in the scan. - **/ -ssize_t iio_scan_el_ts_show(struct device *dev, struct device_attribute *attr, -			    char *buf); -/** - * 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, _name##_en. - * @_number:	unique id number for the scan element. - *		length devices). - * @_label:	indentification variable used by drivers.  Often a reg address. - * @_controlfunc: function used to notify hardware of whether state changes - **/ -#define __IIO_SCAN_EL_C(_name, _number, _label, _controlfunc)	\ -	struct iio_scan_el iio_scan_el_##_name = {			\ -		.dev_attr = __ATTR(_name##_en,				\ -				   S_IRUGO | S_IWUSR,			\ -				   iio_scan_el_show,			\ -				   iio_scan_el_store),			\ -		.number =  _number,					\ -		.label = _label,					\ -		.set_state = _controlfunc,				\ -	};								\ -	static IIO_CONST_ATTR(_name##_index, #_number) - -#define IIO_SCAN_EL_C(_name, _number, _label, _controlfunc)	\ -	__IIO_SCAN_EL_C(_name, _number, _label, _controlfunc) - -#define __IIO_SCAN_NAMED_EL_C(_name, _string, _number, _label, _cf)	\ -	struct iio_scan_el iio_scan_el_##_name = {			\ -		.dev_attr = __ATTR(_string##_en,			\ -				   S_IRUGO | S_IWUSR,			\ -				   iio_scan_el_show,			\ -				   iio_scan_el_store),			\ -		.number =  _number,					\ -		.label = _label,					\ -		.set_state = _cf,					\ -	};								\ -	static struct iio_const_attr iio_const_attr_##_name##_index = {	\ -		.string = #_number,					\ -		.dev_attr = __ATTR(_string##_index,			\ -				   S_IRUGO, iio_read_const_attr, NULL)	\ -	} - - -#define IIO_SCAN_NAMED_EL_C(_name, _string, _number, _label, _cf) \ -	__IIO_SCAN_NAMED_EL_C(_name, _string, _number, _label, _cf) -/** - * IIO_SCAN_EL_TIMESTAMP - declare a special scan element for timestamps - * @number: specify where in the scan order this is stored. - * - * Odd one out. Handled slightly differently from other scan elements. - **/ -#define IIO_SCAN_EL_TIMESTAMP(number)				\ -	struct iio_scan_el iio_scan_el_timestamp = {		\ -		.dev_attr = __ATTR(timestamp_en,		\ -				   S_IRUGO | S_IWUSR,		\ -				   iio_scan_el_ts_show,		\ -				   iio_scan_el_ts_store),	\ -	};							\ -	static IIO_CONST_ATTR(timestamp_index, #number) - -/** - * IIO_CONST_ATTR_SCAN_EL_TYPE - attr to specify the data format of a scan el - * @name: the scan el name (may be more general and cover a set of scan elements - * @_sign: either s or u for signed or unsigned - * @_bits: number of actual bits occuplied by the value - * @_storagebits: number of bits _bits is padded to when read out of buffer - **/ -#define IIO_CONST_ATTR_SCAN_EL_TYPE(_name, _sign, _bits, _storagebits) \ -	IIO_CONST_ATTR(_name##_type, #_sign#_bits"/"#_storagebits); - -/** - * IIO_CONST_ATTR_SCAN_EL_TYPE_WITH_SHIFT - attr to specify the data format of a scan el - * @name: the scan el name (may be more general and cover a set of scan elements - * @_sign: either s or u for signed or unsigned - * @_bits: number of actual bits occuplied by the value - * @_storagebits: number of bits _bits is padded to when read out of buffer - * @_shiftbits: number of bits _shiftbits the result must be shifted - **/ -#define IIO_CONST_ATTR_SCAN_EL_TYPE_WITH_SHIFT(_name, _sign, _bits, \ -					       _storagebits, _shiftbits) \ -	IIO_CONST_ATTR(_name##_type, #_sign#_bits"/"#_storagebits \ -		       ">>"#_shiftbits); - -#define IIO_SCAN_EL_TYPE_SIGNED         's' -#define IIO_SCAN_EL_TYPE_UNSIGNED       'u' - -/* - * 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 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_ring_buffer *ring, int bit) -{ -	struct iio_dev *dev_info = ring->indio_dev; -	u32 mask; - -	if (bit > IIO_MAX_SCAN_LENGTH) -		return -EINVAL; - -	if (!ring->scan_mask) -		return 0; - -	if (dev_info->available_scan_masks) -		mask = iio_scan_mask_match(dev_info->available_scan_masks, -					ring->scan_mask); -	else -		mask = ring->scan_mask; - -	if (!mask) -		return -EINVAL; - -	return !!(mask & (1 << bit)); -}; - -/** - * iio_scan_mask_set() - set particular bit in the scan mask - * @ring: the ring buffer whose scan mask we are interested in - * @bit: the bit to be set. - **/ -static inline int iio_scan_mask_set(struct iio_ring_buffer *ring, int bit) -{ -	struct iio_dev *dev_info = ring->indio_dev; -	u32 mask; -	u32 trialmask = ring->scan_mask | (1 << bit); - -	if (bit > IIO_MAX_SCAN_LENGTH) -		return -EINVAL; -	if (dev_info->available_scan_masks) { -		mask = iio_scan_mask_match(dev_info->available_scan_masks, -					trialmask); -		if (!mask) -			return -EINVAL; -	} -	ring->scan_mask = trialmask; -	ring->scan_count++; - -	return 0; -}; - -/** - * iio_scan_mask_clear() - clear a particular element from the scan mask - * @ring: the ring buffer whose scan mask we are interested in - * @bit: the bit to clear - **/ -static inline int iio_scan_mask_clear(struct iio_ring_buffer *ring, int bit) -{ -	if (bit > IIO_MAX_SCAN_LENGTH) -		return -EINVAL; -	ring->scan_mask &= ~(1 << bit); -	ring->scan_count--; -	return 0; -}; - -/** - * iio_scan_mask_count_to_right() - how many scan elements occur before here - * @ring: the ring buffer whose scan mask we interested in - * @bit: which number scan element is this - **/ -static inline int iio_scan_mask_count_to_right(struct iio_ring_buffer *ring, -						int bit) -{ -	int count = 0; -	int mask = (1 << bit); -	if (bit > IIO_MAX_SCAN_LENGTH) -		return -EINVAL; -	while (mask) { -		mask >>= 1; -		if (mask & ring->scan_mask) -			count++; -	} - -	return count; -} - -/** - * iio_put_ring_buffer() - notify done with buffer - * @ring: the buffer we are done with. - **/ -static inline void iio_put_ring_buffer(struct iio_ring_buffer *ring) -{ -	put_device(&ring->dev); -}; - -#define to_iio_ring_buffer(d)			\ -	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) - -/** - * iio_ring_buffer_register() - register the buffer with IIO core - * @ring: the buffer to be registered - * @id: the id of the buffer (typically 0) - **/ -int iio_ring_buffer_register(struct iio_ring_buffer *ring, int id); - -/** - * iio_ring_buffer_unregister() - unregister the buffer from IIO core - * @ring: the buffer to be unregistered - **/ -void iio_ring_buffer_unregister(struct iio_ring_buffer *ring); - -/** - * iio_read_ring_length() - attr func to get number of datums in the buffer - **/ -ssize_t iio_read_ring_length(struct device *dev, -			     struct device_attribute *attr, -			     char *buf); -/** - * iio_write_ring_length() - attr func to set number of datums in the buffer - **/ -ssize_t iio_write_ring_length(struct device *dev, -			      struct device_attribute *attr, -			      const char *buf, -			      size_t len); -/** - * iio_read_ring_bytes_per_datum() - attr for number of bytes in whole datum - **/ -ssize_t iio_read_ring_bytes_per_datum(struct device *dev, -			  struct device_attribute *attr, -			  char *buf); -/** - * iio_store_ring_enable() - attr to turn the buffer on - **/ -ssize_t iio_store_ring_enable(struct device *dev, -			      struct device_attribute *attr, -			      const char *buf, -			      size_t len); -/** - * iio_show_ring_enable() - attr to see if the buffer is on - **/ -ssize_t iio_show_ring_enable(struct device *dev, -			     struct device_attribute *attr, -			     char *buf); -#define IIO_RING_LENGTH_ATTR DEVICE_ATTR(length, S_IRUGO | S_IWUSR,	\ -					 iio_read_ring_length,		\ -					 iio_write_ring_length) -#define IIO_RING_BYTES_PER_DATUM_ATTR DEVICE_ATTR(bytes_per_datum, S_IRUGO | S_IWUSR,	\ -				      iio_read_ring_bytes_per_datum, NULL) -#define IIO_RING_ENABLE_ATTR DEVICE_ATTR(enable, S_IRUGO | S_IWUSR, \ -					 iio_show_ring_enable,		\ -					 iio_store_ring_enable) -#else /* CONFIG_IIO_RING_BUFFER */ -static inline int iio_ring_buffer_register(struct iio_ring_buffer *ring, int id) -{ -	return 0; -}; -static inline void iio_ring_buffer_unregister(struct iio_ring_buffer *ring) -{}; - -#endif /* CONFIG_IIO_RING_BUFFER */ - -#endif /* _IIO_RING_GENERIC_H_ */ diff --git a/drivers/staging/iio/ring_hw.h b/drivers/staging/iio/ring_hw.h index bb8cfd28d45..39c14a71586 100644 --- a/drivers/staging/iio/ring_hw.h +++ b/drivers/staging/iio/ring_hw.h @@ -5,7 +5,7 @@   * under the terms of the GNU General Public License version 2 as published by   * the Free Software Foundation.   * - * Copyright (c) 2009 Jonathan Cameron <jic23@cam.ac.uk> + * Copyright (c) 2009 Jonathan Cameron <jic23@kernel.org>   *   */ @@ -14,9 +14,9 @@   * @buf:	generic ring buffer elements   * @private:	device specific data   */ -struct iio_hw_ring_buffer { -	struct iio_ring_buffer buf; +struct iio_hw_buffer { +	struct iio_buffer buf;  	void *private;  }; -#define iio_to_hw_ring_buf(r) container_of(r, struct iio_hw_ring_buffer, buf) +#define iio_to_hw_buf(r) container_of(r, struct iio_hw_buffer, buf) diff --git a/drivers/staging/iio/ring_sw.c b/drivers/staging/iio/ring_sw.c deleted file mode 100644 index 52624ace0bc..00000000000 --- a/drivers/staging/iio/ring_sw.c +++ /dev/null @@ -1,505 +0,0 @@ -/* The industrial I/O simple minimally locked ring buffer. - * - * Copyright (c) 2008 Jonathan Cameron - * - * 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/slab.h> -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/device.h> -#include <linux/workqueue.h> -#include "ring_sw.h" -#include "trigger.h" - -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.bytes_per_datum, GFP_ATOMIC); -	ring->read_p = NULL; -	ring->write_p = NULL; -	ring->last_written_p = NULL; -	ring->half_p = NULL; -	return ring->data ? 0 : -ENOMEM; -} - -static inline void __iio_init_sw_ring_buffer(struct iio_sw_ring_buffer *ring) -{ -	spin_lock_init(&ring->use_lock); -} - -static inline void __iio_free_sw_ring_buffer(struct iio_sw_ring_buffer *ring) -{ -	kfree(ring->data); -} - -void iio_mark_sw_rb_in_use(struct iio_ring_buffer *r) -{ -	struct iio_sw_ring_buffer *ring = iio_to_sw_ring(r); -	spin_lock(&ring->use_lock); -	ring->use_count++; -	spin_unlock(&ring->use_lock); -} -EXPORT_SYMBOL(iio_mark_sw_rb_in_use); - -void iio_unmark_sw_rb_in_use(struct iio_ring_buffer *r) -{ -	struct iio_sw_ring_buffer *ring = iio_to_sw_ring(r); -	spin_lock(&ring->use_lock); -	ring->use_count--; -	spin_unlock(&ring->use_lock); -} -EXPORT_SYMBOL(iio_unmark_sw_rb_in_use); - - -/* Ring buffer related functionality */ -/* Store to ring is typically called in the bh of a data ready interrupt handler - * 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 */ -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 == 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 -		 * passes it - guaranteed as set later in this function. -		 */ -		ring->half_p = ring->data - ring->buf.length*ring->buf.bytes_per_datum/2; -	} -	/* Copy data to where ever the current write pointer says */ -	memcpy(ring->write_p, data, ring->buf.bytes_per_datum); -	barrier(); -	/* Update the pointer used to get most recent value. -	 * Always valid as either points to latest or second latest value. -	 * Before this runs it is null and read attempts fail with -EAGAIN. -	 */ -	ring->last_written_p = ring->write_p; -	barrier(); -	/* temp_ptr used to ensure we never have an invalid pointer -	 * it may be slightly lagging, but never invalid -	 */ -	temp_ptr = ring->write_p + ring->buf.bytes_per_datum; -	/* End of ring, back to the beginning */ -	if (temp_ptr == ring->data + ring->buf.length*ring->buf.bytes_per_datum) -		temp_ptr = ring->data; -	/* Update the write pointer -	 * always valid as long as this is the only function able to write. -	 * Care needed with smp systems to ensure more than one ring fill -	 * is never scheduled. -	 */ -	ring->write_p = temp_ptr; - -	if (ring->read_p == NULL) -		ring->read_p = ring->data; -	/* Buffer full - move the read pointer and create / escalate -	 * ring event */ -	/* Tricky case - if the read pointer moves before we adjust it. -	 * Handle by not pushing if it has moved - may result in occasional -	 * unnecessary buffer full events when it wasn't quite true. -	 */ -	else if (ring->write_p == ring->read_p) { -		change_test_ptr = ring->read_p; -		temp_ptr = change_test_ptr + ring->buf.bytes_per_datum; -		if (temp_ptr -		    == ring->data + ring->buf.length*ring->buf.bytes_per_datum) { -			temp_ptr = ring->data; -		} -		/* We are moving pointer on one because the ring is full.  Any -		 * change to the read pointer will be this or greater. -		 */ -		if (change_test_ptr == ring->read_p) -			ring->read_p = temp_ptr; - -		spin_lock(&ring->buf.shared_ev_pointer.lock); - -		ret = iio_push_or_escallate_ring_event(&ring->buf, -			       IIO_EVENT_CODE_RING_100_FULL, timestamp); -		spin_unlock(&ring->buf.shared_ev_pointer.lock); -		if (ret) -			goto error_ret; -	} -	/* investigate if our event barrier has been passed */ -	/* There are definite 'issues' with this and chances of -	 * simultaneous read */ -	/* Also need to use loop count to ensure this only happens once */ -	ring->half_p += ring->buf.bytes_per_datum; -	if (ring->half_p == ring->data + ring->buf.length*ring->buf.bytes_per_datum) -		ring->half_p = ring->data; -	if (ring->half_p == ring->read_p) { -		spin_lock(&ring->buf.shared_ev_pointer.lock); -		code = IIO_EVENT_CODE_RING_50_FULL; -		ret = __iio_push_event(&ring->buf.ev_int, -				       code, -				       timestamp, -				       &ring->buf.shared_ev_pointer); -		spin_unlock(&ring->buf.shared_ev_pointer.lock); -	} -error_ret: -	return ret; -} - -int iio_rip_sw_rb(struct iio_ring_buffer *r, -		  size_t count, u8 **data, int *dead_offset) -{ -	struct iio_sw_ring_buffer *ring = iio_to_sw_ring(r); - -	u8 *initial_read_p, *initial_write_p, *current_read_p, *end_read_p; -	int ret, max_copied; -	int bytes_to_rip; - -	/* A userspace program has probably made an error if it tries to -	 *  read something that is not a whole number of bpds. -	 * Return an error. -	 */ -	if (count % ring->buf.bytes_per_datum) { -		ret = -EINVAL; -		printk(KERN_INFO "Ring buffer read request not whole number of" -		       "samples: Request bytes %zd, Current bytes per datum %d\n", -		       count, ring->buf.bytes_per_datum); -		goto error_ret; -	} -	/* Limit size to whole of ring buffer */ -	bytes_to_rip = min((size_t)(ring->buf.bytes_per_datum*ring->buf.length), count); - -	*data = kmalloc(bytes_to_rip, GFP_KERNEL); -	if (*data == NULL) { -		ret = -ENOMEM; -		goto error_ret; -	} - -	/* build local copy */ -	initial_read_p = ring->read_p; -	if (unlikely(initial_read_p == NULL)) { /* No data here as yet */ -		ret = 0; -		goto error_free_data_cpy; -	} - -	initial_write_p = ring->write_p; - -	/* Need a consistent pair */ -	while ((initial_read_p != ring->read_p) -	       || (initial_write_p != ring->write_p)) { -		initial_read_p = ring->read_p; -		initial_write_p = ring->write_p; -	} -	if (initial_write_p == initial_read_p) { -		/* No new data available.*/ -		ret = 0; -		goto error_free_data_cpy; -	} - -	if (initial_write_p >= initial_read_p + bytes_to_rip) { -		/* write_p is greater than necessary, all is easy */ -		max_copied = bytes_to_rip; -		memcpy(*data, initial_read_p, max_copied); -		end_read_p = initial_read_p + max_copied; -	} else if (initial_write_p > initial_read_p) { -		/*not enough data to cpy */ -		max_copied = initial_write_p - initial_read_p; -		memcpy(*data, initial_read_p, max_copied); -		end_read_p = initial_write_p; -	} else { -		/* going through 'end' of ring buffer */ -		max_copied = ring->data -			+ ring->buf.length*ring->buf.bytes_per_datum - initial_read_p; -		memcpy(*data, initial_read_p, max_copied); -		/* possible we are done if we align precisely with end */ -		if (max_copied == bytes_to_rip) -			end_read_p = ring->data; -		else if (initial_write_p -			 > ring->data + bytes_to_rip - max_copied) { -			/* enough data to finish */ -			memcpy(*data + max_copied, ring->data, -			       bytes_to_rip - max_copied); -			max_copied = bytes_to_rip; -			end_read_p = ring->data + (bytes_to_rip - max_copied); -		} else {  /* not enough data */ -			memcpy(*data + max_copied, ring->data, -			       initial_write_p - ring->data); -			max_copied += initial_write_p - ring->data; -			end_read_p = initial_write_p; -		} -	} -	/* Now to verify which section was cleanly copied - i.e. how far -	 * read pointer has been pushed */ -	current_read_p = ring->read_p; - -	if (initial_read_p <= current_read_p) -		*dead_offset = current_read_p - initial_read_p; -	else -		*dead_offset = ring->buf.length*ring->buf.bytes_per_datum -			- (initial_read_p - current_read_p); - -	/* possible issue if the initial write has been lapped or indeed -	 * the point we were reading to has been passed */ -	/* No valid data read. -	 * In this case the read pointer is already correct having been -	 * pushed further than we would look. */ -	if (max_copied - *dead_offset < 0) { -		ret = 0; -		goto error_free_data_cpy; -	} - -	/* setup the next read position */ -	/* Beware, this may fail due to concurrency fun and games. -	 *  Possible that sufficient fill commands have run to push the read -	 * pointer past where we would be after the rip. If this occurs, leave -	 * it be. -	 */ -	/* Tricky - deal with loops */ - -	while (ring->read_p != end_read_p) -		ring->read_p = end_read_p; - -	return max_copied - *dead_offset; - -error_free_data_cpy: -	kfree(*data); -error_ret: -	return ret; -} -EXPORT_SYMBOL(iio_rip_sw_rb); - -int iio_store_to_sw_rb(struct iio_ring_buffer *r, u8 *data, s64 timestamp) -{ -	struct iio_sw_ring_buffer *ring = iio_to_sw_ring(r); -	return iio_store_to_sw_ring(ring, data, timestamp); -} -EXPORT_SYMBOL(iio_store_to_sw_rb); - -static int iio_read_last_from_sw_ring(struct iio_sw_ring_buffer *ring, -				      unsigned char *data) -{ -	unsigned char *last_written_p_copy; - -	iio_mark_sw_rb_in_use(&ring->buf); -again: -	barrier(); -	last_written_p_copy = ring->last_written_p; -	barrier(); /*unnessecary? */ -	/* Check there is anything here */ -	if (last_written_p_copy == NULL) -		return -EAGAIN; -	memcpy(data, last_written_p_copy, ring->buf.bytes_per_datum); - -	if (unlikely(ring->last_written_p != last_written_p_copy)) -		goto again; - -	iio_unmark_sw_rb_in_use(&ring->buf); -	return 0; -} - -int iio_read_last_from_sw_rb(struct iio_ring_buffer *r, -			     unsigned char *data) -{ -	return iio_read_last_from_sw_ring(iio_to_sw_ring(r), data); -} -EXPORT_SYMBOL(iio_read_last_from_sw_rb); - -int iio_request_update_sw_rb(struct iio_ring_buffer *r) -{ -	int ret = 0; -	struct iio_sw_ring_buffer *ring = iio_to_sw_ring(r); - -	spin_lock(&ring->use_lock); -	if (!ring->update_needed) -		goto error_ret; -	if (ring->use_count) { -		ret = -EAGAIN; -		goto error_ret; -	} -	__iio_free_sw_ring_buffer(ring); -	ret = __iio_allocate_sw_ring_buffer(ring, ring->buf.bytes_per_datum, -					    ring->buf.length); -error_ret: -	spin_unlock(&ring->use_lock); -	return ret; -} -EXPORT_SYMBOL(iio_request_update_sw_rb); - -int iio_get_bytes_per_datum_sw_rb(struct iio_ring_buffer *r) -{ -	struct iio_sw_ring_buffer *ring = iio_to_sw_ring(r); -	return ring->buf.bytes_per_datum; -} -EXPORT_SYMBOL(iio_get_bytes_per_datum_sw_rb); - -int iio_set_bytes_per_datum_sw_rb(struct iio_ring_buffer *r, size_t bpd) -{ -	if (r->bytes_per_datum != bpd) { -		r->bytes_per_datum = bpd; -		if (r->access.mark_param_change) -			r->access.mark_param_change(r); -	} -	return 0; -} -EXPORT_SYMBOL(iio_set_bytes_per_datum_sw_rb); - -int iio_get_length_sw_rb(struct iio_ring_buffer *r) -{ -	return r->length; -} -EXPORT_SYMBOL(iio_get_length_sw_rb); - -int iio_set_length_sw_rb(struct iio_ring_buffer *r, int length) -{ -	if (r->length != length) { -		r->length = length; -		if (r->access.mark_param_change) -			r->access.mark_param_change(r); -	} -	return 0; -} -EXPORT_SYMBOL(iio_set_length_sw_rb); - -int iio_mark_update_needed_sw_rb(struct iio_ring_buffer *r) -{ -	struct iio_sw_ring_buffer *ring = iio_to_sw_ring(r); -	ring->update_needed = true; -	return 0; -} -EXPORT_SYMBOL(iio_mark_update_needed_sw_rb); - -static void iio_sw_rb_release(struct device *dev) -{ -	struct iio_ring_buffer *r = to_iio_ring_buffer(dev); -	kfree(iio_to_sw_ring(r)); -} - -static IIO_RING_ENABLE_ATTR; -static IIO_RING_BYTES_PER_DATUM_ATTR; -static IIO_RING_LENGTH_ATTR; - -/* Standard set of ring buffer attributes */ -static struct attribute *iio_ring_attributes[] = { -	&dev_attr_length.attr, -	&dev_attr_bytes_per_datum.attr, -	&dev_attr_enable.attr, -	NULL, -}; - -static struct attribute_group iio_ring_attribute_group = { -	.attrs = iio_ring_attributes, -}; - -static const struct attribute_group *iio_ring_attribute_groups[] = { -	&iio_ring_attribute_group, -	NULL -}; - -static struct device_type iio_sw_ring_type = { -	.release = iio_sw_rb_release, -	.groups = iio_ring_attribute_groups, -}; - -struct iio_ring_buffer *iio_sw_rb_allocate(struct iio_dev *indio_dev) -{ -	struct iio_ring_buffer *buf; -	struct iio_sw_ring_buffer *ring; - -	ring = kzalloc(sizeof *ring, GFP_KERNEL); -	if (!ring) -		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.bus = &iio_bus_type; -	dev_set_drvdata(&buf->dev, (void *)buf); - -	return buf; -} -EXPORT_SYMBOL(iio_sw_rb_allocate); - -void iio_sw_rb_free(struct iio_ring_buffer *r) -{ -	if (r) -		iio_put_ring_buffer(r); -} -EXPORT_SYMBOL(iio_sw_rb_free); - -int iio_sw_ring_preenable(struct iio_dev *indio_dev) -{ -	struct iio_ring_buffer *ring = indio_dev->ring; -	size_t size; -	dev_dbg(&indio_dev->dev, "%s\n", __func__); -	/* Check if there are any scan elements enabled, if not fail*/ -	if (!(ring->scan_count || ring->scan_timestamp)) -		return -EINVAL; -	if (ring->scan_timestamp) -		if (ring->scan_count) -			/* Timestamp (aligned to s64) and data */ -			size = (((ring->scan_count * ring->bpe) -					+ sizeof(s64) - 1) -				& ~(sizeof(s64) - 1)) -				+ sizeof(s64); -		else /* Timestamp only  */ -			size = sizeof(s64); -	else /* Data only */ -		size = ring->scan_count * ring->bpe; -	ring->access.set_bytes_per_datum(ring, size); - -	return 0; -} -EXPORT_SYMBOL(iio_sw_ring_preenable); - -void iio_sw_trigger_bh_to_ring(struct work_struct *work_s) -{ -	struct iio_sw_ring_helper_state *st -		= container_of(work_s, struct iio_sw_ring_helper_state, -			work_trigger_to_ring); -	struct iio_ring_buffer *ring = st->indio_dev->ring; -	int len = 0; -	size_t datasize = ring->access.get_bytes_per_datum(ring); -	char *data = kmalloc(datasize, GFP_KERNEL); - -	if (data == NULL) { -		dev_err(st->indio_dev->dev.parent, -			"memory alloc failed in ring bh"); -		return; -	} - -	if (ring->scan_count) -		len = st->get_ring_element(st, data); - -	  /* Guaranteed to be aligned with 8 byte boundary */ -	if (ring->scan_timestamp) -		*(s64 *)(((phys_addr_t)data + len -				+ sizeof(s64) - 1) & ~(sizeof(s64) - 1)) -			= st->last_timestamp; -	ring->access.store_to(ring, -			(u8 *)data, -			st->last_timestamp); - -	iio_trigger_notify_done(st->indio_dev->trig); -	kfree(data); - -	return; -} -EXPORT_SYMBOL(iio_sw_trigger_bh_to_ring); - -void iio_sw_poll_func_th(struct iio_dev *indio_dev, s64 time) -{	struct iio_sw_ring_helper_state *h -		= iio_dev_get_devdata(indio_dev); -	h->last_timestamp = time; -	schedule_work(&h->work_trigger_to_ring); -} -EXPORT_SYMBOL(iio_sw_poll_func_th); - -MODULE_DESCRIPTION("Industrialio I/O software ring buffer"); -MODULE_LICENSE("GPL"); diff --git a/drivers/staging/iio/ring_sw.h b/drivers/staging/iio/ring_sw.h deleted file mode 100644 index ad03d832c1b..00000000000 --- a/drivers/staging/iio/ring_sw.h +++ /dev/null @@ -1,227 +0,0 @@ -/* The industrial I/O simple minimally locked ring buffer. - * - * Copyright (c) 2008 Jonathan Cameron - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 as published by - * the Free Software Foundation. - * - * This code is deliberately kept separate from the main industrialio I/O core - * as it is intended that in the future a number of different software ring - * buffer implementations will exist with different characteristics to suit - * different applications. - * - * This particular one was designed for a data capture application where it was - * particularly important that no userspace reads would interrupt the capture - * process. To this end the ring is not locked during a read. - * - * Comments on this buffer design welcomed. It's far from efficient and some of - * my understanding of the effects of scheduling on this are somewhat limited. - * Frankly, to my mind, this is the current weak point in the industrial I/O - * patch set. - */ - -#ifndef _IIO_RING_SW_H_ -#define _IIO_RING_SW_H_ -/* NEEDS COMMENTS */ -/* The intention is that this should be a separate module from the iio core. - * This is a bit like supporting algorithms dependent on what the device - * driver requests - some may support multiple options */ - - -#include "iio.h" -#include "ring_generic.h" - -#if defined CONFIG_IIO_SW_RING || defined CONFIG_IIO_SW_RING_MODULE - -/** - * iio_create_sw_rb() - software ring buffer allocation - * @r:		pointer to ring buffer pointer - **/ -int iio_create_sw_rb(struct iio_ring_buffer **r); - -/** - * iio_init_sw_rb() - initialize the software ring buffer - * @r:		pointer to a software ring buffer created by an - *		iio_create_sw_rb call - * @indio_dev:		industrial I/O device structure - **/ -int iio_init_sw_rb(struct iio_ring_buffer *r, struct iio_dev *indio_dev); - -/** - * iio_exit_sw_rb() - reverse what was done in iio_init_sw_rb - * @r:		pointer to a software ring buffer created by an - *		iio_create_sw_rb call - **/ -void iio_exit_sw_rb(struct iio_ring_buffer *r); - -/** - * iio_free_sw_rb() - free memory occupied by the core ring buffer struct - * @r:		pointer to a software ring buffer created by an - *		iio_create_sw_rb call - **/ -void iio_free_sw_rb(struct iio_ring_buffer *r); - -/** - * iio_mark_sw_rb_in_use() - reference counting to prevent incorrect chances - * @r:		pointer to a software ring buffer created by an - *		iio_create_sw_rb call - **/ -void iio_mark_sw_rb_in_use(struct iio_ring_buffer *r); - -/** - *  iio_unmark_sw_rb_in_use() - notify the ring buffer that we don't care anymore - * @r:		pointer to a software ring buffer created by an - *		iio_create_sw_rb call - **/ -void iio_unmark_sw_rb_in_use(struct iio_ring_buffer *r); - -/** - * iio_read_last_from_sw_rb() - attempt to read the last stored datum from the rb - * @r:		pointer to a software ring buffer created by an - *		iio_create_sw_rb call - * @data:	where to store the last datum - **/ -int iio_read_last_from_sw_rb(struct iio_ring_buffer *r, u8 *data); - -/** - * iio_store_to_sw_rb() - store a new datum to the ring buffer - * @r:		pointer to ring buffer instance - * @data:	the datum to be stored including timestamp if relevant - * @timestamp:	timestamp which will be attached to buffer events if relevant - **/ -int iio_store_to_sw_rb(struct iio_ring_buffer *r, u8 *data, s64 timestamp); - -/** - * iio_rip_sw_rb() - attempt to read data from the ring buffer - * @r:			ring buffer instance - * @count:		number of datum's to try and read - * @data:		where the data will be stored. - * @dead_offset:	how much of the stored data was possibly invalidated by - *			the end of the copy. - **/ -int iio_rip_sw_rb(struct iio_ring_buffer *r, -		  size_t count, -		  u8 **data, -		  int *dead_offset); - -/** - * iio_request_update_sw_rb() - update params if update needed - * @r:		pointer to a software ring buffer created by an - *		iio_create_sw_rb call - **/ -int iio_request_update_sw_rb(struct iio_ring_buffer *r); - -/** - * iio_mark_update_needed_sw_rb() - tell the ring buffer it needs a param update - * @r:		pointer to a software ring buffer created by an - *		iio_create_sw_rb call - **/ -int iio_mark_update_needed_sw_rb(struct iio_ring_buffer *r); - - -/** - * iio_get_bytes_per_datum_sw_rb() - get the datum size in bytes - * @r:		pointer to a software ring buffer created by an - *		iio_create_sw_rb call - **/ -int iio_get_bytes_per_datum_sw_rb(struct iio_ring_buffer *r); - -/** - * iio_set_bytes_per_datum_sw_rb() - set the datum size in bytes - * @r:		pointer to a software ring buffer created by an - *		iio_create_sw_rb call - * @bpd:	bytes per datum value - **/ -int iio_set_bytes_per_datum_sw_rb(struct iio_ring_buffer *r, size_t bpd); - -/** - * iio_get_length_sw_rb() - get how many datums the rb may contain - * @r:		pointer to a software ring buffer created by an - *		iio_create_sw_rb call - **/ -int iio_get_length_sw_rb(struct iio_ring_buffer *r); - -/** - * iio_set_length_sw_rb() - set how many datums the rb may contain - * @r:		pointer to a software ring buffer created by an - *		iio_create_sw_rb call - * @length:	max number of data items for the ring buffer - **/ -int iio_set_length_sw_rb(struct iio_ring_buffer *r, int length); - -/** - * iio_ring_sw_register_funcs() - helper function to set up rb access - * @ra:		pointer to @iio_ring_access_funcs - **/ -static inline void iio_ring_sw_register_funcs(struct iio_ring_access_funcs *ra) -{ -	ra->mark_in_use = &iio_mark_sw_rb_in_use; -	ra->unmark_in_use = &iio_unmark_sw_rb_in_use; - -	ra->store_to = &iio_store_to_sw_rb; -	ra->read_last = &iio_read_last_from_sw_rb; -	ra->rip_lots = &iio_rip_sw_rb; - -	ra->mark_param_change = &iio_mark_update_needed_sw_rb; -	ra->request_update = &iio_request_update_sw_rb; - -	ra->get_bytes_per_datum = &iio_get_bytes_per_datum_sw_rb; -	ra->set_bytes_per_datum = &iio_set_bytes_per_datum_sw_rb; - -	ra->get_length = &iio_get_length_sw_rb; -	ra->set_length = &iio_set_length_sw_rb; -}; - -/** - * struct iio_sw_ring_buffer - software ring buffer - * @buf:		generic ring buffer elements - * @data:		the ring buffer memory - * @read_p:		read pointer (oldest available) - * @write_p:		write pointer - * @last_written_p:	read pointer (newest available) - * @half_p:		half buffer length behind write_p (event generation) - * @use_count:		reference count to prevent resizing when in use - * @update_needed:	flag to indicated change in size requested - * @use_lock:		lock to prevent change in size when in use - * - * Note that the first element of all ring buffers must be a - * struct iio_ring_buffer. -**/ - -struct iio_sw_ring_buffer { -	struct iio_ring_buffer  buf; -	unsigned char		*data; -	unsigned char		*read_p; -	unsigned char		*write_p; -	unsigned char		*last_written_p; -	/* used to act as a point at which to signal an event */ -	unsigned char		*half_p; -	int			use_count; -	int			update_needed; -	spinlock_t		use_lock; -}; - -#define iio_to_sw_ring(r) container_of(r, struct iio_sw_ring_buffer, buf) - -struct iio_ring_buffer *iio_sw_rb_allocate(struct iio_dev *indio_dev); -void iio_sw_rb_free(struct iio_ring_buffer *ring); - -int iio_sw_ring_preenable(struct iio_dev *indio_dev); - -struct iio_sw_ring_helper_state { -	struct work_struct		work_trigger_to_ring; -	struct iio_dev			*indio_dev; -	int (*get_ring_element)(struct iio_sw_ring_helper_state *st, u8 *buf); -	s64				last_timestamp; -}; - -void iio_sw_poll_func_th(struct iio_dev *indio_dev, s64 time); -void iio_sw_trigger_bh_to_ring(struct work_struct *work_s); - -#else /* CONFIG_IIO_RING_BUFFER*/ -struct iio_sw_ring_helper_state { -	struct iio_dev			*indio_dev; -}; -#endif /* !CONFIG_IIO_RING_BUFFER */ -#endif /* _IIO_RING_SW_H_ */ diff --git a/drivers/staging/iio/sysfs.h b/drivers/staging/iio/sysfs.h deleted file mode 100644 index ee91a95a8b9..00000000000 --- a/drivers/staging/iio/sysfs.h +++ /dev/null @@ -1,334 +0,0 @@ -/* The industrial I/O core - * - *Copyright (c) 2008 Jonathan Cameron - * - * 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. - * - * General attributes - */ - -#ifndef _INDUSTRIAL_IO_SYSFS_H_ -#define _INDUSTRIAL_IO_SYSFS_H_ - -#include "iio.h" - -/** - * struct iio_event_attr - event control attribute - * @dev_attr:	underlying device attribute - * @mask:	mask for the event when detecting - * @listel:	list header to allow addition to list of event handlers -*/ -struct iio_event_attr { -	struct device_attribute dev_attr; -	int mask; -	struct iio_event_handler_list *listel; -}; - -#define to_iio_event_attr(_dev_attr) \ -	container_of(_dev_attr, struct iio_event_attr, dev_attr) - -/** - * struct iio_dev_attr - iio specific device attribute - * @dev_attr:	underlying device attribute - * @address:	associated register address - * @val2:	secondary attribute value - */ -struct iio_dev_attr { -	struct device_attribute dev_attr; -	int address; -	int val2; -}; - -#define to_iio_dev_attr(_dev_attr)				\ -	container_of(_dev_attr, struct iio_dev_attr, dev_attr) - -ssize_t iio_read_const_attr(struct device *dev, -			    struct device_attribute *attr, -			    char *len); - -/** - * struct iio_const_attr - constant device specific attribute - *                         often used for things like available modes - * @string:	attribute string - * @dev_attr:	underlying device attribute - */ -struct iio_const_attr { -	const char *string; -	struct device_attribute dev_attr; -}; - -#define to_iio_const_attr(_dev_attr) \ -	container_of(_dev_attr, struct iio_const_attr, dev_attr) - -/* Some attributes will be hard coded (device dependent) and not require an -   address, in these cases pass a negative */ -#define IIO_ATTR(_name, _mode, _show, _store, _addr)		\ -	{ .dev_attr = __ATTR(_name, _mode, _show, _store),	\ -	  .address = _addr } - -#define IIO_DEVICE_ATTR(_name, _mode, _show, _store, _addr)	\ -	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			\ -	= IIO_ATTR_2(_name, _mode, _show, _store, _addr, _val2) - -#define IIO_CONST_ATTR(_name, _string)					\ -	struct iio_const_attr iio_const_attr_##_name			\ -	= { .string = _string,						\ -	    .dev_attr = __ATTR(_name, S_IRUGO, iio_read_const_attr, NULL)} - -#define IIO_CONST_ATTR_NAMED(_vname, _name, _string)			\ -	struct iio_const_attr iio_const_attr_##_vname			\ -	= { .string = _string,						\ -	    .dev_attr = __ATTR(_name, S_IRUGO, iio_read_const_attr, NULL)} -/* Generic attributes of onetype or another */ - -/** - * IIO_DEV_ATTR_REV - revision number for the device - * @_show: output method for the attribute - * - * Very much device dependent. - **/ -#define IIO_DEV_ATTR_REV(_show)			\ -	IIO_DEVICE_ATTR(revision, S_IRUGO, _show, NULL, 0) - -/** - * IIO_DEV_ATTR_NAME - chip type dependent identifier - * @_show: output method for the attribute - **/ -#define IIO_DEV_ATTR_NAME(_show)				\ -	IIO_DEVICE_ATTR(name, S_IRUGO, _show, NULL, 0) - -/** - * IIO_CONST_ATTR_NAME - constant identifier - * @_string: the name - **/ -#define IIO_CONST_ATTR_NAME(_string)				\ -	IIO_CONST_ATTR(name, _string) - -/** - * IIO_DEV_ATTR_SAMP_FREQ - sets any internal clock frequency - * @_mode: sysfs file mode/permissions - * @_show: output method for the attribute - * @_store: input method for the attribute - **/ -#define IIO_DEV_ATTR_SAMP_FREQ(_mode, _show, _store)			\ -	IIO_DEVICE_ATTR(sampling_frequency, _mode, _show, _store, 0) - -/** - * IIO_DEV_ATTR_AVAIL_SAMP_FREQ - list available sampling frequencies - * @_show: output method for the attribute - * - * 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_SAMP_FREQ_AVAIL(_string)			\ -	IIO_CONST_ATTR(sampling_frequency_available, _string) - -/** - * IIO_DEV_ATTR_SW_RING_ENABLE - enable software ring buffer - * @_show: output method for the attribute - * @_store: input method for the attribute - * - * Success may be dependent on attachment of trigger previously. - **/ -#define IIO_DEV_ATTR_SW_RING_ENABLE(_show, _store)			\ -	IIO_DEVICE_ATTR(sw_ring_enable, S_IRUGO | S_IWUSR, _show, _store, 0) - -/** - * IIO_DEV_ATTR_HW_RING_ENABLE - enable hardware ring buffer - * @_show: output method for the attribute - * @_store: input method for the attribute - * - * This is a different attribute from the software one as one can envision - * schemes where a combination of the two may be used. - **/ -#define IIO_DEV_ATTR_HW_RING_ENABLE(_show, _store)			\ -	IIO_DEVICE_ATTR(hw_ring_enable, S_IRUGO | S_IWUSR, _show, _store, 0) - -#define IIO_DEV_ATTR_TEMP_RAW(_show)			\ -	IIO_DEVICE_ATTR(temp_raw, S_IRUGO, _show, NULL, 0) - -#define IIO_CONST_ATTR_TEMP_OFFSET(_string)		\ -	IIO_CONST_ATTR(temp_offset, _string) - -#define IIO_CONST_ATTR_TEMP_SCALE(_string)		\ -	IIO_CONST_ATTR(temp_scale, _string) - -/** - * IIO_EVENT_SH - generic shared event handler - * @_name: event name - * @_handler: handler function to be called - * - * This is used in cases where more than one event may result from a single - * handler.  Often the case that some alarm register must be read and multiple - * alarms may have been triggered. - **/ -#define IIO_EVENT_SH(_name, _handler)					\ -	static struct iio_event_handler_list				\ -	iio_event_##_name = {						\ -		.handler = _handler,					\ -		.refcount = 0,						\ -		.exist_lock = __MUTEX_INITIALIZER(iio_event_##_name	\ -						  .exist_lock),		\ -		.list = {						\ -			.next = &iio_event_##_name.list,		\ -			.prev = &iio_event_##_name.list,		\ -		},							\ -	}; - -/** - * IIO_EVENT_ATTR_SH - generic shared event attribute - * @_name: event name - * @_ev_list: event handler list - * @_show: output method for the attribute - * @_store: input method for the attribute - * @_mask: mask used when detecting the event - * - * An attribute with an associated IIO_EVENT_SH - **/ -#define IIO_EVENT_ATTR_SH(_name, _ev_list, _show, _store, _mask)	\ -	static struct iio_event_attr					\ -	iio_event_attr_##_name						\ -	= { .dev_attr = __ATTR(_name, S_IRUGO | S_IWUSR,		\ -			       _show, _store),				\ -	    .mask = _mask,						\ -	    .listel = &_ev_list }; - -#define IIO_EVENT_ATTR_NAMED_SH(_vname, _name, _ev_list, _show, _store, _mask) \ -	static struct iio_event_attr					\ -	iio_event_attr_##_vname						\ -	= { .dev_attr = __ATTR(_name, S_IRUGO | S_IWUSR,		\ -			       _show, _store),				\ -	    .mask = _mask,						\ -	    .listel = &_ev_list }; - -/** - * IIO_EVENT_ATTR - non-shared event attribute - * @_name: event name - * @_show: output method for the attribute - * @_store: input method for the attribute - * @_mask: mask used when detecting the event - * @_handler: handler function to be called - **/ -#define IIO_EVENT_ATTR(_name, _show, _store, _mask, _handler)		\ -	IIO_EVENT_SH(_name, _handler);					\ -	static struct							\ -	iio_event_attr							\ -	iio_event_attr_##_name						\ -	= { .dev_attr = __ATTR(_name, S_IRUGO | S_IWUSR,		\ -			       _show, _store),				\ -	    .mask = _mask,						\ -	    .listel = &iio_event_##_name };				\ - -/** - * IIO_EVENT_ATTR_DATA_RDY - event driven by data ready signal - * @_show: output method for the attribute - * @_store: input method for the attribute - * @_mask: mask used when detecting the event - * @_handler: handler function to be called - * - * Not typically implemented in devices where full triggering support - * has been implemented. - **/ -#define IIO_EVENT_ATTR_DATA_RDY(_show, _store, _mask, _handler) \ -	IIO_EVENT_ATTR(data_rdy, _show, _store, _mask, _handler) - -#define IIO_EV_CLASS_BUFFER		0 -#define IIO_EV_CLASS_IN			1 -#define IIO_EV_CLASS_ACCEL		2 -#define IIO_EV_CLASS_GYRO		3 -#define IIO_EV_CLASS_MAGN		4 -#define IIO_EV_CLASS_LIGHT		5 -#define IIO_EV_CLASS_PROXIMITY		6 - -#define IIO_EV_MOD_X			0 -#define IIO_EV_MOD_Y			1 -#define IIO_EV_MOD_Z			2 -#define IIO_EV_MOD_X_AND_Y		3 -#define IIO_EV_MOD_X_ANX_Z		4 -#define IIO_EV_MOD_Y_AND_Z		5 -#define IIO_EV_MOD_X_AND_Y_AND_Z	6 -#define IIO_EV_MOD_X_OR_Y		7 -#define IIO_EV_MOD_X_OR_Z		8 -#define IIO_EV_MOD_Y_OR_Z		9 -#define IIO_EV_MOD_X_OR_Y_OR_Z		10 - -#define IIO_EV_TYPE_THRESH		0 -#define IIO_EV_TYPE_MAG			1 -#define IIO_EV_TYPE_ROC			2 - -#define IIO_EV_DIR_EITHER		0 -#define IIO_EV_DIR_RISING		1 -#define IIO_EV_DIR_FALLING		2 - -#define IIO_EVENT_CODE(channelclass, orient_bit, number,		\ -		       modifier, type, direction)			\ -	(channelclass | (orient_bit << 8) | ((number) << 9) |		\ -	 ((modifier) << 13) | ((type) << 16) | ((direction) << 24)) - -#define IIO_MOD_EVENT_CODE(channelclass, number, modifier,		\ -			   type, direction)				\ -	IIO_EVENT_CODE(channelclass, 1, number, modifier, type, direction) - -#define IIO_UNMOD_EVENT_CODE(channelclass, number, type, direction)	\ -	IIO_EVENT_CODE(channelclass, 0, number, 0, type, direction) - - -#define IIO_BUFFER_EVENT_CODE(code)		\ -	(IIO_EV_CLASS_BUFFER | (code << 8)) - -/** - * IIO_EVENT_ATTR_RING_50_FULL - ring buffer event to indicate 50% full - * @_show: output method for the attribute - * @_store: input method for the attribute - * @_mask: mask used when detecting the event - * @_handler: handler function to be called - **/ -#define IIO_EVENT_ATTR_RING_50_FULL(_show, _store, _mask, _handler)	\ -	IIO_EVENT_ATTR(ring_50_full, _show, _store, _mask, _handler) - -/** - * IIO_EVENT_ATTR_RING_50_FULL_SH - shared ring event to indicate 50% full - * @_evlist: event handler list - * @_show: output method for the attribute - * @_store: input method for the attribute - * @_mask: mask used when detecting the event - **/ -#define IIO_EVENT_ATTR_RING_50_FULL_SH(_evlist, _show, _store, _mask)	\ -	IIO_EVENT_ATTR_SH(ring_50_full, _evlist, _show, _store, _mask) - -/** - * IIO_EVENT_ATTR_RING_75_FULL_SH - shared ring event to indicate 75% full - * @_evlist: event handler list - * @_show: output method for the attribute - * @_store: input method for the attribute - * @_mask: mask used when detecting the event - **/ -#define IIO_EVENT_ATTR_RING_75_FULL_SH(_evlist, _show, _store, _mask)	\ -	IIO_EVENT_ATTR_SH(ring_75_full, _evlist, _show, _store, _mask) - -#define IIO_EVENT_CODE_RING_50_FULL	IIO_BUFFER_EVENT_CODE(0) -#define IIO_EVENT_CODE_RING_75_FULL	IIO_BUFFER_EVENT_CODE(1) -#define IIO_EVENT_CODE_RING_100_FULL	IIO_BUFFER_EVENT_CODE(2) - -#endif /* _INDUSTRIAL_IO_SYSFS_H_ */ diff --git a/drivers/staging/iio/trigger.h b/drivers/staging/iio/trigger.h deleted file mode 100644 index 469beba3e71..00000000000 --- a/drivers/staging/iio/trigger.h +++ /dev/null @@ -1,164 +0,0 @@ -/* The industrial I/O core, trigger handling functions - * - * Copyright (c) 2008 Jonathan Cameron - * - * 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 _IIO_TRIGGER_H_ -#define _IIO_TRIGGER_H_ - -/** - * struct iio_trigger - industrial I/O trigger device - * - * @id:			[INTERN] unique id number - * @name:		[DRIVER] unique name - * @dev:		[DRIVER] associated device (if relevant) - * @private_data:	[DRIVER] device specific data - * @list:		[INTERN] used in maintenance of global trigger list - * @alloc_list:		[DRIVER] used for driver specific trigger list - * @pollfunc_list_lock:	[INTERN] protection of the polling function list - * @pollfunc_list:	[INTERN] list of functions to run on trigger. - * @control_attrs:	[DRIVER] sysfs attributes relevant to trigger type - * @owner:		[DRIVER] used to monitor usage count of the trigger. - * @use_count:		use count for the trigger - * @set_trigger_state:	[DRIVER] switch on/off the trigger on demand - * @try_reenable:	function to reenable the trigger when the - *			use count is zero (may be NULL) - **/ -struct iio_trigger { -	int				id; -	const char			*name; -	struct device			dev; - -	void				*private_data; -	struct list_head		list; -	struct list_head		alloc_list; -	spinlock_t			pollfunc_list_lock; -	struct list_head		pollfunc_list; -	const struct attribute_group	*control_attrs; -	struct module			*owner; -	int use_count; - -	int (*set_trigger_state)(struct iio_trigger *trig, bool state); -	int (*try_reenable)(struct iio_trigger *trig); -}; - -static inline struct iio_trigger *to_iio_trigger(struct device *d) -{ -	return container_of(d, struct iio_trigger, dev); -}; - -static inline void iio_put_trigger(struct iio_trigger *trig) -{ -	put_device(&trig->dev); -	module_put(trig->owner); -}; - -static inline void iio_get_trigger(struct iio_trigger *trig) -{ -	__module_get(trig->owner); -	get_device(&trig->dev); -}; - -/** - * iio_trigger_read_name() - sysfs access function to get the trigger name - * @dev: the system device - * @attr: device attributes for the device - * @buf: output buffer to store the trigger name - **/ -ssize_t iio_trigger_read_name(struct device *dev, -			      struct device_attribute *attr, -			      char *buf); - -#define IIO_TRIGGER_NAME_ATTR DEVICE_ATTR(name, S_IRUGO,		\ -					  iio_trigger_read_name,	\ -					  NULL); - -/** - * iio_trigger_find_by_name() - search global trigger list - * @name: trigger name to search for - * @len: trigger name string length to compare - **/ -struct iio_trigger *iio_trigger_find_by_name(const char *name, size_t len); - -/** - * iio_trigger_register() - register a trigger with the IIO core - * @trig_info:	trigger to be registered - **/ -int iio_trigger_register(struct iio_trigger *trig_info); - -/** - * iio_trigger_unregister() - unregister a trigger from the core - * @trig_info:	trigger to be unregistered - **/ -void iio_trigger_unregister(struct iio_trigger *trig_info); - -/** - * iio_trigger_attach_poll_func() - add a function pair to be run on trigger - * @trig:	trigger to which the function pair are being added - * @pf:		poll function pair - **/ -int iio_trigger_attach_poll_func(struct iio_trigger *trig, -				 struct iio_poll_func *pf); - -/** - * iio_trigger_dettach_poll_func() -	remove function pair from those to be - *					run on trigger - * @trig:	trigger from which the function is being removed - * @pf:		poll function pair - **/ -int iio_trigger_dettach_poll_func(struct iio_trigger *trig, -				  struct iio_poll_func *pf); - -/** - * iio_trigger_poll() - called on a trigger occurring - * @trig: trigger which occurred - * - * Typically called in relevant hardware interrupt handler. - **/ -void iio_trigger_poll(struct iio_trigger *trig, s64 time); -void iio_trigger_notify_done(struct iio_trigger *trig); - -/** - * struct iio_poll_func - poll function pair - * - * @list:			associate this with a triggers pollfunc_list - * @private_data:		data specific to device (passed into poll func) - * @poll_func_immediate:	function in here is run first. They should be - *				extremely lightweight.  Typically used for latch - *				control on sensor supporting it. - * @poll_func_main:		function in here is run after all immediates. - *				Reading from sensor etc typically involves - *				scheduling from here. - * - * The two stage approach used here is only important when multiple sensors are - * being triggered by a single trigger. This really comes into its own with - * simultaneous sampling devices where a simple latch command can be used to - * make the device store the values on all inputs. - **/ -struct iio_poll_func { -	struct				list_head list; -	void				*private_data; -	void (*poll_func_immediate)(struct iio_dev *indio_dev); -	void (*poll_func_main)(struct iio_dev *private_data, s64 time); - -}; - -int iio_alloc_pollfunc(struct iio_dev *indio_dev, -		       void (*immediate)(struct iio_dev *indio_dev), -		       void (*main)(struct iio_dev *private_data, s64 time)); - -/* - * Two functions for common case where all that happens is a pollfunc - * is attached and detached from a trigger - */ -int iio_triggered_ring_postenable(struct iio_dev *indio_dev); -int iio_triggered_ring_predisable(struct iio_dev *indio_dev); - -struct iio_trigger *iio_allocate_trigger(void); - -void iio_free_trigger(struct iio_trigger *trig); - -#endif /* _IIO_TRIGGER_H_ */ diff --git a/drivers/staging/iio/trigger/Kconfig b/drivers/staging/iio/trigger/Kconfig index d842a584a3a..2fd18c60323 100644 --- a/drivers/staging/iio/trigger/Kconfig +++ b/drivers/staging/iio/trigger/Kconfig @@ -12,10 +12,15 @@ config IIO_PERIODIC_RTC_TRIGGER  	  Provides support for using periodic capable real time  	  clocks as IIO triggers. -config IIO_GPIO_TRIGGER -	tristate "GPIO trigger" -	depends on GENERIC_GPIO +config IIO_BFIN_TMR_TRIGGER +	tristate "Blackfin TIMER trigger" +	depends on BLACKFIN +	select BFIN_GPTIMERS  	help -	  Provides support for using GPIO pins as IIO triggers. +	  Provides support for using a Blackfin timer as IIO triggers. +	  If unsure, say N (but it's safe to say "Y"). + +	  To compile this driver as a module, choose M here: the +	  module will be called iio-trig-bfin-timer.  endif # IIO_TRIGGER diff --git a/drivers/staging/iio/trigger/Makefile b/drivers/staging/iio/trigger/Makefile index 10aeca5e347..238481b78e7 100644 --- a/drivers/staging/iio/trigger/Makefile +++ b/drivers/staging/iio/trigger/Makefile @@ -3,4 +3,4 @@  #  obj-$(CONFIG_IIO_PERIODIC_RTC_TRIGGER) += iio-trig-periodic-rtc.o -obj-$(CONFIG_IIO_GPIO_TRIGGER) += iio-trig-gpio.o +obj-$(CONFIG_IIO_BFIN_TMR_TRIGGER) += iio-trig-bfin-timer.o diff --git a/drivers/staging/iio/trigger/iio-trig-bfin-timer.c b/drivers/staging/iio/trigger/iio-trig-bfin-timer.c new file mode 100644 index 00000000000..26e1ca0b780 --- /dev/null +++ b/drivers/staging/iio/trigger/iio-trig-bfin-timer.c @@ -0,0 +1,302 @@ +/* + * Copyright 2011 Analog Devices Inc. + * + * Licensed under the GPL-2. + * + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/delay.h> + +#include <asm/gptimers.h> +#include <asm/portmux.h> + +#include <linux/iio/iio.h> +#include <linux/iio/trigger.h> + +#include "iio-trig-bfin-timer.h" + +struct bfin_timer { +	unsigned short id, bit; +	unsigned long irqbit; +	int irq; +	int pin; +}; + +/* + * this covers all hardware timer configurations on + * all Blackfin derivatives out there today + */ + +static struct bfin_timer iio_bfin_timer_code[MAX_BLACKFIN_GPTIMERS] = { +	{TIMER0_id,  TIMER0bit,  TIMER_STATUS_TIMIL0,  IRQ_TIMER0, P_TMR0}, +	{TIMER1_id,  TIMER1bit,  TIMER_STATUS_TIMIL1,  IRQ_TIMER1, P_TMR1}, +	{TIMER2_id,  TIMER2bit,  TIMER_STATUS_TIMIL2,  IRQ_TIMER2, P_TMR2}, +#if (MAX_BLACKFIN_GPTIMERS > 3) +	{TIMER3_id,  TIMER3bit,  TIMER_STATUS_TIMIL3,  IRQ_TIMER3, P_TMR3}, +	{TIMER4_id,  TIMER4bit,  TIMER_STATUS_TIMIL4,  IRQ_TIMER4, P_TMR4}, +	{TIMER5_id,  TIMER5bit,  TIMER_STATUS_TIMIL5,  IRQ_TIMER5, P_TMR5}, +	{TIMER6_id,  TIMER6bit,  TIMER_STATUS_TIMIL6,  IRQ_TIMER6, P_TMR6}, +	{TIMER7_id,  TIMER7bit,  TIMER_STATUS_TIMIL7,  IRQ_TIMER7, P_TMR7}, +#endif +#if (MAX_BLACKFIN_GPTIMERS > 8) +	{TIMER8_id,  TIMER8bit,  TIMER_STATUS_TIMIL8,  IRQ_TIMER8, P_TMR8}, +	{TIMER9_id,  TIMER9bit,  TIMER_STATUS_TIMIL9,  IRQ_TIMER9, P_TMR9}, +	{TIMER10_id, TIMER10bit, TIMER_STATUS_TIMIL10, IRQ_TIMER10, P_TMR10}, +#if (MAX_BLACKFIN_GPTIMERS > 11) +	{TIMER11_id, TIMER11bit, TIMER_STATUS_TIMIL11, IRQ_TIMER11, P_TMR11}, +#endif +#endif +}; + +struct bfin_tmr_state { +	struct iio_trigger *trig; +	struct bfin_timer *t; +	unsigned timer_num; +	bool output_enable; +	unsigned int duty; +	int irq; +}; + +static int iio_bfin_tmr_set_state(struct iio_trigger *trig, bool state) +{ +	struct bfin_tmr_state *st = iio_trigger_get_drvdata(trig); + +	if (get_gptimer_period(st->t->id) == 0) +		return -EINVAL; + +	if (state) +		enable_gptimers(st->t->bit); +	else +		disable_gptimers(st->t->bit); + +	return 0; +} + +static ssize_t iio_bfin_tmr_frequency_store(struct device *dev, +		struct device_attribute *attr, const char *buf, size_t count) +{ +	struct iio_trigger *trig = to_iio_trigger(dev); +	struct bfin_tmr_state *st = iio_trigger_get_drvdata(trig); +	unsigned int val; +	bool enabled; +	int ret; + +	ret = kstrtouint(buf, 10, &val); +	if (ret) +		return ret; + +	if (val > 100000) +		return -EINVAL; + +	enabled = get_enabled_gptimers() & st->t->bit; + +	if (enabled) +		disable_gptimers(st->t->bit); + +	if (val == 0) +		return count; + +	val = get_sclk() / val; +	if (val <= 4 || val <= st->duty) +		return -EINVAL; + +	set_gptimer_period(st->t->id, val); +	set_gptimer_pwidth(st->t->id, val - st->duty); + +	if (enabled) +		enable_gptimers(st->t->bit); + +	return count; +} + +static ssize_t iio_bfin_tmr_frequency_show(struct device *dev, +				 struct device_attribute *attr, +				 char *buf) +{ +	struct iio_trigger *trig = to_iio_trigger(dev); +	struct bfin_tmr_state *st = iio_trigger_get_drvdata(trig); +	unsigned int period = get_gptimer_period(st->t->id); +	unsigned long val; + +	if (period == 0) +		val = 0; +	else +		val = get_sclk() / get_gptimer_period(st->t->id); + +	return sprintf(buf, "%lu\n", val); +} + +static DEVICE_ATTR(frequency, S_IRUGO | S_IWUSR, iio_bfin_tmr_frequency_show, +		   iio_bfin_tmr_frequency_store); + +static struct attribute *iio_bfin_tmr_trigger_attrs[] = { +	&dev_attr_frequency.attr, +	NULL, +}; + +static const struct attribute_group iio_bfin_tmr_trigger_attr_group = { +	.attrs = iio_bfin_tmr_trigger_attrs, +}; + +static const struct attribute_group *iio_bfin_tmr_trigger_attr_groups[] = { +	&iio_bfin_tmr_trigger_attr_group, +	NULL +}; + +static irqreturn_t iio_bfin_tmr_trigger_isr(int irq, void *devid) +{ +	struct bfin_tmr_state *st = devid; + +	clear_gptimer_intr(st->t->id); +	iio_trigger_poll(st->trig, 0); + +	return IRQ_HANDLED; +} + +static int iio_bfin_tmr_get_number(int irq) +{ +	int i; + +	for (i = 0; i < MAX_BLACKFIN_GPTIMERS; i++) +		if (iio_bfin_timer_code[i].irq == irq) +			return i; + +	return -ENODEV; +} + +static const struct iio_trigger_ops iio_bfin_tmr_trigger_ops = { +	.owner = THIS_MODULE, +	.set_trigger_state = iio_bfin_tmr_set_state, +}; + +static int iio_bfin_tmr_trigger_probe(struct platform_device *pdev) +{ +	struct iio_bfin_timer_trigger_pdata *pdata = pdev->dev.platform_data; +	struct bfin_tmr_state *st; +	unsigned int config; +	int ret; + +	st = kzalloc(sizeof(*st), GFP_KERNEL); +	if (st == NULL) { +		ret = -ENOMEM; +		goto out; +	} + +	st->irq = platform_get_irq(pdev, 0); +	if (!st->irq) { +		dev_err(&pdev->dev, "No IRQs specified"); +		ret = -ENODEV; +		goto out1; +	} + +	ret = iio_bfin_tmr_get_number(st->irq); +	if (ret < 0) +		goto out1; + +	st->timer_num = ret; +	st->t = &iio_bfin_timer_code[st->timer_num]; + +	st->trig = iio_trigger_alloc("bfintmr%d", st->timer_num); +	if (!st->trig) { +		ret = -ENOMEM; +		goto out1; +	} + +	st->trig->ops = &iio_bfin_tmr_trigger_ops; +	st->trig->dev.groups = iio_bfin_tmr_trigger_attr_groups; +	iio_trigger_set_drvdata(st->trig, st); +	ret = iio_trigger_register(st->trig); +	if (ret) +		goto out2; + +	ret = request_irq(st->irq, iio_bfin_tmr_trigger_isr, +			  0, st->trig->name, st); +	if (ret) { +		dev_err(&pdev->dev, +			"request IRQ-%d failed", st->irq); +		goto out4; +	} + +	config = PWM_OUT | PERIOD_CNT | IRQ_ENA; + +	if (pdata && pdata->output_enable) { +		unsigned long long val; + +		st->output_enable = true; + +		ret = peripheral_request(st->t->pin, st->trig->name); +		if (ret) +			goto out_free_irq; + +		val = (unsigned long long)get_sclk() * pdata->duty_ns; +		do_div(val, NSEC_PER_SEC); +		st->duty = val; + +		/** +		 * The interrupt will be generated at the end of the period, +		 * since we want the interrupt to be generated at end of the +		 * pulse we invert both polarity and duty cycle, so that the +		 * pulse will be generated directly before the interrupt. +		 */ +		if (pdata->active_low) +			config |= PULSE_HI; +	} else { +		st->duty = 1; +		config |= OUT_DIS; +	} + +	set_gptimer_config(st->t->id, config); + +	dev_info(&pdev->dev, "iio trigger Blackfin TMR%d, IRQ-%d", +		 st->timer_num, st->irq); +	platform_set_drvdata(pdev, st); + +	return 0; +out_free_irq: +	free_irq(st->irq, st); +out4: +	iio_trigger_unregister(st->trig); +out2: +	iio_trigger_put(st->trig); +out1: +	kfree(st); +out: +	return ret; +} + +static int iio_bfin_tmr_trigger_remove(struct platform_device *pdev) +{ +	struct bfin_tmr_state *st = platform_get_drvdata(pdev); + +	disable_gptimers(st->t->bit); +	if (st->output_enable) +		peripheral_free(st->t->pin); +	free_irq(st->irq, st); +	iio_trigger_unregister(st->trig); +	iio_trigger_put(st->trig); +	kfree(st); + +	return 0; +} + +static struct platform_driver iio_bfin_tmr_trigger_driver = { +	.driver = { +		.name = "iio_bfin_tmr_trigger", +		.owner = THIS_MODULE, +	}, +	.probe = iio_bfin_tmr_trigger_probe, +	.remove = iio_bfin_tmr_trigger_remove, +}; + +module_platform_driver(iio_bfin_tmr_trigger_driver); + +MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>"); +MODULE_DESCRIPTION("Blackfin system timer based trigger for the iio subsystem"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:iio-trig-bfin-timer"); diff --git a/drivers/staging/iio/trigger/iio-trig-bfin-timer.h b/drivers/staging/iio/trigger/iio-trig-bfin-timer.h new file mode 100644 index 00000000000..c07321f8d94 --- /dev/null +++ b/drivers/staging/iio/trigger/iio-trig-bfin-timer.h @@ -0,0 +1,24 @@ +#ifndef __IIO_BFIN_TIMER_TRIGGER_H__ +#define __IIO_BFIN_TIMER_TRIGGER_H__ + +/** + * struct iio_bfin_timer_trigger_pdata - timer trigger platform data + * @output_enable: Enable external trigger pulse generation. + * @active_low: Whether the trigger pulse is active low. + * @duty_ns: Length of the trigger pulse in nanoseconds. + * + * This struct is used to configure the output pulse generation of the blackfin + * timer trigger. If output_enable is set to true an external trigger signal + * will generated on the pin corresponding to the timer. This is useful for + * converters which needs an external signal to start conversion. active_low and + * duty_ns are used to configure the type of the trigger pulse. If output_enable + * is set to false no external trigger pulse will be generated and active_low + * and duty_ns are ignored. + **/ +struct iio_bfin_timer_trigger_pdata { +	bool output_enable; +	bool active_low; +	unsigned int duty_ns; +}; + +#endif diff --git a/drivers/staging/iio/trigger/iio-trig-gpio.c b/drivers/staging/iio/trigger/iio-trig-gpio.c deleted file mode 100644 index 2ce95e964cf..00000000000 --- a/drivers/staging/iio/trigger/iio-trig-gpio.c +++ /dev/null @@ -1,194 +0,0 @@ -/* - * Industrial I/O - gpio based trigger support - * - * Copyright (c) 2008 Jonathan Cameron - * - * 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. - * - * Currently this is more of a functioning proof of concept that a fully - * fledged trigger driver. - * - * TODO: - * - * Add board config elements to allow specification of startup settings. - */ - -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/platform_device.h> -#include <linux/interrupt.h> -#include <linux/gpio.h> -#include <linux/slab.h> - -#include "../iio.h" -#include "../trigger.h" - -static LIST_HEAD(iio_gpio_trigger_list); -static DEFINE_MUTEX(iio_gpio_trigger_list_lock); - -struct iio_gpio_trigger_info { -	struct mutex in_use; -	unsigned int irq; -}; -/* - * Need to reference count these triggers and only enable gpio interrupts - * as appropriate. - */ - -/* So what functionality do we want in here?... */ -/* set high / low as interrupt type? */ - -static irqreturn_t iio_gpio_trigger_poll(int irq, void *private) -{ -	/* Timestamp not currently provided */ -	iio_trigger_poll(private, 0); -	return IRQ_HANDLED; -} - -static IIO_TRIGGER_NAME_ATTR; - -static struct attribute *iio_gpio_trigger_attrs[] = { -	&dev_attr_name.attr, -	NULL, -}; - -static const struct attribute_group iio_gpio_trigger_attr_group = { -	.attrs = iio_gpio_trigger_attrs, -}; - -static int iio_gpio_trigger_probe(struct platform_device *pdev) -{ -	struct iio_gpio_trigger_info *trig_info; -	struct iio_trigger *trig, *trig2; -	unsigned long irqflags; -	struct resource *irq_res; -	int irq, ret = 0, irq_res_cnt = 0; - -	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; -		} -		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 = kasprintf(GFP_KERNEL, "irqtrig%d", irq); -			if (trig->name == NULL) { -				ret = -ENOMEM; -				goto error_free_trig_info; -			} -			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); -		} - -		irq_res_cnt++; -	} while (irq_res != NULL); - - -	return 0; - -/* First clean up the partly allocated trigger */ -error_release_irq: -	free_irq(irq, trig); -error_free_name: -	kfree(trig->name); -error_free_trig_info: -	kfree(trig_info); -error_put_trigger: -	iio_put_trigger(trig); -error_free_completed_registrations: -	/* The rest should have been added to the iio_gpio_trigger_list */ -	list_for_each_entry_safe(trig, -				 trig2, -				 &iio_gpio_trigger_list, -				 alloc_list) { -		trig_info = trig->private_data; -		free_irq(gpio_to_irq(trig_info->irq), trig); -		kfree(trig->name); -		kfree(trig_info); -		iio_trigger_unregister(trig); -	} - -	return ret; -} - -static int iio_gpio_trigger_remove(struct platform_device *pdev) -{ -	struct iio_trigger *trig, *trig2; -	struct iio_gpio_trigger_info *trig_info; - -	mutex_lock(&iio_gpio_trigger_list_lock); -	list_for_each_entry_safe(trig, -				 trig2, -				 &iio_gpio_trigger_list, -				 alloc_list) { -		trig_info = trig->private_data; -		iio_trigger_unregister(trig); -		free_irq(trig_info->irq, trig); -		kfree(trig->name); -		kfree(trig_info); -		iio_put_trigger(trig); -	} -	mutex_unlock(&iio_gpio_trigger_list_lock); - -	return 0; -} - -static struct platform_driver iio_gpio_trigger_driver = { -	.probe = iio_gpio_trigger_probe, -	.remove = iio_gpio_trigger_remove, -	.driver = { -		.name = "iio_gpio_trigger", -		.owner = THIS_MODULE, -	}, -}; - -static int __init iio_gpio_trig_init(void) -{ -	return platform_driver_register(&iio_gpio_trigger_driver); -} -module_init(iio_gpio_trig_init); - -static void __exit iio_gpio_trig_exit(void) -{ -	platform_driver_unregister(&iio_gpio_trigger_driver); -} -module_exit(iio_gpio_trig_exit); - -MODULE_AUTHOR("Jonathan Cameron <jic23@cam.ac.uk>"); -MODULE_DESCRIPTION("Example gpio trigger for the iio subsystem"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/iio/trigger/iio-trig-periodic-rtc.c b/drivers/staging/iio/trigger/iio-trig-periodic-rtc.c index 24f174e1cda..38ecb4bb6e4 100644 --- a/drivers/staging/iio/trigger/iio-trig-periodic-rtc.c +++ b/drivers/staging/iio/trigger/iio-trig-periodic-rtc.c @@ -16,8 +16,8 @@  #include <linux/module.h>  #include <linux/slab.h>  #include <linux/rtc.h> -#include "../iio.h" -#include "../trigger.h" +#include <linux/iio/iio.h> +#include <linux/iio/trigger.h>  static LIST_HEAD(iio_prtc_trigger_list);  static DEFINE_MUTEX(iio_prtc_trigger_list_lock); @@ -30,10 +30,11 @@ struct iio_prtc_trigger_info {  static int iio_trig_periodic_rtc_set_state(struct iio_trigger *trig, bool state)  { -	struct iio_prtc_trigger_info *trig_info = trig->private_data; +	struct iio_prtc_trigger_info *trig_info = iio_trigger_get_drvdata(trig);  	if (trig_info->frequency == 0)  		return -EINVAL; -	printk(KERN_INFO "trigger frequency is %d\n", trig_info->frequency); +	dev_info(&trig_info->rtc->dev, "trigger frequency is %d\n", +			trig_info->frequency);  	return rtc_irq_set_state(trig_info->rtc, &trig_info->task, state);  } @@ -41,8 +42,8 @@ static ssize_t iio_trig_periodic_read_freq(struct device *dev,  					   struct device_attribute *attr,  					   char *buf)  { -	struct iio_trigger *trig = dev_get_drvdata(dev); -	struct iio_prtc_trigger_info *trig_info = trig->private_data; +	struct iio_trigger *trig = to_iio_trigger(dev); +	struct iio_prtc_trigger_info *trig_info = iio_trigger_get_drvdata(trig);  	return sprintf(buf, "%u\n", trig_info->frequency);  } @@ -51,12 +52,12 @@ static ssize_t iio_trig_periodic_write_freq(struct device *dev,  					    const char *buf,  					    size_t len)  { -	struct iio_trigger *trig = dev_get_drvdata(dev); -	struct iio_prtc_trigger_info *trig_info = trig->private_data; -	unsigned long val; +	struct iio_trigger *trig = to_iio_trigger(dev); +	struct iio_prtc_trigger_info *trig_info = iio_trigger_get_drvdata(trig); +	int val;  	int ret; -	ret = strict_strtoul(buf, 10, &val); +	ret = kstrtoint(buf, 10, &val);  	if (ret)  		goto error_ret; @@ -72,26 +73,35 @@ error_ret:  	return ret;  } -static IIO_TRIGGER_NAME_ATTR;  static DEVICE_ATTR(frequency, S_IRUGO | S_IWUSR,  	    iio_trig_periodic_read_freq,  	    iio_trig_periodic_write_freq);  static struct attribute *iio_trig_prtc_attrs[] = {  	&dev_attr_frequency.attr, -	&dev_attr_name.attr,  	NULL,  }; +  static const struct attribute_group iio_trig_prtc_attr_group = {  	.attrs = iio_trig_prtc_attrs,  }; +static const struct attribute_group *iio_trig_prtc_attr_groups[] = { +	&iio_trig_prtc_attr_group, +	NULL +}; +  static void iio_prtc_trigger_poll(void *private_data)  {  	/* Timestamp is not provided currently */  	iio_trigger_poll(private_data, 0);  } +static const struct iio_trigger_ops iio_prtc_trigger_ops = { +	.owner = THIS_MODULE, +	.set_trigger_state = &iio_trig_periodic_rtc_set_state, +}; +  static int iio_trig_periodic_rtc_probe(struct platform_device *dev)  {  	char **pdata = dev->dev.platform_data; @@ -103,7 +113,7 @@ static int iio_trig_periodic_rtc_probe(struct platform_device *dev)  	for (i = 0;; i++) {  		if (pdata[i] == NULL)  			break; -		trig = iio_allocate_trigger(); +		trig = iio_trigger_alloc("periodic%s", pdata[i]);  		if (!trig) {  			ret = -ENOMEM;  			goto error_free_completed_registrations; @@ -115,28 +125,21 @@ static int iio_trig_periodic_rtc_probe(struct platform_device *dev)  			ret = -ENOMEM;  			goto error_put_trigger_and_remove_from_list;  		} -		trig->private_data = trig_info; -		trig->owner = THIS_MODULE; -		trig->set_trigger_state = &iio_trig_periodic_rtc_set_state; -		trig->name = kasprintf(GFP_KERNEL, "periodic%s", pdata[i]); -		if (trig->name == NULL) { -			ret = -ENOMEM; -			goto error_free_trig_info; -		} - +		iio_trigger_set_drvdata(trig, trig_info); +		trig->ops = &iio_prtc_trigger_ops;  		/* RTC access */  		trig_info->rtc  			= rtc_class_open(pdata[i]);  		if (trig_info->rtc == NULL) {  			ret = -EINVAL; -			goto error_free_name; +			goto error_free_trig_info;  		}  		trig_info->task.func = iio_prtc_trigger_poll;  		trig_info->task.private_data = trig;  		ret = rtc_irq_register(trig_info->rtc, &trig_info->task);  		if (ret)  			goto error_close_rtc; -		trig->control_attrs = &iio_trig_prtc_attr_group; +		trig->dev.groups = iio_trig_prtc_attr_groups;  		ret = iio_trigger_register(trig);  		if (ret)  			goto error_unregister_rtc_irq; @@ -146,22 +149,19 @@ error_unregister_rtc_irq:  	rtc_irq_unregister(trig_info->rtc, &trig_info->task);  error_close_rtc:  	rtc_class_close(trig_info->rtc); -error_free_name: -	kfree(trig->name);  error_free_trig_info:  	kfree(trig_info);  error_put_trigger_and_remove_from_list:  	list_del(&trig->alloc_list); -	iio_put_trigger(trig); +	iio_trigger_put(trig);  error_free_completed_registrations:  	list_for_each_entry_safe(trig,  				 trig2,  				 &iio_prtc_trigger_list,  				 alloc_list) { -		trig_info = trig->private_data; +		trig_info = iio_trigger_get_drvdata(trig);  		rtc_irq_unregister(trig_info->rtc, &trig_info->task);  		rtc_class_close(trig_info->rtc); -		kfree(trig->name);  		kfree(trig_info);  		iio_trigger_unregister(trig);  	} @@ -177,10 +177,9 @@ static int iio_trig_periodic_rtc_remove(struct platform_device *dev)  				 trig2,  				 &iio_prtc_trigger_list,  				 alloc_list) { -		trig_info = trig->private_data; +		trig_info = iio_trigger_get_drvdata(trig);  		rtc_irq_unregister(trig_info->rtc, &trig_info->task);  		rtc_class_close(trig_info->rtc); -		kfree(trig->name);  		kfree(trig_info);  		iio_trigger_unregister(trig);  	} @@ -197,18 +196,8 @@ static struct platform_driver iio_trig_periodic_rtc_driver = {  	},  }; -static int __init iio_trig_periodic_rtc_init(void) -{ -	return platform_driver_register(&iio_trig_periodic_rtc_driver); -} - -static void __exit iio_trig_periodic_rtc_exit(void) -{ -	return platform_driver_unregister(&iio_trig_periodic_rtc_driver); -} +module_platform_driver(iio_trig_periodic_rtc_driver); -module_init(iio_trig_periodic_rtc_init); -module_exit(iio_trig_periodic_rtc_exit); -MODULE_AUTHOR("Jonathan Cameron <jic23@cam.ac.uk>"); +MODULE_AUTHOR("Jonathan Cameron <jic23@kernel.org>");  MODULE_DESCRIPTION("Periodic realtime clock  trigger for the iio subsystem");  MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/iio/trigger_consumer.h b/drivers/staging/iio/trigger_consumer.h deleted file mode 100644 index 9d52d963777..00000000000 --- a/drivers/staging/iio/trigger_consumer.h +++ /dev/null @@ -1,47 +0,0 @@ - -/* The industrial I/O core, trigger consumer handling functions - * - * Copyright (c) 2008 Jonathan Cameron - * - * 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. - */ - -#ifdef CONFIG_IIO_TRIGGER -/** - * iio_device_register_trigger_consumer() - set up an iio_dev to use triggers - * @dev_info: iio_dev associated with the device that will consume the trigger - **/ -int iio_device_register_trigger_consumer(struct iio_dev *dev_info); - -/** - * iio_device_unregister_trigger_consumer() - reverse the registration process - * @dev_info: iio_dev associated with the device that consumed the trigger - **/ -int iio_device_unregister_trigger_consumer(struct iio_dev *dev_info); - -#else - -/** - * iio_device_register_trigger_consumer() - set up an iio_dev to use triggers - * @dev_info: iio_dev associated with the device that will consume the trigger - **/ -static int iio_device_register_trigger_consumer(struct iio_dev *dev_info) -{ -	return 0; -}; - -/** - * iio_device_unregister_trigger_consumer() - reverse the registration process - * @dev_info: iio_dev associated with the device that consumed the trigger - **/ -static int iio_device_unregister_trigger_consumer(struct iio_dev *dev_info) -{ -	return 0; -}; - -#endif /* CONFIG_TRIGGER_CONSUMER */ - - -  | 
