From 6f2dfb3101bb431ae9adc827fa8526d699e9dbd0 Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Tue, 2 Mar 2010 13:35:35 +0000 Subject: staging: IIO: Fix uses of spinlocks prior to init in ring implementations Some confusion was caused by the ___iio_init_ring_buffer and equivalent in ring_sw handling both init of spin locks etc and allocation and of the actual buffer. This resulted in ring->use_lock being held before it was initialized and actually during the initialization. Some of the recent cleanups in the spin lock code seem to have triggered the bug actually causing traceable crashes. The following patch should fix this but hasn't been extensively tested as of yet and there may well be some side effects I haven't thought of. Just wanted to get this out there before anyone else runs into it! Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/staging/iio/industrialio-ring.c | 2 ++ drivers/staging/iio/ring_generic.h | 8 +++----- drivers/staging/iio/ring_sw.c | 18 +++++++++++------- 3 files changed, 16 insertions(+), 12 deletions(-) (limited to 'drivers/staging/iio') diff --git a/drivers/staging/iio/industrialio-ring.c b/drivers/staging/iio/industrialio-ring.c index e53e214..5f48632 100644 --- a/drivers/staging/iio/industrialio-ring.c +++ b/drivers/staging/iio/industrialio-ring.c @@ -266,6 +266,8 @@ void iio_ring_buffer_init(struct iio_ring_buffer *ring, ring->indio_dev = dev_info; ring->ev_int.private = ring; ring->access_handler.private = ring; + ring->shared_ev_pointer.ev_p = 0; + spin_lock_init(&ring->shared_ev_pointer.lock); } EXPORT_SYMBOL(iio_ring_buffer_init); diff --git a/drivers/staging/iio/ring_generic.h b/drivers/staging/iio/ring_generic.h index 09044ad..75e0fc0 100644 --- a/drivers/staging/iio/ring_generic.h +++ b/drivers/staging/iio/ring_generic.h @@ -134,19 +134,17 @@ void iio_ring_buffer_init(struct iio_ring_buffer *ring, struct iio_dev *dev_info); /** - * __iio_init_ring_buffer() - initialize common elements of ring buffers + * __iio_update_ring_buffer() - update common elements of ring buffers * @ring: ring buffer that is the event source * @bytes_per_datum: size of individual datum including timestamp * @length: number of datums in ring **/ -static inline void __iio_init_ring_buffer(struct iio_ring_buffer *ring, - int bytes_per_datum, int length) +static inline void __iio_update_ring_buffer(struct iio_ring_buffer *ring, + int bytes_per_datum, int length) { ring->bpd = bytes_per_datum; ring->length = length; ring->loopcount = 0; - ring->shared_ev_pointer.ev_p = 0; - spin_lock_init(&ring->shared_ev_pointer.lock); } /** diff --git a/drivers/staging/iio/ring_sw.c b/drivers/staging/iio/ring_sw.c index b104c3d..851a97e 100644 --- a/drivers/staging/iio/ring_sw.c +++ b/drivers/staging/iio/ring_sw.c @@ -14,14 +14,12 @@ #include #include "ring_sw.h" -static inline int __iio_init_sw_ring_buffer(struct iio_sw_ring_buffer *ring, - int bytes_per_datum, int length) +static inline int __iio_allocate_sw_ring_buffer(struct iio_sw_ring_buffer *ring, + int bytes_per_datum, int length) { if ((length == 0) || (bytes_per_datum == 0)) return -EINVAL; - - __iio_init_ring_buffer(&ring->buf, bytes_per_datum, length); - spin_lock_init(&ring->use_lock); + __iio_update_ring_buffer(&ring->buf, bytes_per_datum, length); ring->data = kmalloc(length*ring->buf.bpd, GFP_KERNEL); ring->read_p = 0; ring->write_p = 0; @@ -30,6 +28,11 @@ static inline int __iio_init_sw_ring_buffer(struct iio_sw_ring_buffer *ring, 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); @@ -320,7 +323,8 @@ int iio_request_update_sw_rb(struct iio_ring_buffer *r) goto error_ret; } __iio_free_sw_ring_buffer(ring); - ret = __iio_init_sw_ring_buffer(ring, ring->buf.bpd, ring->buf.length); + ret = __iio_allocate_sw_ring_buffer(ring, ring->buf.bpd, + ring->buf.length); error_ret: spin_unlock(&ring->use_lock); return ret; @@ -411,8 +415,8 @@ struct iio_ring_buffer *iio_sw_rb_allocate(struct iio_dev *indio_dev) if (!ring) return 0; 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; -- cgit v1.1 From 013659f558d90369d1a555ae3c39cc42a6c3cb74 Mon Sep 17 00:00:00 2001 From: Andrea Gelmini Date: Thu, 25 Mar 2010 18:22:37 +0100 Subject: Staging: iio: Documentation/lis3l02dqbuffersimple.c: duplicated include drivers/staging/iio/Documentation/lis3l02dqbuffersimple.c: dirent.h is included more than once. Signed-off-by: Andrea Gelmini Acked-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/staging/iio/Documentation/lis3l02dqbuffersimple.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers/staging/iio') diff --git a/drivers/staging/iio/Documentation/lis3l02dqbuffersimple.c b/drivers/staging/iio/Documentation/lis3l02dqbuffersimple.c index 2b5cfc5..6a8fa0c 100644 --- a/drivers/staging/iio/Documentation/lis3l02dqbuffersimple.c +++ b/drivers/staging/iio/Documentation/lis3l02dqbuffersimple.c @@ -19,7 +19,6 @@ #include #include -#include #include "iio_util.h" static const char *ring_access = "/dev/iio/lis3l02dq_ring_access"; -- cgit v1.1 From ceb0525c9e4f7e6a3ec762deb0960be3522030dc Mon Sep 17 00:00:00 2001 From: Barry Song <21cnbao@gmail.com> Date: Mon, 26 Apr 2010 12:19:01 +0800 Subject: Staging: iio: fix typo in userspace example codes and document Signed-off-by: Barry Song <21cnbao@gmail.com> Acked-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/staging/iio/Documentation/lis3l02dqbuffersimple.c | 2 +- drivers/staging/iio/Documentation/userspace.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/staging/iio') diff --git a/drivers/staging/iio/Documentation/lis3l02dqbuffersimple.c b/drivers/staging/iio/Documentation/lis3l02dqbuffersimple.c index 6a8fa0c..08e012f 100644 --- a/drivers/staging/iio/Documentation/lis3l02dqbuffersimple.c +++ b/drivers/staging/iio/Documentation/lis3l02dqbuffersimple.c @@ -19,7 +19,7 @@ #include #include -#include "iio_util.h" +#include "iio_utils.h" static const char *ring_access = "/dev/iio/lis3l02dq_ring_access"; static const char *ring_event = "/dev/iio/lis3l02dq_ring_event"; diff --git a/drivers/staging/iio/Documentation/userspace.txt b/drivers/staging/iio/Documentation/userspace.txt index 661015a..4838818 100644 --- a/drivers/staging/iio/Documentation/userspace.txt +++ b/drivers/staging/iio/Documentation/userspace.txt @@ -56,5 +56,5 @@ KERNEL="ring_event_line*", ID="spi1.0", DRIVER="lis3l02dq", NAME="iio/lis3l02dq_ KERNEL="event_line*", ID="spi1.0", DRIVER="lis3l02dq", NAME="iio/lis3l02dq_event" KERNEL="ring_access*", ID="spi1.0", DRIVER="lis3l02dq", NAME="iio/lis3l02dq_ring_access" -The files, lis3l02dqbuffersimple.c and iio_util.h in this directory provide an example +The files, lis3l02dqbuffersimple.c and iio_utils.h in this directory provide an example of how to use the ring buffer and event interfaces. -- cgit v1.1 From a05d7ce36c55bc6496e1085584c9df901b899ab2 Mon Sep 17 00:00:00 2001 From: Michael Hennerich Date: Mon, 26 Apr 2010 10:36:36 +0200 Subject: Staging: iio: iio-trig-gpio: Remove redundant gpio_request Remove redundant gpio_request: The GPIO used as trigger IRQ, is also requested as gpio, but actually never read. Use platform resource facility to get IRQs numbers and flags. Make sure this driver can be used with any system IRQ, not necessarily limited to GPIO-IRQs. Use dev_err(dev...) and friends instead of printk(KERN_ERR...) Signed-off-by: Michael Hennerich Acked-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/staging/iio/trigger/iio-trig-gpio.c | 127 +++++++++++++--------------- 1 file changed, 61 insertions(+), 66 deletions(-) (limited to 'drivers/staging/iio') diff --git a/drivers/staging/iio/trigger/iio-trig-gpio.c b/drivers/staging/iio/trigger/iio-trig-gpio.c index 0c3bad3..e40099f 100644 --- a/drivers/staging/iio/trigger/iio-trig-gpio.c +++ b/drivers/staging/iio/trigger/iio-trig-gpio.c @@ -13,7 +13,6 @@ * TODO: * * Add board config elements to allow specification of startup settings. - * Add configuration settings (irq type etc) */ #include @@ -31,7 +30,7 @@ DEFINE_MUTEX(iio_gpio_trigger_list_lock); struct iio_gpio_trigger_info { struct mutex in_use; - int gpio; + unsigned int irq; }; /* * Need to reference count these triggers and only enable gpio interrupts @@ -58,78 +57,77 @@ static const struct attribute_group iio_gpio_trigger_attr_group = { .attrs = iio_gpio_trigger_attrs, }; -static int iio_gpio_trigger_probe(struct platform_device *dev) +static int iio_gpio_trigger_probe(struct platform_device *pdev) { - int *pdata = dev->dev.platform_data; struct iio_gpio_trigger_info *trig_info; struct iio_trigger *trig, *trig2; - int i, irq, ret = 0; - if (!pdata) { - printk(KERN_ERR "No IIO gpio trigger platform data found\n"); - goto error_ret; - } - for (i = 0;; i++) { - if (!gpio_is_valid(pdata[i])) - break; - trig = iio_allocate_trigger(); - if (!trig) { - ret = -ENOMEM; - goto error_free_completed_registrations; - } + unsigned long irqflags; + struct resource *irq_res; + int irq, ret = 0, irq_res_cnt = 0; - trig_info = kzalloc(sizeof(*trig_info), GFP_KERNEL); - if (!trig_info) { - ret = -ENOMEM; - goto error_put_trigger; - } - trig->control_attrs = &iio_gpio_trigger_attr_group; - trig->private_data = trig_info; - trig_info->gpio = pdata[i]; - trig->owner = THIS_MODULE; - trig->name = kmalloc(IIO_TRIGGER_NAME_LENGTH, GFP_KERNEL); - if (!trig->name) { - ret = -ENOMEM; - goto error_free_trig_info; + do { + irq_res = platform_get_resource(pdev, + IORESOURCE_IRQ, irq_res_cnt); + + if (irq_res == NULL) { + if (irq_res_cnt == 0) + dev_err(&pdev->dev, "No GPIO IRQs specified"); + break; } - snprintf((char *)trig->name, - IIO_TRIGGER_NAME_LENGTH, - "gpiotrig%d", - pdata[i]); - ret = gpio_request(trig_info->gpio, trig->name); - if (ret) - goto error_free_name; - - ret = gpio_direction_input(trig_info->gpio); - if (ret) - goto error_release_gpio; - - irq = gpio_to_irq(trig_info->gpio); - if (irq < 0) { - ret = irq; - goto error_release_gpio; + irqflags = (irq_res->flags & IRQF_TRIGGER_MASK) | IRQF_SHARED; + + for (irq = irq_res->start; irq <= irq_res->end; irq++) { + + trig = iio_allocate_trigger(); + if (!trig) { + ret = -ENOMEM; + goto error_free_completed_registrations; + } + + trig_info = kzalloc(sizeof(*trig_info), GFP_KERNEL); + if (!trig_info) { + ret = -ENOMEM; + goto error_put_trigger; + } + trig->control_attrs = &iio_gpio_trigger_attr_group; + trig->private_data = trig_info; + trig_info->irq = irq; + trig->owner = THIS_MODULE; + trig->name = kmalloc(IIO_TRIGGER_NAME_LENGTH, + GFP_KERNEL); + if (!trig->name) { + ret = -ENOMEM; + goto error_free_trig_info; + } + snprintf((char *)trig->name, + IIO_TRIGGER_NAME_LENGTH, + "irqtrig%d", irq); + + ret = request_irq(irq, iio_gpio_trigger_poll, + irqflags, trig->name, trig); + if (ret) { + dev_err(&pdev->dev, + "request IRQ-%d failed", irq); + goto error_free_name; + } + + ret = iio_trigger_register(trig); + if (ret) + goto error_release_irq; + + list_add_tail(&trig->alloc_list, + &iio_gpio_trigger_list); } - ret = request_irq(irq, iio_gpio_trigger_poll, - IRQF_TRIGGER_RISING, - trig->name, - trig); - if (ret) - goto error_release_gpio; + irq_res_cnt++; + } while (irq_res != NULL); - ret = iio_trigger_register(trig); - if (ret) - goto error_release_irq; - list_add_tail(&trig->alloc_list, &iio_gpio_trigger_list); - - } return 0; /* First clean up the partly allocated trigger */ error_release_irq: free_irq(irq, trig); -error_release_gpio: - gpio_free(trig_info->gpio); error_free_name: kfree(trig->name); error_free_trig_info: @@ -143,18 +141,16 @@ error_free_completed_registrations: &iio_gpio_trigger_list, alloc_list) { trig_info = trig->private_data; - free_irq(gpio_to_irq(trig_info->gpio), trig); - gpio_free(trig_info->gpio); + free_irq(gpio_to_irq(trig_info->irq), trig); kfree(trig->name); kfree(trig_info); iio_trigger_unregister(trig); } -error_ret: return ret; } -static int iio_gpio_trigger_remove(struct platform_device *dev) +static int iio_gpio_trigger_remove(struct platform_device *pdev) { struct iio_trigger *trig, *trig2; struct iio_gpio_trigger_info *trig_info; @@ -166,8 +162,7 @@ static int iio_gpio_trigger_remove(struct platform_device *dev) alloc_list) { trig_info = trig->private_data; iio_trigger_unregister(trig); - free_irq(gpio_to_irq(trig_info->gpio), trig); - gpio_free(trig_info->gpio); + free_irq(trig_info->irq, trig); kfree(trig->name); kfree(trig_info); iio_put_trigger(trig); -- cgit v1.1 From 7c327857016116eba1595c67c25d0314d1385e85 Mon Sep 17 00:00:00 2001 From: Michael Hennerich Date: Mon, 26 Apr 2010 10:49:10 +0200 Subject: Staging: iio: iio_trigger_find_by_name: Skip trailing newline if available Skip trailing newline if available. Signed-off-by: Michael Hennerich Acked-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/staging/iio/industrialio-trigger.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers/staging/iio') diff --git a/drivers/staging/iio/industrialio-trigger.c b/drivers/staging/iio/industrialio-trigger.c index 35ec80b..3c8f6ff 100644 --- a/drivers/staging/iio/industrialio-trigger.c +++ b/drivers/staging/iio/industrialio-trigger.c @@ -156,6 +156,9 @@ struct iio_trigger *iio_trigger_find_by_name(const char *name, size_t len) struct iio_trigger *trig; bool found = false; + if (len && name[len - 1] == '\n') + len--; + mutex_lock(&iio_trigger_list_lock); list_for_each_entry(trig, &iio_trigger_list, list) { if (strncmp(trig->name, name, len) == 0) { -- cgit v1.1 From c849d2538ebeef1ac26fad7a10c18b1e0fc35161 Mon Sep 17 00:00:00 2001 From: Roel Van Nyen Date: Thu, 29 Apr 2010 19:27:31 +0200 Subject: Staging: iio: Fix remaining code style issues fix code style issues Signed-of-by: Roel Van Nyen Cc: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/staging/iio/industrialio-core.c | 2 +- drivers/staging/iio/ring_sw.c | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) (limited to 'drivers/staging/iio') diff --git a/drivers/staging/iio/industrialio-core.c b/drivers/staging/iio/industrialio-core.c index 37f58f6..7c9a12e 100644 --- a/drivers/staging/iio/industrialio-core.c +++ b/drivers/staging/iio/industrialio-core.c @@ -809,7 +809,7 @@ int iio_device_register(struct iio_dev *dev_info) ret = iio_device_register_eventset(dev_info); if (ret) { dev_err(dev_info->dev.parent, - "Failed to register event set \n"); + "Failed to register event set\n"); goto error_free_sysfs; } if (dev_info->modes & INDIO_RING_TRIGGERED) diff --git a/drivers/staging/iio/ring_sw.c b/drivers/staging/iio/ring_sw.c index 851a97e..e9570e3 100644 --- a/drivers/staging/iio/ring_sw.c +++ b/drivers/staging/iio/ring_sw.c @@ -126,8 +126,7 @@ int iio_store_to_sw_ring(struct iio_sw_ring_buffer *ring, spin_lock(&ring->buf.shared_ev_pointer.lock); ret = iio_push_or_escallate_ring_event(&ring->buf, - IIO_EVENT_CODE_RING_100_FULL, - timestamp); + IIO_EVENT_CODE_RING_100_FULL, timestamp); spin_unlock(&ring->buf.shared_ev_pointer.lock); if (ret) goto error_ret; -- cgit v1.1 From ad313b1062f0e16e5fa64e1a34eec37e1b8a3341 Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Tue, 4 May 2010 14:42:56 +0100 Subject: staging:iio: Add new in_raw definitions for adc channels. Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/staging/iio/adc/adc.h | 12 ++++++++++++ drivers/staging/iio/sysfs.h | 3 +++ 2 files changed, 15 insertions(+) (limited to 'drivers/staging/iio') diff --git a/drivers/staging/iio/adc/adc.h b/drivers/staging/iio/adc/adc.h index d925b2c..46f0d08 100644 --- a/drivers/staging/iio/adc/adc.h +++ b/drivers/staging/iio/adc/adc.h @@ -9,5 +9,17 @@ * */ +/* 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_DIFF_RAW(_nump, _numn, _show, _addr) \ + IIO_DEVICE_ATTR_NAMED(in##_nump##min##_numn##_raw, \ + in##_nump-in##_numn##_raw, \ + S_IRUGO, \ + _show, \ + NULL, \ + _addr) diff --git a/drivers/staging/iio/sysfs.h b/drivers/staging/iio/sysfs.h index e501e13..d8fe0e2 100644 --- a/drivers/staging/iio/sysfs.h +++ b/drivers/staging/iio/sysfs.h @@ -98,6 +98,9 @@ struct iio_const_attr { struct iio_dev_attr iio_dev_attr_##_name \ = IIO_ATTR(_name, _mode, _show, _store, _addr) +#define IIO_DEVICE_ATTR_NAMED(_vname, _name, _mode, _show, _store, _addr) \ + struct iio_dev_attr iio_dev_attr_##_vname \ + = IIO_ATTR(_name, _mode, _show, _store, _addr) #define IIO_DEVICE_ATTR_2(_name, _mode, _show, _store, _addr, _val2) \ struct iio_dev_attr iio_dev_attr_##_name \ -- cgit v1.1 From ff7723e203349c18d7149e7cf2a4ae928bb9da69 Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Tue, 4 May 2010 14:42:57 +0100 Subject: staging:iio: Add new attrs for sampling frequency available and temp_raw Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/staging/iio/sysfs.h | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) (limited to 'drivers/staging/iio') diff --git a/drivers/staging/iio/sysfs.h b/drivers/staging/iio/sysfs.h index d8fe0e2..afcf5ab 100644 --- a/drivers/staging/iio/sysfs.h +++ b/drivers/staging/iio/sysfs.h @@ -144,18 +144,25 @@ struct iio_const_attr { * * May be mode dependent on some devices **/ +/* Deprecated */ #define IIO_DEV_ATTR_AVAIL_SAMP_FREQ(_show) \ IIO_DEVICE_ATTR(available_sampling_frequency, S_IRUGO, _show, NULL, 0) +#define IIO_DEV_ATTR_SAMP_FREQ_AVAIL(_show) \ + IIO_DEVICE_ATTR(sampling_frequency_available, S_IRUGO, _show, NULL, 0) /** * IIO_CONST_ATTR_AVAIL_SAMP_FREQ - list available sampling frequencies * @_string: frequency string for the attribute * * Constant version **/ -#define IIO_CONST_ATTR_AVAIL_SAMP_FREQ(_string) \ +/* Deprecated */ +#define IIO_CONST_ATTR_AVAIL_SAMP_FREQ(_string) \ IIO_CONST_ATTR(available_sampling_frequency, _string) +#define IIO_CONST_ATTR_SAMP_FREQ_AVAIL(_string) \ + IIO_CONST_ATTR(sampling_frequency_available, _string) + /** * IIO_DEV_ATTR_SCAN_MODE - select a scan mode * @_mode: sysfs file mode/permissions @@ -234,6 +241,9 @@ struct iio_const_attr { #define IIO_DEV_ATTR_TEMP(_show) \ IIO_DEVICE_ATTR(temp, S_IRUGO, _show, NULL, 0) +#define IIO_DEV_ATTR_TEMP_RAW(_show) \ + IIO_DEVICE_ATTR(temp_raw, S_IRUGO, _show, NULL, 0) + /** * IIO_EVENT_SH - generic shared event handler * @_name: event name -- cgit v1.1 From f3fb001191a38a81bbc3cb363af2c279609ecc7c Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Tue, 4 May 2010 14:42:58 +0100 Subject: iio:staging:accelerometers move towards the new abi Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/staging/iio/accel/accel.h | 8 +- drivers/staging/iio/accel/kxsd9.c | 88 +++++++++------ drivers/staging/iio/accel/lis3l02dq.h | 4 - drivers/staging/iio/accel/lis3l02dq_core.c | 175 +++++++++++++++-------------- drivers/staging/iio/accel/lis3l02dq_ring.c | 8 +- drivers/staging/iio/accel/sca3000.h | 2 + drivers/staging/iio/accel/sca3000_core.c | 172 ++++++++++++++-------------- drivers/staging/iio/accel/sca3000_ring.c | 33 ++++-- drivers/staging/iio/ring_generic.h | 4 +- 9 files changed, 262 insertions(+), 232 deletions(-) (limited to 'drivers/staging/iio') diff --git a/drivers/staging/iio/accel/accel.h b/drivers/staging/iio/accel/accel.h index d7fc7f9..059209c 100644 --- a/drivers/staging/iio/accel/accel.h +++ b/drivers/staging/iio/accel/accel.h @@ -2,7 +2,6 @@ #include "../sysfs.h" /* Accelerometer types of attribute */ - #define IIO_DEV_ATTR_ACCEL_X_OFFSET(_mode, _show, _store, _addr) \ IIO_DEVICE_ATTR(accel_x_offset, _mode, _show, _store, _addr) @@ -22,13 +21,13 @@ IIO_DEVICE_ATTR(accel_z_gain, _mode, _show, _store, _addr) #define IIO_DEV_ATTR_ACCEL_X(_show, _addr) \ - IIO_DEVICE_ATTR(accel_x, S_IRUGO, _show, NULL, _addr) + IIO_DEVICE_ATTR(accel_x_raw, S_IRUGO, _show, NULL, _addr) #define IIO_DEV_ATTR_ACCEL_Y(_show, _addr) \ - IIO_DEVICE_ATTR(accel_y, S_IRUGO, _show, NULL, _addr) + IIO_DEVICE_ATTR(accel_y_raw, S_IRUGO, _show, NULL, _addr) #define IIO_DEV_ATTR_ACCEL_Z(_show, _addr) \ - IIO_DEVICE_ATTR(accel_z, S_IRUGO, _show, NULL, _addr) + IIO_DEVICE_ATTR(accel_z_raw, S_IRUGO, _show, NULL, _addr) /* Thresholds are somewhat chip dependent - may need quite a few defs here */ /* For unified thresholds (shared across all directions */ @@ -61,7 +60,6 @@ #define IIO_DEV_ATTR_ACCEL_THRESH_Z(_mode, _show, _store, _addr) \ IIO_DEVICE_ATTR(thresh_accel_z, _mode, _show, _store, _addr) - /** * IIO_EVENT_ATTR_ACCEL_X_HIGH: threshold event, x acceleration * @_show: read x acceleration high threshold diff --git a/drivers/staging/iio/accel/kxsd9.c b/drivers/staging/iio/accel/kxsd9.c index db2dd53..ae7ffe1 100644 --- a/drivers/staging/iio/accel/kxsd9.c +++ b/drivers/staging/iio/accel/kxsd9.c @@ -26,6 +26,7 @@ #include #include #include +#include #include "../iio.h" #include "../sysfs.h" @@ -51,8 +52,10 @@ #define KXSD9_READ(a) (0x80 | (a)) #define KXSD9_WRITE(a) (a) -#define IIO_DEV_ATTR_ACCEL_SET_RANGE(_mode, _show, _store) \ - IIO_DEVICE_ATTR(accel_range, _mode, _show, _store, 0) +#define KXSD9_SCALE_2G "0.011978" +#define KXSD9_SCALE_4G "0.023927" +#define KXSD9_SCALE_6G "0.035934" +#define KXSD9_SCALE_8G "0.047853" #define KXSD9_STATE_RX_SIZE 2 #define KXSD9_STATE_TX_SIZE 4 @@ -73,9 +76,9 @@ struct kxsd9_state { }; /* This may want to move to mili g to allow for non integer ranges */ -static ssize_t kxsd9_read_accel_range(struct device *dev, - struct device_attribute *attr, - char *buf) +static ssize_t kxsd9_read_scale(struct device *dev, + struct device_attribute *attr, + char *buf) { int ret; ssize_t len = 0; @@ -101,16 +104,16 @@ static ssize_t kxsd9_read_accel_range(struct device *dev, switch (st->rx[1] & KXSD9_FS_MASK) { case KXSD9_FS_8: - len += sprintf(buf, "8\n"); + len += sprintf(buf, "%s\n", KXSD9_SCALE_8G); break; case KXSD9_FS_6: - len += sprintf(buf, "6\n"); + len += sprintf(buf, "%s\n", KXSD9_SCALE_6G); break; case KXSD9_FS_4: - len += sprintf(buf, "4\n"); + len += sprintf(buf, "%s\n", KXSD9_SCALE_4G); break; case KXSD9_FS_2: - len += sprintf(buf, "2\n"); + len += sprintf(buf, "%s\n", KXSD9_SCALE_2G); break; } @@ -119,12 +122,12 @@ error_ret: return ret ? ret : len; } -static ssize_t kxsd9_write_accel_range(struct device *dev, - struct device_attribute *attr, - const char *buf, - size_t len) +static ssize_t kxsd9_write_scale(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t len) { - long readin; + struct spi_message msg; int ret; struct iio_dev *indio_dev = dev_get_drvdata(dev); @@ -145,25 +148,25 @@ static ssize_t kxsd9_write_accel_range(struct device *dev, }, }; - ret = strict_strtol(buf, 10, &readin); - if (ret) - return ret; - switch (readin) { - case 8: + if (!strncmp(buf, KXSD9_SCALE_8G, + strlen(buf) < strlen(KXSD9_SCALE_8G) + ? strlen(buf) : strlen(KXSD9_SCALE_8G))) val = KXSD9_FS_8; - break; - case 6: + else if (!strncmp(buf, KXSD9_SCALE_6G, + strlen(buf) < strlen(KXSD9_SCALE_6G) + ? strlen(buf) : strlen(KXSD9_SCALE_6G))) val = KXSD9_FS_6; - break; - case 4: + else if (!strncmp(buf, KXSD9_SCALE_4G, + strlen(buf) < strlen(KXSD9_SCALE_4G) + ? strlen(buf) : strlen(KXSD9_SCALE_4G))) val = KXSD9_FS_4; - break; - case 2: + else if (!strncmp(buf, KXSD9_SCALE_2G, + strlen(buf) < strlen(KXSD9_SCALE_2G) + ? strlen(buf) : strlen(KXSD9_SCALE_2G))) val = KXSD9_FS_2; - break; - default: + else return -EINVAL; - } + mutex_lock(&st->buf_lock); st->tx[0] = KXSD9_READ(KXSD9_REG_CTRL_C); st->tx[1] = 0; @@ -182,6 +185,7 @@ error_ret: mutex_unlock(&st->buf_lock); return ret ? ret : len; } + static ssize_t kxsd9_read_accel(struct device *dev, struct device_attribute *attr, char *buf) @@ -227,17 +231,27 @@ error_ret: static IIO_DEV_ATTR_ACCEL_X(kxsd9_read_accel, KXSD9_REG_X); static IIO_DEV_ATTR_ACCEL_Y(kxsd9_read_accel, KXSD9_REG_Y); static IIO_DEV_ATTR_ACCEL_Z(kxsd9_read_accel, KXSD9_REG_Z); -static IIO_DEV_ATTR_ADC(0, kxsd9_read_accel, KXSD9_REG_AUX); -static IIO_DEV_ATTR_ACCEL_SET_RANGE(S_IRUGO | S_IWUSR, - kxsd9_read_accel_range, - kxsd9_write_accel_range); +static IIO_DEV_ATTR_IN_RAW(0, kxsd9_read_accel, KXSD9_REG_AUX); + +static IIO_DEVICE_ATTR(accel_scale, + S_IRUGO | S_IWUSR, + kxsd9_read_scale, + kxsd9_write_scale, + 0); + +static IIO_CONST_ATTR(accel_scale_available, + KXSD9_SCALE_2G " " + KXSD9_SCALE_4G " " + KXSD9_SCALE_6G " " + KXSD9_SCALE_8G); static struct attribute *kxsd9_attributes[] = { - &iio_dev_attr_accel_x.dev_attr.attr, - &iio_dev_attr_accel_y.dev_attr.attr, - &iio_dev_attr_accel_z.dev_attr.attr, - &iio_dev_attr_adc_0.dev_attr.attr, - &iio_dev_attr_accel_range.dev_attr.attr, + &iio_dev_attr_accel_x_raw.dev_attr.attr, + &iio_dev_attr_accel_y_raw.dev_attr.attr, + &iio_dev_attr_accel_z_raw.dev_attr.attr, + &iio_dev_attr_in0_raw.dev_attr.attr, + &iio_dev_attr_accel_scale.dev_attr.attr, + &iio_const_attr_accel_scale_available.dev_attr.attr, NULL, }; diff --git a/drivers/staging/iio/accel/lis3l02dq.h b/drivers/staging/iio/accel/lis3l02dq.h index 91a5375..e76a979 100644 --- a/drivers/staging/iio/accel/lis3l02dq.h +++ b/drivers/staging/iio/accel/lis3l02dq.h @@ -179,10 +179,6 @@ int lis3l02dq_spi_read_reg_8(struct device *dev, int lis3l02dq_spi_write_reg_8(struct device *dev, u8 reg_address, u8 *val); -#define LIS3L02DQ_SCAN_ACC_X 0 -#define LIS3L02DQ_SCAN_ACC_Y 1 -#define LIS3L02DQ_SCAN_ACC_Z 2 - #ifdef CONFIG_IIO_RING_BUFFER /* At the moment triggers are only used for ring buffer diff --git a/drivers/staging/iio/accel/lis3l02dq_core.c b/drivers/staging/iio/accel/lis3l02dq_core.c index ea76902..6b5577d 100644 --- a/drivers/staging/iio/accel/lis3l02dq_core.c +++ b/drivers/staging/iio/accel/lis3l02dq_core.c @@ -458,41 +458,39 @@ err_ret: return ret; } -static IIO_DEV_ATTR_ACCEL_X_OFFSET(S_IWUSR | S_IRUGO, - lis3l02dq_read_signed, - lis3l02dq_write_signed, - LIS3L02DQ_REG_OFFSET_X_ADDR); - -static IIO_DEV_ATTR_ACCEL_Y_OFFSET(S_IWUSR | S_IRUGO, - lis3l02dq_read_signed, - lis3l02dq_write_signed, - LIS3L02DQ_REG_OFFSET_Y_ADDR); - -static IIO_DEV_ATTR_ACCEL_Z_OFFSET(S_IWUSR | S_IRUGO, - lis3l02dq_read_signed, - lis3l02dq_write_signed, - LIS3L02DQ_REG_OFFSET_Z_ADDR); - -static IIO_DEV_ATTR_ACCEL_X_GAIN(S_IWUSR | S_IRUGO, - lis3l02dq_read_unsigned, - lis3l02dq_write_unsigned, - LIS3L02DQ_REG_GAIN_X_ADDR); - -static IIO_DEV_ATTR_ACCEL_Y_GAIN(S_IWUSR | S_IRUGO, - lis3l02dq_read_unsigned, - lis3l02dq_write_unsigned, - LIS3L02DQ_REG_GAIN_Y_ADDR); - -static IIO_DEV_ATTR_ACCEL_Z_GAIN(S_IWUSR | S_IRUGO, - lis3l02dq_read_unsigned, - lis3l02dq_write_unsigned, - LIS3L02DQ_REG_GAIN_Z_ADDR); - -static IIO_DEV_ATTR_ACCEL_THRESH(S_IWUSR | S_IRUGO, - lis3l02dq_read_16bit_signed, - lis3l02dq_write_16bit_signed, - LIS3L02DQ_REG_THS_L_ADDR); - +#define LIS3L02DQ_SIGNED_ATTR(name, reg) \ + IIO_DEVICE_ATTR(name, \ + S_IWUSR | S_IRUGO, \ + lis3l02dq_read_signed, \ + lis3l02dq_write_signed, \ + reg); + +#define LIS3L02DQ_UNSIGNED_ATTR(name, reg) \ + IIO_DEVICE_ATTR(name, \ + S_IWUSR | S_IRUGO, \ + lis3l02dq_read_unsigned, \ + lis3l02dq_write_unsigned, \ + reg); + +static LIS3L02DQ_SIGNED_ATTR(accel_x_calibbias, + LIS3L02DQ_REG_OFFSET_X_ADDR); +static LIS3L02DQ_SIGNED_ATTR(accel_y_calibbias, + LIS3L02DQ_REG_OFFSET_Y_ADDR); +static LIS3L02DQ_SIGNED_ATTR(accel_z_calibbias, + LIS3L02DQ_REG_OFFSET_Z_ADDR); + +static LIS3L02DQ_UNSIGNED_ATTR(accel_x_calibscale, + LIS3L02DQ_REG_GAIN_X_ADDR); +static LIS3L02DQ_UNSIGNED_ATTR(accel_y_calibscale, + LIS3L02DQ_REG_GAIN_Y_ADDR); +static LIS3L02DQ_UNSIGNED_ATTR(accel_z_calibscale, + LIS3L02DQ_REG_GAIN_Z_ADDR); + +static IIO_DEVICE_ATTR(accel_mag_either_rising_value, + S_IWUSR | S_IRUGO, + lis3l02dq_read_16bit_signed, + lis3l02dq_write_16bit_signed, + LIS3L02DQ_REG_THS_L_ADDR); /* RFC The reading method for these will change depending on whether * ring buffer capture is in use. Is it worth making these take two * functions and let the core handle which to call, or leave as in this @@ -512,7 +510,7 @@ static IIO_DEV_ATTR_SAMP_FREQ(S_IWUSR | S_IRUGO, lis3l02dq_read_frequency, lis3l02dq_write_frequency); -static IIO_CONST_ATTR_AVAIL_SAMP_FREQ("280 560 1120 4480"); +static IIO_CONST_ATTR_SAMP_FREQ_AVAIL("280 560 1120 4480"); static ssize_t lis3l02dq_read_interrupt_config(struct device *dev, struct device_attribute *attr, @@ -522,7 +520,7 @@ static ssize_t lis3l02dq_read_interrupt_config(struct device *dev, s8 val; struct iio_event_attr *this_attr = to_iio_event_attr(attr); - ret = lis3l02dq_spi_read_reg_8(dev, + ret = lis3l02dq_spi_read_reg_8(dev->parent, LIS3L02DQ_REG_WAKE_UP_CFG_ADDR, (u8 *)&val); @@ -545,7 +543,7 @@ static ssize_t lis3l02dq_write_interrupt_config(struct device *dev, mutex_lock(&indio_dev->mlock); /* read current value */ - ret = lis3l02dq_spi_read_reg_8(dev, + ret = lis3l02dq_spi_read_reg_8(dev->parent, LIS3L02DQ_REG_WAKE_UP_CFG_ADDR, &valold); if (ret) @@ -618,7 +616,7 @@ static int lis3l02dq_thresh_handler_th(struct iio_dev *dev_info, static void lis3l02dq_thresh_handler_bh_no_check(struct work_struct *work_s) { struct iio_work_cont *wc - = container_of(work_s, struct iio_work_cont, ws_nocheck); + = container_of(work_s, struct iio_work_cont, ws); struct lis3l02dq_state *st = wc->st; u8 t; @@ -668,43 +666,51 @@ static void lis3l02dq_thresh_handler_bh_no_check(struct work_struct *work_s) /* A shared handler for a number of threshold types */ IIO_EVENT_SH(threshold, &lis3l02dq_thresh_handler_th); -IIO_EVENT_ATTR_ACCEL_X_HIGH_SH(iio_event_threshold, - lis3l02dq_read_interrupt_config, - lis3l02dq_write_interrupt_config, - LIS3L02DQ_REG_WAKE_UP_CFG_INTERRUPT_X_HIGH); - -IIO_EVENT_ATTR_ACCEL_Y_HIGH_SH(iio_event_threshold, - lis3l02dq_read_interrupt_config, - lis3l02dq_write_interrupt_config, - LIS3L02DQ_REG_WAKE_UP_CFG_INTERRUPT_Y_HIGH); - -IIO_EVENT_ATTR_ACCEL_Z_HIGH_SH(iio_event_threshold, - lis3l02dq_read_interrupt_config, - lis3l02dq_write_interrupt_config, - LIS3L02DQ_REG_WAKE_UP_CFG_INTERRUPT_Z_HIGH); - -IIO_EVENT_ATTR_ACCEL_X_LOW_SH(iio_event_threshold, - lis3l02dq_read_interrupt_config, - lis3l02dq_write_interrupt_config, - LIS3L02DQ_REG_WAKE_UP_CFG_INTERRUPT_X_LOW); - -IIO_EVENT_ATTR_ACCEL_Y_LOW_SH(iio_event_threshold, - lis3l02dq_read_interrupt_config, - lis3l02dq_write_interrupt_config, - LIS3L02DQ_REG_WAKE_UP_CFG_INTERRUPT_Y_LOW); +IIO_EVENT_ATTR_SH(accel_x_mag_pos_rising_en, + iio_event_threshold, + lis3l02dq_read_interrupt_config, + lis3l02dq_write_interrupt_config, + LIS3L02DQ_REG_WAKE_UP_CFG_INTERRUPT_X_HIGH); + +IIO_EVENT_ATTR_SH(accel_y_mag_pos_rising_en, + iio_event_threshold, + lis3l02dq_read_interrupt_config, + lis3l02dq_write_interrupt_config, + LIS3L02DQ_REG_WAKE_UP_CFG_INTERRUPT_Y_HIGH); + +IIO_EVENT_ATTR_SH(accel_z_mag_pos_rising_en, + iio_event_threshold, + lis3l02dq_read_interrupt_config, + lis3l02dq_write_interrupt_config, + LIS3L02DQ_REG_WAKE_UP_CFG_INTERRUPT_Z_HIGH); + +IIO_EVENT_ATTR_SH(accel_x_mag_neg_rising_en, + iio_event_threshold, + lis3l02dq_read_interrupt_config, + lis3l02dq_write_interrupt_config, + LIS3L02DQ_REG_WAKE_UP_CFG_INTERRUPT_X_LOW); + +IIO_EVENT_ATTR_SH(accel_y_mag_neg_rising_en, + iio_event_threshold, + lis3l02dq_read_interrupt_config, + lis3l02dq_write_interrupt_config, + LIS3L02DQ_REG_WAKE_UP_CFG_INTERRUPT_Y_LOW); + +IIO_EVENT_ATTR_SH(accel_z_mag_neg_rising_en, + iio_event_threshold, + lis3l02dq_read_interrupt_config, + lis3l02dq_write_interrupt_config, + LIS3L02DQ_REG_WAKE_UP_CFG_INTERRUPT_Z_LOW); -IIO_EVENT_ATTR_ACCEL_Z_LOW_SH(iio_event_threshold, - lis3l02dq_read_interrupt_config, - lis3l02dq_write_interrupt_config, - LIS3L02DQ_REG_WAKE_UP_CFG_INTERRUPT_Z_LOW); static struct attribute *lis3l02dq_event_attributes[] = { - &iio_event_attr_accel_x_high.dev_attr.attr, - &iio_event_attr_accel_y_high.dev_attr.attr, - &iio_event_attr_accel_z_high.dev_attr.attr, - &iio_event_attr_accel_x_low.dev_attr.attr, - &iio_event_attr_accel_y_low.dev_attr.attr, - &iio_event_attr_accel_z_low.dev_attr.attr, + &iio_event_attr_accel_x_mag_pos_rising_en.dev_attr.attr, + &iio_event_attr_accel_y_mag_pos_rising_en.dev_attr.attr, + &iio_event_attr_accel_z_mag_pos_rising_en.dev_attr.attr, + &iio_event_attr_accel_x_mag_neg_rising_en.dev_attr.attr, + &iio_event_attr_accel_y_mag_neg_rising_en.dev_attr.attr, + &iio_event_attr_accel_z_mag_neg_rising_en.dev_attr.attr, + &iio_dev_attr_accel_mag_either_rising_value.dev_attr.attr, NULL }; @@ -713,20 +719,21 @@ static struct attribute_group lis3l02dq_event_attribute_group = { }; static IIO_CONST_ATTR(name, "lis3l02dq"); +static IIO_CONST_ATTR(accel_scale, "0.00958"); static struct attribute *lis3l02dq_attributes[] = { - &iio_dev_attr_accel_x_offset.dev_attr.attr, - &iio_dev_attr_accel_y_offset.dev_attr.attr, - &iio_dev_attr_accel_z_offset.dev_attr.attr, - &iio_dev_attr_accel_x_gain.dev_attr.attr, - &iio_dev_attr_accel_y_gain.dev_attr.attr, - &iio_dev_attr_accel_z_gain.dev_attr.attr, - &iio_dev_attr_thresh.dev_attr.attr, - &iio_dev_attr_accel_x.dev_attr.attr, - &iio_dev_attr_accel_y.dev_attr.attr, - &iio_dev_attr_accel_z.dev_attr.attr, + &iio_dev_attr_accel_x_calibbias.dev_attr.attr, + &iio_dev_attr_accel_y_calibbias.dev_attr.attr, + &iio_dev_attr_accel_z_calibbias.dev_attr.attr, + &iio_dev_attr_accel_x_calibscale.dev_attr.attr, + &iio_dev_attr_accel_y_calibscale.dev_attr.attr, + &iio_dev_attr_accel_z_calibscale.dev_attr.attr, + &iio_const_attr_accel_scale.dev_attr.attr, + &iio_dev_attr_accel_x_raw.dev_attr.attr, + &iio_dev_attr_accel_y_raw.dev_attr.attr, + &iio_dev_attr_accel_z_raw.dev_attr.attr, &iio_dev_attr_sampling_frequency.dev_attr.attr, - &iio_const_attr_available_sampling_frequency.dev_attr.attr, + &iio_const_attr_sampling_frequency_available.dev_attr.attr, &iio_const_attr_name.dev_attr.attr, NULL }; diff --git a/drivers/staging/iio/accel/lis3l02dq_ring.c b/drivers/staging/iio/accel/lis3l02dq_ring.c index 9371243..bba4b09 100644 --- a/drivers/staging/iio/accel/lis3l02dq_ring.c +++ b/drivers/staging/iio/accel/lis3l02dq_ring.c @@ -75,16 +75,16 @@ error_ret: return ret; } -static IIO_SCAN_EL_C(accel_x, LIS3L02DQ_SCAN_ACC_X, IIO_SIGNED(16), +static IIO_SCAN_EL_C(accel_x, 0, IIO_SIGNED(16), LIS3L02DQ_REG_OUT_X_L_ADDR, &lis3l02dq_scan_el_set_state); -static IIO_SCAN_EL_C(accel_y, LIS3L02DQ_SCAN_ACC_Y, IIO_SIGNED(16), +static IIO_SCAN_EL_C(accel_y, 1, IIO_SIGNED(16), LIS3L02DQ_REG_OUT_Y_L_ADDR, &lis3l02dq_scan_el_set_state); -static IIO_SCAN_EL_C(accel_z, LIS3L02DQ_SCAN_ACC_Z, IIO_SIGNED(16), +static IIO_SCAN_EL_C(accel_z, 2, IIO_SIGNED(16), LIS3L02DQ_REG_OUT_Z_L_ADDR, &lis3l02dq_scan_el_set_state); -static IIO_SCAN_EL_TIMESTAMP; +static IIO_SCAN_EL_TIMESTAMP(3); static struct attribute *lis3l02dq_scan_el_attrs[] = { &iio_scan_el_accel_x.dev_attr.attr, diff --git a/drivers/staging/iio/accel/sca3000.h b/drivers/staging/iio/accel/sca3000.h index da7d3cb..e532199 100644 --- a/drivers/staging/iio/accel/sca3000.h +++ b/drivers/staging/iio/accel/sca3000.h @@ -187,6 +187,7 @@ struct sca3000_state { /** * struct sca3000_chip_info - model dependant parameters * @name: model identification + * @scale: string containing floating point scale factor * @temp_output: some devices have temperature sensors. * @measurement_mode_freq: normal mode sampling frequency * @option_mode_1: first optional mode. Not all models have one @@ -199,6 +200,7 @@ struct sca3000_state { **/ struct sca3000_chip_info { const char *name; + const char *scale; bool temp_output; int measurement_mode_freq; int option_mode_1; diff --git a/drivers/staging/iio/accel/sca3000_core.c b/drivers/staging/iio/accel/sca3000_core.c index 1c22986..45e4777 100644 --- a/drivers/staging/iio/accel/sca3000_core.c +++ b/drivers/staging/iio/accel/sca3000_core.c @@ -27,11 +27,9 @@ enum sca3000_variant { d01, - d03, e02, e04, e05, - l01, }; /* Note where option modes are not defined, the chip simply does not @@ -44,21 +42,20 @@ enum sca3000_variant { static const struct sca3000_chip_info sca3000_spi_chip_info_tbl[] = { { .name = "sca3000-d01", + .scale = " 0.0073575", .temp_output = true, .measurement_mode_freq = 250, .option_mode_1 = SCA3000_OP_MODE_BYPASS, .option_mode_1_freq = 250, }, { - /* No data sheet available - may be the same as the 3100-d03?*/ - .name = "sca3000-d03", - .temp_output = true, - }, { .name = "sca3000-e02", + .scale = "0.00981", .measurement_mode_freq = 125, .option_mode_1 = SCA3000_OP_MODE_NARROW, .option_mode_1_freq = 63, }, { .name = "sca3000-e04", + .scale = "0.01962", .measurement_mode_freq = 100, .option_mode_1 = SCA3000_OP_MODE_NARROW, .option_mode_1_freq = 50, @@ -66,18 +63,12 @@ static const struct sca3000_chip_info sca3000_spi_chip_info_tbl[] = { .option_mode_2_freq = 400, }, { .name = "sca3000-e05", + .scale = "0.0613125", .measurement_mode_freq = 200, .option_mode_1 = SCA3000_OP_MODE_NARROW, .option_mode_1_freq = 50, .option_mode_2 = SCA3000_OP_MODE_WIDE, .option_mode_2_freq = 400, - }, { - /* No data sheet available. - * Frequencies are unknown. - */ - .name = "sca3000-l01", - .temp_output = true, - .option_mode_1 = SCA3000_OP_MODE_BYPASS, }, }; @@ -327,6 +318,14 @@ error_ret: return ret ? ret : len; } +static ssize_t sca3000_show_scale(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct iio_dev *dev_info = dev_get_drvdata(dev); + struct sca3000_state *st = dev_info->dev_data; + return sprintf(buf, "%s\n", st->info->scale); +} static ssize_t sca3000_show_name(struct device *dev, struct device_attribute *attr, @@ -495,7 +494,7 @@ error_ret: /* Not even vaguely standard attributes so defined here rather than * in the relevant IIO core headers */ -static IIO_DEVICE_ATTR(available_measurement_modes, S_IRUGO, +static IIO_DEVICE_ATTR(measurement_mode_available, S_IRUGO, sca3000_show_available_measurement_modes, NULL, 0); @@ -508,6 +507,8 @@ static IIO_DEVICE_ATTR(measurement_mode, S_IRUGO | S_IWUSR, static IIO_DEV_ATTR_NAME(sca3000_show_name); static IIO_DEV_ATTR_REV(sca3000_show_rev); +static IIO_DEVICE_ATTR(accel_scale, S_IRUGO, sca3000_show_scale, + NULL, 0); static IIO_DEV_ATTR_ACCEL_X(sca3000_read_13bit_signed, SCA3000_REG_ADDR_X_MSB); @@ -683,7 +684,7 @@ error_free_lock: /* Should only really be registered if ring buffer support is compiled in. * Does no harm however and doing it right would add a fair bit of complexity */ -static IIO_DEV_ATTR_AVAIL_SAMP_FREQ(sca3000_read_av_freq); +static IIO_DEV_ATTR_SAMP_FREQ_AVAIL(sca3000_read_av_freq); static IIO_DEV_ATTR_SAMP_FREQ(S_IWUSR | S_IRUGO, sca3000_read_frequency, @@ -718,7 +719,10 @@ static ssize_t sca3000_read_temp(struct device *dev, error_ret: return ret; } -static IIO_DEV_ATTR_TEMP(sca3000_read_temp); +static IIO_DEV_ATTR_TEMP_RAW(sca3000_read_temp); + +static IIO_CONST_ATTR(temp_scale, "0.555556"); +static IIO_CONST_ATTR(temp_offset, "-214.6"); /** * sca3000_show_thresh() sysfs query of a threshold @@ -770,31 +774,34 @@ static ssize_t sca3000_write_thresh(struct device *dev, return ret ? ret : len; } -static IIO_DEV_ATTR_ACCEL_THRESH_X(S_IRUGO | S_IWUSR, - sca3000_show_thresh, - sca3000_write_thresh, - SCA3000_REG_CTRL_SEL_MD_X_TH); -static IIO_DEV_ATTR_ACCEL_THRESH_Y(S_IRUGO | S_IWUSR, - sca3000_show_thresh, - sca3000_write_thresh, - SCA3000_REG_CTRL_SEL_MD_Y_TH); -static IIO_DEV_ATTR_ACCEL_THRESH_Z(S_IRUGO | S_IWUSR, - sca3000_show_thresh, - sca3000_write_thresh, - SCA3000_REG_CTRL_SEL_MD_Z_TH); +static IIO_DEVICE_ATTR(accel_x_mag_either_rising_value, + S_IRUGO | S_IWUSR, + sca3000_show_thresh, + sca3000_write_thresh, + SCA3000_REG_CTRL_SEL_MD_X_TH); + +static IIO_DEVICE_ATTR(accel_y_mag_either_rising_value, + S_IRUGO | S_IWUSR, + sca3000_show_thresh, + sca3000_write_thresh, + SCA3000_REG_CTRL_SEL_MD_Y_TH); + +static IIO_DEVICE_ATTR(accel_z_mag_either_rising_value, + S_IRUGO | S_IWUSR, + sca3000_show_thresh, + sca3000_write_thresh, + SCA3000_REG_CTRL_SEL_MD_Z_TH); static struct attribute *sca3000_attributes[] = { &iio_dev_attr_name.dev_attr.attr, &iio_dev_attr_revision.dev_attr.attr, - &iio_dev_attr_accel_x.dev_attr.attr, - &iio_dev_attr_accel_y.dev_attr.attr, - &iio_dev_attr_accel_z.dev_attr.attr, - &iio_dev_attr_thresh_accel_x.dev_attr.attr, - &iio_dev_attr_thresh_accel_y.dev_attr.attr, - &iio_dev_attr_thresh_accel_z.dev_attr.attr, - &iio_dev_attr_available_measurement_modes.dev_attr.attr, + &iio_dev_attr_accel_scale.dev_attr.attr, + &iio_dev_attr_accel_x_raw.dev_attr.attr, + &iio_dev_attr_accel_y_raw.dev_attr.attr, + &iio_dev_attr_accel_z_raw.dev_attr.attr, + &iio_dev_attr_measurement_mode_available.dev_attr.attr, &iio_dev_attr_measurement_mode.dev_attr.attr, - &iio_dev_attr_available_sampling_frequency.dev_attr.attr, + &iio_dev_attr_sampling_frequency_available.dev_attr.attr, &iio_dev_attr_sampling_frequency.dev_attr.attr, NULL, }; @@ -802,18 +809,18 @@ static struct attribute *sca3000_attributes[] = { static struct attribute *sca3000_attributes_with_temp[] = { &iio_dev_attr_name.dev_attr.attr, &iio_dev_attr_revision.dev_attr.attr, - &iio_dev_attr_accel_x.dev_attr.attr, - &iio_dev_attr_accel_y.dev_attr.attr, - &iio_dev_attr_accel_z.dev_attr.attr, - &iio_dev_attr_thresh_accel_x.dev_attr.attr, - &iio_dev_attr_thresh_accel_y.dev_attr.attr, - &iio_dev_attr_thresh_accel_z.dev_attr.attr, - &iio_dev_attr_available_measurement_modes.dev_attr.attr, + &iio_dev_attr_accel_scale.dev_attr.attr, + &iio_dev_attr_accel_x_raw.dev_attr.attr, + &iio_dev_attr_accel_y_raw.dev_attr.attr, + &iio_dev_attr_accel_z_raw.dev_attr.attr, + &iio_dev_attr_measurement_mode_available.dev_attr.attr, &iio_dev_attr_measurement_mode.dev_attr.attr, - &iio_dev_attr_available_sampling_frequency.dev_attr.attr, + &iio_dev_attr_sampling_frequency_available.dev_attr.attr, &iio_dev_attr_sampling_frequency.dev_attr.attr, /* Only present if temp sensor is */ - &iio_dev_attr_temp.dev_attr.attr, + &iio_dev_attr_temp_raw.dev_attr.attr, + &iio_const_attr_temp_offset.dev_attr.attr, + &iio_const_attr_temp_scale.dev_attr.attr, NULL, }; @@ -910,7 +917,7 @@ static ssize_t sca3000_query_mo_det(struct device *dev, struct device_attribute *attr, char *buf) { - struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct iio_dev *indio_dev = dev_get_drvdata(dev->parent); struct sca3000_state *st = indio_dev->dev_data; struct iio_event_attr *this_attr = to_iio_event_attr(attr); int ret, len = 0; @@ -975,7 +982,7 @@ static ssize_t sca3000_query_ring_int(struct device *dev, struct iio_event_attr *this_attr = to_iio_event_attr(attr); int ret, len; u8 *rx; - struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct iio_dev *indio_dev = dev_get_drvdata(dev->parent); struct sca3000_state *st = indio_dev->dev_data; mutex_lock(&st->lock); ret = sca3000_read_data(st, SCA3000_REG_ADDR_INT_MASK, &rx, 1); @@ -995,7 +1002,7 @@ static ssize_t sca3000_set_ring_int(struct device *dev, const char *buf, size_t len) { - struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct iio_dev *indio_dev = dev_get_drvdata(dev->parent); struct sca3000_state *st = indio_dev->dev_data; struct iio_event_attr *this_attr = to_iio_event_attr(attr); @@ -1085,7 +1092,7 @@ static ssize_t sca3000_set_mo_det(struct device *dev, const char *buf, size_t len) { - struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct iio_dev *indio_dev = dev_get_drvdata(dev->parent); struct sca3000_state *st = indio_dev->dev_data; struct iio_event_attr *this_attr = to_iio_event_attr(attr); long val; @@ -1155,20 +1162,23 @@ IIO_EVENT_ATTR_FREE_FALL_DETECT_SH(iio_event_all, 0) /* Motion detector related event attributes */ -IIO_EVENT_ATTR_ACCEL_X_HIGH_SH(iio_event_all, - sca3000_query_mo_det, - sca3000_set_mo_det, - SCA3000_MD_CTRL_OR_X); - -IIO_EVENT_ATTR_ACCEL_Y_HIGH_SH(iio_event_all, - sca3000_query_mo_det, - sca3000_set_mo_det, - SCA3000_MD_CTRL_OR_Y); - -IIO_EVENT_ATTR_ACCEL_Z_HIGH_SH(iio_event_all, - sca3000_query_mo_det, - sca3000_set_mo_det, - SCA3000_MD_CTRL_OR_Z); +IIO_EVENT_ATTR_SH(accel_x_mag_either_rising_en, + iio_event_all, + sca3000_query_mo_det, + sca3000_set_mo_det, + SCA3000_MD_CTRL_OR_X); + +IIO_EVENT_ATTR_SH(accel_y_mag_either_rising_en, + iio_event_all, + sca3000_query_mo_det, + sca3000_set_mo_det, + SCA3000_MD_CTRL_OR_Y); + +IIO_EVENT_ATTR_SH(accel_z_mag_either_rising_en, + iio_event_all, + sca3000_query_mo_det, + sca3000_set_mo_det, + SCA3000_MD_CTRL_OR_Z); /* Hardware ring buffer related event attributes */ IIO_EVENT_ATTR_RING_50_FULL_SH(iio_event_all, @@ -1183,11 +1193,14 @@ IIO_EVENT_ATTR_RING_75_FULL_SH(iio_event_all, static struct attribute *sca3000_event_attributes[] = { &iio_event_attr_free_fall.dev_attr.attr, - &iio_event_attr_accel_x_high.dev_attr.attr, - &iio_event_attr_accel_y_high.dev_attr.attr, - &iio_event_attr_accel_z_high.dev_attr.attr, + &iio_event_attr_accel_x_mag_either_rising_en.dev_attr.attr, + &iio_event_attr_accel_y_mag_either_rising_en.dev_attr.attr, + &iio_event_attr_accel_z_mag_either_rising_en.dev_attr.attr, &iio_event_attr_ring_50_full.dev_attr.attr, &iio_event_attr_ring_75_full.dev_attr.attr, + &iio_dev_attr_accel_x_mag_either_rising_value.dev_attr.attr, + &iio_dev_attr_accel_y_mag_either_rising_value.dev_attr.attr, + &iio_dev_attr_accel_z_mag_either_rising_value.dev_attr.attr, NULL, }; @@ -1344,9 +1357,10 @@ static int __devinit __sca3000_probe(struct spi_device *spi, * is overkill. At very least a simpler registration method * might be worthwhile. */ - iio_add_event_to_list(iio_event_attr_accel_z_high.listel, - &st->indio_dev - ->interrupts[0]->ev_list); + iio_add_event_to_list( + iio_event_attr_accel_z_mag_either_rising_en.listel, + &st->indio_dev + ->interrupts[0]->ev_list); } sca3000_register_ring_funcs(st->indio_dev); ret = sca3000_clean_setup(st); @@ -1437,9 +1451,6 @@ static int sca3000_remove(struct spi_device *spi) SCA3000_VARIANT_PROBE(d01); static SCA3000_VARIANT_SPI_DRIVER(d01); -SCA3000_VARIANT_PROBE(d03); -static SCA3000_VARIANT_SPI_DRIVER(d03); - SCA3000_VARIANT_PROBE(e02); static SCA3000_VARIANT_SPI_DRIVER(e02); @@ -1449,9 +1460,6 @@ static SCA3000_VARIANT_SPI_DRIVER(e04); SCA3000_VARIANT_PROBE(e05); static SCA3000_VARIANT_SPI_DRIVER(e05); -SCA3000_VARIANT_PROBE(l01); -static SCA3000_VARIANT_SPI_DRIVER(l01); - static __init int sca3000_init(void) { int ret; @@ -1459,32 +1467,22 @@ static __init int sca3000_init(void) ret = spi_register_driver(&sca3000_d01_driver); if (ret) goto error_ret; - ret = spi_register_driver(&sca3000_d03_driver); - if (ret) - goto error_unreg_d01; ret = spi_register_driver(&sca3000_e02_driver); if (ret) - goto error_unreg_d03; + goto error_unreg_d01; ret = spi_register_driver(&sca3000_e04_driver); if (ret) goto error_unreg_e02; ret = spi_register_driver(&sca3000_e05_driver); if (ret) goto error_unreg_e04; - ret = spi_register_driver(&sca3000_l01_driver); - if (ret) - goto error_unreg_e05; return 0; -error_unreg_e05: - spi_unregister_driver(&sca3000_e05_driver); error_unreg_e04: spi_unregister_driver(&sca3000_e04_driver); error_unreg_e02: spi_unregister_driver(&sca3000_e02_driver); -error_unreg_d03: - spi_unregister_driver(&sca3000_d03_driver); error_unreg_d01: spi_unregister_driver(&sca3000_d01_driver); error_ret: @@ -1494,11 +1492,9 @@ error_ret: static __exit void sca3000_exit(void) { - spi_unregister_driver(&sca3000_l01_driver); spi_unregister_driver(&sca3000_e05_driver); spi_unregister_driver(&sca3000_e04_driver); spi_unregister_driver(&sca3000_e02_driver); - spi_unregister_driver(&sca3000_d03_driver); spi_unregister_driver(&sca3000_d01_driver); } diff --git a/drivers/staging/iio/accel/sca3000_ring.c b/drivers/staging/iio/accel/sca3000_ring.c index 40cbab2..2b39e6f 100644 --- a/drivers/staging/iio/accel/sca3000_ring.c +++ b/drivers/staging/iio/accel/sca3000_ring.c @@ -186,11 +186,29 @@ static ssize_t sca3000_store_ring_bpse(struct device *dev, return ret ? ret : len; } -static IIO_CONST_ATTR(bpse_available, "8 11"); +static IIO_SCAN_EL_C(accel_x, 0, 0, 0, 0); +static IIO_SCAN_EL_C(accel_y, 1, 0, 0, 0); +static IIO_SCAN_EL_C(accel_z, 2, 0, 0, 0); +static IIO_CONST_ATTR(accel_precision_available, "8 11"); +static IIO_DEVICE_ATTR(accel_precision, + S_IRUGO | S_IWUSR, + sca3000_show_ring_bpse, + sca3000_store_ring_bpse, + 0); + +static struct attribute *sca3000_scan_el_attrs[] = { + &iio_scan_el_accel_x.dev_attr.attr, + &iio_scan_el_accel_y.dev_attr.attr, + &iio_scan_el_accel_z.dev_attr.attr, + &iio_const_attr_accel_precision_available.dev_attr.attr, + &iio_dev_attr_accel_precision.dev_attr.attr, + NULL +}; -static IIO_DEV_ATTR_BPSE(S_IRUGO | S_IWUSR, - sca3000_show_ring_bpse, - sca3000_store_ring_bpse); +static struct attribute_group sca3000_scan_el_group = { + .attrs = sca3000_scan_el_attrs, + .name = "scan_elements", +}; /* * Ring buffer attributes @@ -198,17 +216,15 @@ static IIO_DEV_ATTR_BPSE(S_IRUGO | S_IWUSR, * only apply to the ring buffer. At all times full rate and accuracy * is available via direct reading from registers. */ -static struct attribute *iio_ring_attributes[] = { +static struct attribute *sca3000_ring_attributes[] = { &dev_attr_length.attr, &dev_attr_bps.attr, &dev_attr_ring_enable.attr, - &iio_dev_attr_bpse.dev_attr.attr, - &iio_const_attr_bpse_available.dev_attr.attr, NULL, }; static struct attribute_group sca3000_ring_attr = { - .attrs = iio_ring_attributes, + .attrs = sca3000_ring_attributes, }; static const struct attribute_group *sca3000_ring_attr_groups[] = { @@ -248,6 +264,7 @@ static inline void sca3000_rb_free(struct iio_ring_buffer *r) int sca3000_configure_ring(struct iio_dev *indio_dev) { + indio_dev->scan_el_attrs = &sca3000_scan_el_group; indio_dev->ring = sca3000_rb_allocate(indio_dev); if (indio_dev->ring == NULL) return -ENOMEM; diff --git a/drivers/staging/iio/ring_generic.h b/drivers/staging/iio/ring_generic.h index 75e0fc0..c482074 100644 --- a/drivers/staging/iio/ring_generic.h +++ b/drivers/staging/iio/ring_generic.h @@ -248,9 +248,9 @@ ssize_t iio_scan_el_ts_show(struct device *dev, struct device_attribute *attr, * * Odd one out. Handled slightly differently from other scan elements. **/ -#define IIO_SCAN_EL_TIMESTAMP \ +#define IIO_SCAN_EL_TIMESTAMP(number) \ struct iio_scan_el iio_scan_el_timestamp = { \ - .dev_attr = __ATTR(scan_en_timestamp, \ + .dev_attr = __ATTR(number##_timestamp_en, \ S_IRUGO | S_IWUSR, \ iio_scan_el_ts_show, \ iio_scan_el_ts_store), \ -- cgit v1.1 From e5c003ae82865c43feca9e11fb291ec2304a63d1 Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Tue, 4 May 2010 14:42:59 +0100 Subject: staging:iio: Support functions for scan mask matching Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/staging/iio/iio.h | 45 +++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 41 insertions(+), 4 deletions(-) (limited to 'drivers/staging/iio') diff --git a/drivers/staging/iio/iio.h b/drivers/staging/iio/iio.h index 71dbfe1..a12072a 100644 --- a/drivers/staging/iio/iio.h +++ b/drivers/staging/iio/iio.h @@ -96,6 +96,7 @@ void iio_remove_event_from_list(struct iio_event_handler_list *el, * control method is used * @scan_count: [INTERN] the number of elements in the current scan mode * @scan_mask: [INTERN] bitmask used in masking scan mode elements + * @available_scan_masks: [DRIVER] optional array of allowed bitmasks * @scan_timestamp: [INTERN] does the scan mode include a timestamp * @trig: [INTERN] current device trigger (ring buffer modes) * @pollfunc: [DRIVER] function run on trigger being recieved @@ -122,7 +123,8 @@ struct iio_dev { struct attribute_group *scan_el_attrs; int scan_count; - u16 scan_mask; + u32 scan_mask; + u32 *available_scan_masks; bool scan_timestamp; struct iio_trigger *trig; struct iio_poll_func *pollfunc; @@ -132,22 +134,57 @@ struct iio_dev { * These are mainly provided to allow for a change of implementation if a device * has a large number of scan elements */ -#define IIO_MAX_SCAN_LENGTH 15 +#define IIO_MAX_SCAN_LENGTH 31 + +/* note 0 used as error indicator as it doesn't make sense. */ +static inline u32 iio_scan_mask_match(u32 *av_masks, u32 mask) +{ + while (*av_masks) { + if (!(~*av_masks & mask)) + return *av_masks; + av_masks++; + } + return 0; +} static inline int iio_scan_mask_query(struct iio_dev *dev_info, int bit) { + u32 mask; + if (bit > IIO_MAX_SCAN_LENGTH) return -EINVAL; + + if (!dev_info->scan_mask) + return 0; + + if (dev_info->available_scan_masks) + mask = iio_scan_mask_match(dev_info->available_scan_masks, + dev_info->scan_mask); else - return !!(dev_info->scan_mask & (1 << bit)); + mask = dev_info->scan_mask; + + if (!mask) + return -EINVAL; + + return !!(mask & (1 << bit)); }; static inline int iio_scan_mask_set(struct iio_dev *dev_info, int bit) { + u32 mask; + u32 trialmask = dev_info->scan_mask | (1 << bit); + if (bit > IIO_MAX_SCAN_LENGTH) return -EINVAL; - dev_info->scan_mask |= (1 << bit); + if (dev_info->available_scan_masks) { + mask = iio_scan_mask_match(dev_info->available_scan_masks, + trialmask); + if (!mask) + return -EINVAL; + } + dev_info->scan_mask = trialmask; dev_info->scan_count++; + return 0; }; -- cgit v1.1 From 5aaaeba82e00958ecb2c890b4953a249bbde9426 Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Tue, 4 May 2010 14:43:00 +0100 Subject: staging: iio: Move from class to bus Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/staging/iio/iio.h | 2 +- drivers/staging/iio/industrialio-core.c | 28 +++++++++++----------------- drivers/staging/iio/industrialio-ring.c | 2 +- drivers/staging/iio/industrialio-trigger.c | 2 +- drivers/staging/iio/ring_sw.c | 2 +- 5 files changed, 15 insertions(+), 21 deletions(-) (limited to 'drivers/staging/iio') diff --git a/drivers/staging/iio/iio.h b/drivers/staging/iio/iio.h index a12072a..fcee47c 100644 --- a/drivers/staging/iio/iio.h +++ b/drivers/staging/iio/iio.h @@ -377,7 +377,7 @@ void iio_deallocate_chrdev(struct iio_handler *handler); #define IIO_UNSIGNED(a) (a) extern dev_t iio_devt; -extern struct class iio_class; +extern struct bus_type iio_bus_type; /** * iio_put_device() - reference counted deallocation of struct device diff --git a/drivers/staging/iio/industrialio-core.c b/drivers/staging/iio/industrialio-core.c index 7c9a12e..ad830b6 100644 --- a/drivers/staging/iio/industrialio-core.c +++ b/drivers/staging/iio/industrialio-core.c @@ -42,16 +42,10 @@ dev_t iio_devt; EXPORT_SYMBOL(iio_devt); #define IIO_DEV_MAX 256 -static char *iio_devnode(struct device *dev, mode_t *mode) -{ - return kasprintf(GFP_KERNEL, "iio/%s", dev_name(dev)); -} - -struct class iio_class = { +struct bus_type iio_bus_type = { .name = "iio", - .devnode = iio_devnode, }; -EXPORT_SYMBOL(iio_class); +EXPORT_SYMBOL(iio_bus_type); void __iio_change_event(struct iio_detected_event_list *ev, int ev_code, @@ -405,7 +399,7 @@ int iio_setup_ev_int(struct iio_event_interface *ev_int, { int ret, minor; - ev_int->dev.class = &iio_class; + ev_int->dev.bus = &iio_bus_type; ev_int->dev.parent = dev; ev_int->dev.type = &iio_event_type; device_initialize(&ev_int->dev); @@ -478,23 +472,23 @@ static int __init iio_init(void) { int ret; - /* Create sysfs class */ - ret = class_register(&iio_class); + /* Register sysfs bus */ + ret = bus_register(&iio_bus_type); if (ret < 0) { printk(KERN_ERR - "%s could not create sysfs class\n", + "%s could not register bus type\n", __FILE__); goto error_nothing; } ret = iio_dev_init(); if (ret < 0) - goto error_unregister_class; + goto error_unregister_bus_type; return 0; -error_unregister_class: - class_unregister(&iio_class); +error_unregister_bus_type: + bus_unregister(&iio_bus_type); error_nothing: return ret; } @@ -502,7 +496,7 @@ error_nothing: static void __exit iio_exit(void) { iio_dev_exit(); - class_unregister(&iio_class); + bus_unregister(&iio_bus_type); } static int iio_device_register_sysfs(struct iio_dev *dev_info) @@ -768,7 +762,7 @@ struct iio_dev *iio_allocate_device(void) if (dev) { dev->dev.type = &iio_dev_type; - dev->dev.class = &iio_class; + dev->dev.bus = &iio_bus_type; device_initialize(&dev->dev); dev_set_drvdata(&dev->dev, (void *)dev); mutex_init(&dev->mlock); diff --git a/drivers/staging/iio/industrialio-ring.c b/drivers/staging/iio/industrialio-ring.c index 5f48632..690df91 100644 --- a/drivers/staging/iio/industrialio-ring.c +++ b/drivers/staging/iio/industrialio-ring.c @@ -210,7 +210,7 @@ __iio_request_ring_buffer_access_chrdev(struct iio_ring_buffer *buf, buf->access_handler.flags = 0; buf->access_dev.parent = &buf->dev; - buf->access_dev.class = &iio_class; + buf->access_dev.bus = &iio_bus_type; buf->access_dev.type = &iio_ring_access_type; device_initialize(&buf->access_dev); diff --git a/drivers/staging/iio/industrialio-trigger.c b/drivers/staging/iio/industrialio-trigger.c index 3c8f6ff..918b0fd 100644 --- a/drivers/staging/iio/industrialio-trigger.c +++ b/drivers/staging/iio/industrialio-trigger.c @@ -365,7 +365,7 @@ struct iio_trigger *iio_allocate_trigger(void) trig = kzalloc(sizeof *trig, GFP_KERNEL); if (trig) { trig->dev.type = &iio_trig_type; - trig->dev.class = &iio_class; + trig->dev.bus = &iio_bus_type; device_initialize(&trig->dev); dev_set_drvdata(&trig->dev, (void *)trig); spin_lock_init(&trig->pollfunc_list_lock); diff --git a/drivers/staging/iio/ring_sw.c b/drivers/staging/iio/ring_sw.c index e9570e3..f8de45d 100644 --- a/drivers/staging/iio/ring_sw.c +++ b/drivers/staging/iio/ring_sw.c @@ -419,7 +419,7 @@ struct iio_ring_buffer *iio_sw_rb_allocate(struct iio_dev *indio_dev) buf->dev.type = &iio_sw_ring_type; device_initialize(&buf->dev); buf->dev.parent = &indio_dev->dev; - buf->dev.class = &iio_class; + buf->dev.bus = &iio_bus_type; dev_set_drvdata(&buf->dev, (void *)buf); return buf; -- cgit v1.1 From 5cba220b0a3211befd5514cbd822a97578ef5ed4 Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Tue, 4 May 2010 14:43:01 +0100 Subject: staging:iio: Move event attributes into the event[n] device in sysfs Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/staging/iio/industrialio-core.c | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) (limited to 'drivers/staging/iio') diff --git a/drivers/staging/iio/industrialio-core.c b/drivers/staging/iio/industrialio-core.c index ad830b6..a4ce221 100644 --- a/drivers/staging/iio/industrialio-core.c +++ b/drivers/staging/iio/industrialio-core.c @@ -676,16 +676,14 @@ static int iio_device_register_eventset(struct iio_dev *dev_info) dev_info->event_interfaces[i].id); goto error_free_setup_ev_ints; } - } - for (i = 0; i < dev_info->num_interrupt_lines; i++) { - snprintf(dev_info->event_interfaces[i]._attrname, 20, - "event_line%d_sources", i); - dev_info->event_attrs[i].name - = (const char *) - (dev_info->event_interfaces[i]._attrname); - ret = sysfs_create_group(&dev_info->dev.kobj, - &dev_info->event_attrs[i]); + dev_set_drvdata(&dev_info->event_interfaces[i].dev, + (void *)dev_info); + ret = sysfs_create_group(&dev_info + ->event_interfaces[i] + .dev.kobj, + &dev_info->event_attrs[i]); + if (ret) { dev_err(&dev_info->dev, "Failed to register sysfs for event attrs"); @@ -707,13 +705,13 @@ error_unregister_config_attrs: i = dev_info->num_interrupt_lines - 1; error_remove_sysfs_interfaces: for (j = 0; j < i; j++) - sysfs_remove_group(&dev_info->dev.kobj, + sysfs_remove_group(&dev_info + ->event_interfaces[j].dev.kobj, &dev_info->event_attrs[j]); - i = dev_info->num_interrupt_lines - 1; error_free_setup_ev_ints: for (j = 0; j < i; j++) { iio_free_idr_val(&iio_event_idr, - dev_info->event_interfaces[i].id); + dev_info->event_interfaces[j].id); iio_free_ev_int(&dev_info->event_interfaces[j]); } kfree(dev_info->interrupts); @@ -731,7 +729,8 @@ static void iio_device_unregister_eventset(struct iio_dev *dev_info) if (dev_info->num_interrupt_lines == 0) return; for (i = 0; i < dev_info->num_interrupt_lines; i++) - sysfs_remove_group(&dev_info->dev.kobj, + sysfs_remove_group(&dev_info + ->event_interfaces[i].dev.kobj, &dev_info->event_attrs[i]); for (i = 0; i < dev_info->num_interrupt_lines; i++) { -- cgit v1.1 From 1722762cead933994883fa57464efd45cf892ed7 Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Tue, 4 May 2010 14:43:02 +0100 Subject: staging:iio: Clean out unused IIO_SCAN_EL and add IIO_SCAN_NAMED_EL_C Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/staging/iio/ring_generic.h | 36 +++++++++++++++--------------------- 1 file changed, 15 insertions(+), 21 deletions(-) (limited to 'drivers/staging/iio') diff --git a/drivers/staging/iio/ring_generic.h b/drivers/staging/iio/ring_generic.h index c482074..d74b5ae 100644 --- a/drivers/staging/iio/ring_generic.h +++ b/drivers/staging/iio/ring_generic.h @@ -196,25 +196,6 @@ ssize_t iio_scan_el_store(struct device *dev, struct device_attribute *attr, **/ ssize_t iio_scan_el_show(struct device *dev, struct device_attribute *attr, char *buf); -/** - * IIO_SCAN_EL - declare and initialize a scan element without control func - * @_name: identifying name. Resulting struct is iio_scan_el_##_name, - * sysfs element, scan_en_##_name. - * @_number: unique id number for the scan element. - * @_bits: number of bits in the scan element result (used in mixed bit - * length devices). - * @_label: indentification variable used by drivers. Often a reg address. - **/ -#define IIO_SCAN_EL(_name, _number, _bits, _label) \ - struct iio_scan_el iio_scan_el_##_name = { \ - .dev_attr = __ATTR(scan_en_##_name, \ - S_IRUGO | S_IWUSR, \ - iio_scan_el_show, \ - iio_scan_el_store), \ - .mask = (1 << _number), \ - .bit_count = _bits, \ - .label = _label, \ - } ssize_t iio_scan_el_ts_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t len); @@ -225,7 +206,7 @@ ssize_t iio_scan_el_ts_show(struct device *dev, struct device_attribute *attr, * IIO_SCAN_EL_C - declare and initialize a scan element with a control func * * @_name: identifying name. Resulting struct is iio_scan_el_##_name, - * sysfs element, scan_en_##_name. + * sysfs element, _name##_en. * @_number: unique id number for the scan element. * @_bits: number of bits in the scan element result (used in mixed bit * length devices). @@ -234,7 +215,7 @@ ssize_t iio_scan_el_ts_show(struct device *dev, struct device_attribute *attr, **/ #define IIO_SCAN_EL_C(_name, _number, _bits, _label, _controlfunc) \ struct iio_scan_el iio_scan_el_##_name = { \ - .dev_attr = __ATTR(scan_en_##_name, \ + .dev_attr = __ATTR(_number##_##_name##_en, \ S_IRUGO | S_IWUSR, \ iio_scan_el_show, \ iio_scan_el_store), \ @@ -243,6 +224,19 @@ ssize_t iio_scan_el_ts_show(struct device *dev, struct device_attribute *attr, .label = _label, \ .set_state = _controlfunc, \ } + +#define IIO_SCAN_NAMED_EL_C(_name, _string, _number, _bits, _label, _cf) \ + struct iio_scan_el iio_scan_el_##_name = { \ + .dev_attr = __ATTR(_number##_##_string##_en, \ + S_IRUGO | S_IWUSR, \ + iio_scan_el_show, \ + iio_scan_el_store), \ + .number = _number, \ + .bit_count = _bits, \ + .label = _label, \ + .set_state = _cf, \ + } + /** * IIO_SCAN_EL_TIMESTAMP - declare a special scan element for timestamps * -- cgit v1.1 From 82020b0ef15de0cde1082ba0ff427c2f47a9a011 Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Tue, 4 May 2010 14:43:03 +0100 Subject: staging:iio:max1363 move to new abi. Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/staging/iio/adc/Kconfig | 1 + drivers/staging/iio/adc/Makefile | 2 +- drivers/staging/iio/adc/max1363.h | 122 +++--- drivers/staging/iio/adc/max1363_core.c | 762 +++++++++++++++++++-------------- drivers/staging/iio/adc/max1363_ring.c | 65 +-- 5 files changed, 548 insertions(+), 404 deletions(-) (limited to 'drivers/staging/iio') diff --git a/drivers/staging/iio/adc/Kconfig b/drivers/staging/iio/adc/Kconfig index 3989c0c..101ea4b 100644 --- a/drivers/staging/iio/adc/Kconfig +++ b/drivers/staging/iio/adc/Kconfig @@ -7,6 +7,7 @@ config MAX1363 tristate "MAXIM max1363 ADC driver" depends on I2C select IIO_TRIGGER if IIO_RING_BUFFER + select MAX1363_RING_BUFFER help Say yes here to build support for many MAXIM i2c analog to digital convertors (ADC). (max1361, max1362, max1363, max1364, max1136, diff --git a/drivers/staging/iio/adc/Makefile b/drivers/staging/iio/adc/Makefile index 08cee5c2..18c9376 100644 --- a/drivers/staging/iio/adc/Makefile +++ b/drivers/staging/iio/adc/Makefile @@ -3,6 +3,6 @@ # max1363-y := max1363_core.o -max1363-$(CONFIG_MAX1363_RING_BUFFER) += max1363_ring.o +max1363-y += max1363_ring.o obj-$(CONFIG_MAX1363) += max1363.o diff --git a/drivers/staging/iio/adc/max1363.h b/drivers/staging/iio/adc/max1363.h index c112fbe..72cf367 100644 --- a/drivers/staging/iio/adc/max1363.h +++ b/drivers/staging/iio/adc/max1363.h @@ -72,77 +72,54 @@ * @numvals: The number of values returned by a single scan */ struct max1363_mode { - const char *name; int8_t conf; - int numvals; + long modemask; }; -#define MAX1363_MODE_SINGLE(_num) { \ - .name = #_num, \ - .conf = MAX1363_CHANNEL_SEL(_num) \ +#define MAX1363_MODE_SINGLE(_num, _mask) { \ + .conf = MAX1363_CHANNEL_SEL(_num) \ | MAX1363_CONFIG_SCAN_SINGLE_1 \ | MAX1363_CONFIG_SE, \ - .numvals = 1, \ + .modemask = _mask, \ } -#define MAX1363_MODE_SINGLE_TIMES_8(_num) { \ - .name = #_num"x8", \ - .conf = MAX1363_CHANNEL_SEL(_num) \ - | MAX1363_CONFIG_SCAN_SINGLE_8 \ - | MAX1363_CONFIG_SE, \ - .numvals = 8, \ - } - -#define MAX1363_MODE_SCAN_TO_CHANNEL(_num) { \ - .name = "0..."#_num, \ - .conf = MAX1363_CHANNEL_SEL(_num) \ +#define MAX1363_MODE_SCAN_TO_CHANNEL(_num, _mask) { \ + .conf = MAX1363_CHANNEL_SEL(_num) \ | MAX1363_CONFIG_SCAN_TO_CS \ | MAX1363_CONFIG_SE, \ - .numvals = _num + 1, \ + .modemask = _mask, \ } /* note not available for max1363 hence naming */ -#define MAX1236_MODE_SCAN_MID_TO_CHANNEL(_mid, _num) { \ - .name = #_mid"..."#_num, \ - .conf = MAX1363_CHANNEL_SEL(_num) \ +#define MAX1236_MODE_SCAN_MID_TO_CHANNEL(_mid, _num, _mask) { \ + .conf = MAX1363_CHANNEL_SEL(_num) \ | MAX1236_SCAN_MID_TO_CHANNEL \ | MAX1363_CONFIG_SE, \ - .numvals = _num - _mid + 1 \ + .modemask = _mask \ } -#define MAX1363_MODE_DIFF_SINGLE(_nump, _numm) { \ - .name = #_nump"-"#_numm, \ - .conf = MAX1363_CHANNEL_SEL(_nump) \ +#define MAX1363_MODE_DIFF_SINGLE(_nump, _numm, _mask) { \ + .conf = MAX1363_CHANNEL_SEL(_nump) \ | MAX1363_CONFIG_SCAN_SINGLE_1 \ | MAX1363_CONFIG_DE, \ - .numvals = 1, \ - } - -#define MAX1363_MODE_DIFF_SINGLE_TIMES_8(_nump, _numm) { \ - .name = #_nump"-"#_numm, \ - .conf = MAX1363_CHANNEL_SEL(_nump) \ - | MAX1363_CONFIG_SCAN_SINGLE_8 \ - | MAX1363_CONFIG_DE, \ - .numvals = 1, \ + .modemask = _mask \ } /* Can't think how to automate naming so specify for now */ -#define MAX1363_MODE_DIFF_SCAN_TO_CHANNEL_NAMED(_name, _num, _numvals) { \ - .name = #_name, \ - .conf = MAX1363_CHANNEL_SEL(_num) \ +#define MAX1363_MODE_DIFF_SCAN_TO_CHANNEL(_num, _numvals, _mask) { \ + .conf = MAX1363_CHANNEL_SEL(_num) \ | MAX1363_CONFIG_SCAN_TO_CS \ | MAX1363_CONFIG_DE, \ - .numvals = _numvals, \ + .modemask = _mask \ } /* note only available for max1363 hence naming */ -#define MAX1236_MODE_DIFF_SCAN_MID_TO_CHANNEL_NAMED(_name, _num, _numvals) { \ - .name = #_name, \ - .conf = MAX1363_CHANNEL_SEL(_num) \ +#define MAX1236_MODE_DIFF_SCAN_MID_TO_CHANNEL(_num, _numvals, _mask) { \ + .conf = MAX1363_CHANNEL_SEL(_num) \ | MAX1236_SCAN_MID_TO_CHANNEL \ | MAX1363_CONFIG_SE, \ - .numvals = _numvals, \ + .modemask = _mask \ } /* Not currently handled */ @@ -158,35 +135,43 @@ struct max1363_mode { * clear what all the various options actually do. Alternative suggestions * that don't require user to have intimate knowledge of the chip welcomed. */ +enum max1363_channels { + max1363_in0, max1363_in1, max1363_in2, max1363_in3, + max1363_in4, max1363_in5, max1363_in6, max1363_in7, + max1363_in8, max1363_in9, max1363_in10, max1363_in11, + + max1363_in0min1, max1363_in2min3, + max1363_in4min5, max1363_in6min7, + max1363_in8min9, max1363_in10min11, + + max1363_in1min0, max1363_in3min2, + max1363_in5min4, max1363_in7min6, + max1363_in9min8, max1363_in11min10, + }; /* This must be maintained along side the max1363_mode_table in max1363_core */ enum max1363_modes { /* Single read of a single channel */ _s0, _s1, _s2, _s3, _s4, _s5, _s6, _s7, _s8, _s9, _s10, _s11, - /* Eight reads of a single channel */ - se0, se1, se2, se3, se4, se5, se6, se7, se8, se9, se10, se11, - /* Scan to channel */ - s0to1, s0to2, s0to3, s0to4, s0to5, s0to6, - s0to7, s0to8, s0to9, s0to10, s0to11, /* Differential single read */ d0m1, d2m3, d4m5, d6m7, d8m9, d10m11, d1m0, d3m2, d5m4, d7m6, d9m8, d11m10, - /* Differential single read 8 times */ - de0m1, de2m3, de4m5, de6m7, de8m9, de10m11, - de1m0, de3m2, de5m4, de7m6, de9m8, de11m10, - /* Differential scan to channel */ - d0m1to2m3, d0m1to4m5, d0m1to6m7, d0m1to8m9, d0m1to10m11, - d1m0to3m2, d1m0to5m4, d1m0to7m6, d1m0to9m8, d1m0to11m10, - /* Scan mid to channel max123{6-9} only */ - s2to3, s6to7, s6to8, s6to9, s6to10, s6to11, - /* Differential scan mid to channel */ - s6m7to8m9, s6m7to10m11, s7m6to9m8, s7m6to11m10, + /* Scan to channel and mid to channel where overlapping */ + s0to1, s0to2, s2to3, s0to3, s0to4, s0to5, s0to6, + s6to7, s0to7, s6to8, s0to8, s6to9, + s0to9, s6to10, s0to10, s6to11, s0to11, + /* Differential scan to channel and mid to channel where overlapping */ + d0m1to2m3, d0m1to4m5, d0m1to6m7, d6m7to8m9, + d0m1to8m9, d6m7to10m11, d0m1to10m11, d1m0to3m2, + d1m0to5m4, d1m0to7m6, d7m6to9m8, d1m0to9m8, + d7m6to11m10, d1m0to11m10, }; /** * struct max1363_chip_info - chip specifc information * @name: indentification string for chip * @num_inputs: number of physical inputs on chip + * @bits: accuracy of the adc in bits * @int_vref_mv: the internal reference voltage * @monitor_mode: whether the chip supports monitor interrupts * @mode_list: array of available scan modes @@ -196,11 +181,14 @@ enum max1363_modes { struct max1363_chip_info { const char *name; u8 num_inputs; + u8 bits; u16 int_vref_mv; bool monitor_mode; const enum max1363_modes *mode_list; int num_modes; enum max1363_modes default_mode; + struct attribute_group *dev_attrs; + struct attribute_group *scan_attrs; }; @@ -212,6 +200,7 @@ struct max1363_chip_info { * @configbyte: cache of current device config byte * @chip_info: chip model specific constants, available modes etc * @current_mode: the scan mode of this chip + * @requestedmask: a valid requested set of channels * @poll_work: bottom half of polling interrupt handler * @protect_ring: used to ensure only one polling bh running at a time * @reg: supply regulator @@ -223,16 +212,21 @@ struct max1363_state { char configbyte; const struct max1363_chip_info *chip_info; const struct max1363_mode *current_mode; + u32 requestedmask; struct work_struct poll_work; atomic_t protect_ring; struct iio_trigger *trig; struct regulator *reg; }; + +const struct max1363_mode +*max1363_match_mode(u32 mask, const struct max1363_chip_info *ci); + +int max1363_set_scan_mode(struct max1363_state *st); + #ifdef CONFIG_MAX1363_RING_BUFFER -ssize_t max1363_scan_from_ring(struct device *dev, - struct device_attribute *attr, - char *buf); +int max1363_single_channel_from_ring(long mask, struct max1363_state *st); int max1363_register_ring_funcs_and_init(struct iio_dev *indio_dev); void max1363_ring_cleanup(struct iio_dev *indio_dev); @@ -250,14 +244,12 @@ static inline int max1363_initialize_ring(struct iio_ring_buffer *ring) return 0; }; - -static inline ssize_t max1363_scan_from_ring(struct device *dev, - struct device_attribute *attr, - char *buf) +int max1363_single_channel_from_ring(long mask, struct max1363_state *st) { - return 0; + return -EINVAL; }; + static inline int max1363_register_ring_funcs_and_init(struct iio_dev *indio_dev) { diff --git a/drivers/staging/iio/adc/max1363_core.c b/drivers/staging/iio/adc/max1363_core.c index 790d1cc..e82f3e7 100644 --- a/drivers/staging/iio/adc/max1363_core.c +++ b/drivers/staging/iio/adc/max1363_core.c @@ -1,27 +1,26 @@ /* - * linux/drivers/industrialio/adc/max1363.c - * Copyright (C) 2008 Jonathan Cameron - * - * based on linux/drivers/i2c/chips/max123x - * Copyright (C) 2002-2004 Stefan Eletzhofer - * - * based on linux/drivers/acron/char/pcf8583.c - * Copyright (C) 2000 Russell King - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * max1363.c - * - * Partial support for max1363 and similar chips. - * - * Not currently implemented. - * - * - Monitor interrrupt generation. - * - Control of internal reference. - * - Sysfs scan interface currently assumes unipolar mode. - */ + * iio/adc/max1363.c + * Copyright (C) 2008-2010 Jonathan Cameron + * + * based on linux/drivers/i2c/chips/max123x + * Copyright (C) 2002-2004 Stefan Eletzhofer + * + * based on linux/drivers/acron/char/pcf8583.c + * Copyright (C) 2000 Russell King + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * max1363.c + * + * Partial support for max1363 and similar chips. + * + * Not currently implemented. + * + * - Monitor interrrupt generation. + * - Control of internal reference. + */ #include #include @@ -38,118 +37,318 @@ #include "../iio.h" #include "../sysfs.h" +#include "../ring_generic.h" +#include "adc.h" #include "max1363.h" -/* Available scan modes. - * Awkwardly the associated enum is in the header so it is available to - * the ring buffer code. - */ -static const struct max1363_mode max1363_mode_table[] = { - MAX1363_MODE_SINGLE(0), - MAX1363_MODE_SINGLE(1), - MAX1363_MODE_SINGLE(2), - MAX1363_MODE_SINGLE(3), - MAX1363_MODE_SINGLE(4), - MAX1363_MODE_SINGLE(5), - MAX1363_MODE_SINGLE(6), - MAX1363_MODE_SINGLE(7), - MAX1363_MODE_SINGLE(8), - MAX1363_MODE_SINGLE(9), - MAX1363_MODE_SINGLE(10), - MAX1363_MODE_SINGLE(11), - - MAX1363_MODE_SINGLE_TIMES_8(0), - MAX1363_MODE_SINGLE_TIMES_8(1), - MAX1363_MODE_SINGLE_TIMES_8(2), - MAX1363_MODE_SINGLE_TIMES_8(3), - MAX1363_MODE_SINGLE_TIMES_8(4), - MAX1363_MODE_SINGLE_TIMES_8(5), - MAX1363_MODE_SINGLE_TIMES_8(6), - MAX1363_MODE_SINGLE_TIMES_8(7), - MAX1363_MODE_SINGLE_TIMES_8(8), - MAX1363_MODE_SINGLE_TIMES_8(9), - MAX1363_MODE_SINGLE_TIMES_8(10), - MAX1363_MODE_SINGLE_TIMES_8(11), - - MAX1363_MODE_SCAN_TO_CHANNEL(1), - MAX1363_MODE_SCAN_TO_CHANNEL(2), - MAX1363_MODE_SCAN_TO_CHANNEL(3), - MAX1363_MODE_SCAN_TO_CHANNEL(4), - MAX1363_MODE_SCAN_TO_CHANNEL(5), - MAX1363_MODE_SCAN_TO_CHANNEL(6), - MAX1363_MODE_SCAN_TO_CHANNEL(7), - MAX1363_MODE_SCAN_TO_CHANNEL(8), - MAX1363_MODE_SCAN_TO_CHANNEL(9), - MAX1363_MODE_SCAN_TO_CHANNEL(10), - MAX1363_MODE_SCAN_TO_CHANNEL(11), - - MAX1363_MODE_DIFF_SINGLE(0, 1), - MAX1363_MODE_DIFF_SINGLE(2, 3), - MAX1363_MODE_DIFF_SINGLE(4, 5), - MAX1363_MODE_DIFF_SINGLE(6, 7), - MAX1363_MODE_DIFF_SINGLE(8, 9), - MAX1363_MODE_DIFF_SINGLE(10, 11), - MAX1363_MODE_DIFF_SINGLE(1, 0), - MAX1363_MODE_DIFF_SINGLE(3, 2), - MAX1363_MODE_DIFF_SINGLE(5, 4), - MAX1363_MODE_DIFF_SINGLE(7, 6), - MAX1363_MODE_DIFF_SINGLE(9, 8), - MAX1363_MODE_DIFF_SINGLE(11, 10), - - MAX1363_MODE_DIFF_SINGLE_TIMES_8(0, 1), - MAX1363_MODE_DIFF_SINGLE_TIMES_8(2, 3), - MAX1363_MODE_DIFF_SINGLE_TIMES_8(4, 5), - MAX1363_MODE_DIFF_SINGLE_TIMES_8(6, 7), - MAX1363_MODE_DIFF_SINGLE_TIMES_8(8, 9), - MAX1363_MODE_DIFF_SINGLE_TIMES_8(10, 11), - MAX1363_MODE_DIFF_SINGLE_TIMES_8(1, 0), - MAX1363_MODE_DIFF_SINGLE_TIMES_8(3, 2), - MAX1363_MODE_DIFF_SINGLE_TIMES_8(5, 4), - MAX1363_MODE_DIFF_SINGLE_TIMES_8(7, 6), - MAX1363_MODE_DIFF_SINGLE_TIMES_8(9, 8), - MAX1363_MODE_DIFF_SINGLE_TIMES_8(11, 10), - - MAX1363_MODE_DIFF_SCAN_TO_CHANNEL_NAMED(0-1...2-3, 2, 2), - MAX1363_MODE_DIFF_SCAN_TO_CHANNEL_NAMED(0-1...4-5, 4, 3), - MAX1363_MODE_DIFF_SCAN_TO_CHANNEL_NAMED(0-1...6-7, 6, 4), - MAX1363_MODE_DIFF_SCAN_TO_CHANNEL_NAMED(0-1...8-9, 8, 5), - MAX1363_MODE_DIFF_SCAN_TO_CHANNEL_NAMED(0-1...10-11, 10, 6), - MAX1363_MODE_DIFF_SCAN_TO_CHANNEL_NAMED(1-0...3-2, 3, 2), - MAX1363_MODE_DIFF_SCAN_TO_CHANNEL_NAMED(1-0...5-4, 5, 3), - MAX1363_MODE_DIFF_SCAN_TO_CHANNEL_NAMED(1-0...7-6, 7, 4), - MAX1363_MODE_DIFF_SCAN_TO_CHANNEL_NAMED(1-0...9-8, 9, 5), - MAX1363_MODE_DIFF_SCAN_TO_CHANNEL_NAMED(1-0...11-10, 11, 6), - - MAX1236_MODE_SCAN_MID_TO_CHANNEL(2, 3), - MAX1236_MODE_SCAN_MID_TO_CHANNEL(6, 7), - MAX1236_MODE_SCAN_MID_TO_CHANNEL(6, 8), - MAX1236_MODE_SCAN_MID_TO_CHANNEL(6, 9), - MAX1236_MODE_SCAN_MID_TO_CHANNEL(6, 10), - MAX1236_MODE_SCAN_MID_TO_CHANNEL(6, 11), - - MAX1236_MODE_DIFF_SCAN_MID_TO_CHANNEL_NAMED(6-7...8-9, 8, 2), - MAX1236_MODE_DIFF_SCAN_MID_TO_CHANNEL_NAMED(6-7...10-11, 10, 3), - MAX1236_MODE_DIFF_SCAN_MID_TO_CHANNEL_NAMED(7-6...9-8, 9, 2), - MAX1236_MODE_DIFF_SCAN_MID_TO_CHANNEL_NAMED(7-6...11-10, 11, 3), +/* Here we claim all are 16 bits. This currently does no harm and saves + * us a lot of scan element listings */ + +#define MAX1363_SCAN_EL(number) \ + IIO_SCAN_EL_C(in##number, number, IIO_UNSIGNED(16), 0, NULL); +#define MAX1363_SCAN_EL_D(p, n, number) \ + IIO_SCAN_NAMED_EL_C(in##p##m##in##n, in##p-in##n, \ + number, IIO_SIGNED(16), 0 , NULL); + +static MAX1363_SCAN_EL(0); +static MAX1363_SCAN_EL(1); +static MAX1363_SCAN_EL(2); +static MAX1363_SCAN_EL(3); +static MAX1363_SCAN_EL(4); +static MAX1363_SCAN_EL(5); +static MAX1363_SCAN_EL(6); +static MAX1363_SCAN_EL(7); +static MAX1363_SCAN_EL(8); +static MAX1363_SCAN_EL(9); +static MAX1363_SCAN_EL(10); +static MAX1363_SCAN_EL(11); +static MAX1363_SCAN_EL_D(0, 1, 12); +static MAX1363_SCAN_EL_D(2, 3, 13); +static MAX1363_SCAN_EL_D(4, 5, 14); +static MAX1363_SCAN_EL_D(6, 7, 15); +static MAX1363_SCAN_EL_D(8, 9, 16); +static MAX1363_SCAN_EL_D(10, 11, 17); +static MAX1363_SCAN_EL_D(1, 0, 18); +static MAX1363_SCAN_EL_D(3, 2, 19); +static MAX1363_SCAN_EL_D(5, 4, 20); +static MAX1363_SCAN_EL_D(7, 6, 21); +static MAX1363_SCAN_EL_D(9, 8, 22); +static MAX1363_SCAN_EL_D(11, 10, 23); + +static const struct max1363_mode max1363_mode_table[] = { + /* All of the single channel options first */ + MAX1363_MODE_SINGLE(0, 1 << 0), + MAX1363_MODE_SINGLE(1, 1 << 1), + MAX1363_MODE_SINGLE(2, 1 << 2), + MAX1363_MODE_SINGLE(3, 1 << 3), + MAX1363_MODE_SINGLE(4, 1 << 4), + MAX1363_MODE_SINGLE(5, 1 << 5), + MAX1363_MODE_SINGLE(6, 1 << 6), + MAX1363_MODE_SINGLE(7, 1 << 7), + MAX1363_MODE_SINGLE(8, 1 << 8), + MAX1363_MODE_SINGLE(9, 1 << 9), + MAX1363_MODE_SINGLE(10, 1 << 10), + MAX1363_MODE_SINGLE(11, 1 << 11), + + MAX1363_MODE_DIFF_SINGLE(0, 1, 1 << 12), + MAX1363_MODE_DIFF_SINGLE(2, 3, 1 << 13), + MAX1363_MODE_DIFF_SINGLE(4, 5, 1 << 14), + MAX1363_MODE_DIFF_SINGLE(6, 7, 1 << 15), + MAX1363_MODE_DIFF_SINGLE(8, 9, 1 << 16), + MAX1363_MODE_DIFF_SINGLE(10, 11, 1 << 17), + MAX1363_MODE_DIFF_SINGLE(1, 0, 1 << 18), + MAX1363_MODE_DIFF_SINGLE(3, 2, 1 << 19), + MAX1363_MODE_DIFF_SINGLE(5, 4, 1 << 20), + MAX1363_MODE_DIFF_SINGLE(7, 6, 1 << 21), + MAX1363_MODE_DIFF_SINGLE(9, 8, 1 << 22), + MAX1363_MODE_DIFF_SINGLE(11, 10, 1 << 23), + + /* The multichannel scans next */ + MAX1363_MODE_SCAN_TO_CHANNEL(1, 0x003), + MAX1363_MODE_SCAN_TO_CHANNEL(2, 0x007), + MAX1236_MODE_SCAN_MID_TO_CHANNEL(2, 3, 0x00C), + MAX1363_MODE_SCAN_TO_CHANNEL(3, 0x00F), + MAX1363_MODE_SCAN_TO_CHANNEL(4, 0x01F), + MAX1363_MODE_SCAN_TO_CHANNEL(5, 0x03F), + MAX1363_MODE_SCAN_TO_CHANNEL(6, 0x07F), + MAX1236_MODE_SCAN_MID_TO_CHANNEL(6, 7, 0x0C0), + MAX1363_MODE_SCAN_TO_CHANNEL(7, 0x0FF), + MAX1236_MODE_SCAN_MID_TO_CHANNEL(6, 8, 0x1C0), + MAX1363_MODE_SCAN_TO_CHANNEL(8, 0x1FF), + MAX1236_MODE_SCAN_MID_TO_CHANNEL(6, 9, 0x3C0), + MAX1363_MODE_SCAN_TO_CHANNEL(9, 0x3FF), + MAX1236_MODE_SCAN_MID_TO_CHANNEL(6, 10, 0x7C0), + MAX1363_MODE_SCAN_TO_CHANNEL(10, 0x7FF), + MAX1236_MODE_SCAN_MID_TO_CHANNEL(6, 11, 0xFC0), + MAX1363_MODE_SCAN_TO_CHANNEL(11, 0xFFF), + + MAX1363_MODE_DIFF_SCAN_TO_CHANNEL(2, 2, 0x003000), + MAX1363_MODE_DIFF_SCAN_TO_CHANNEL(4, 3, 0x007000), + MAX1363_MODE_DIFF_SCAN_TO_CHANNEL(6, 4, 0x00F000), + MAX1236_MODE_DIFF_SCAN_MID_TO_CHANNEL(8, 2, 0x018000), + MAX1363_MODE_DIFF_SCAN_TO_CHANNEL(8, 5, 0x01F000), + MAX1236_MODE_DIFF_SCAN_MID_TO_CHANNEL(10, 3, 0x038000), + MAX1363_MODE_DIFF_SCAN_TO_CHANNEL(10, 6, 0x3F000), + MAX1363_MODE_DIFF_SCAN_TO_CHANNEL(3, 2, 0x0C0000), + MAX1363_MODE_DIFF_SCAN_TO_CHANNEL(5, 3, 0x1C0000), + MAX1363_MODE_DIFF_SCAN_TO_CHANNEL(7, 4, 0x3C0000), + MAX1236_MODE_DIFF_SCAN_MID_TO_CHANNEL(9, 2, 0x600000), + MAX1363_MODE_DIFF_SCAN_TO_CHANNEL(9, 5, 0x7C0000), + MAX1236_MODE_DIFF_SCAN_MID_TO_CHANNEL(11, 3, 0xE00000), + MAX1363_MODE_DIFF_SCAN_TO_CHANNEL(11, 6, 0xFC0000), }; +const struct max1363_mode +*max1363_match_mode(u32 mask, const struct max1363_chip_info *ci) { + int i; + if (mask) + for (i = 0; i < ci->num_modes; i++) + if (!((~max1363_mode_table[ci->mode_list[i]] + .modemask) & + mask)) + return &max1363_mode_table[ci + ->mode_list[i]]; + return 0; +}; + +static ssize_t max1363_show_precision(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct iio_dev *dev_info = dev_get_drvdata(dev); + struct max1363_state *st = iio_dev_get_devdata(dev_info); + return sprintf(buf, "%d\n", st->chip_info->bits); +} + +static IIO_DEVICE_ATTR(in_precision, S_IRUGO, max1363_show_precision, + NULL, 0); + +static int max1363_write_basic_config(struct i2c_client *client, + unsigned char d1, + unsigned char d2) +{ + int ret; + u8 *tx_buf = kmalloc(2 , GFP_KERNEL); + + if (!tx_buf) + return -ENOMEM; + tx_buf[0] = d1; + tx_buf[1] = d2; + + ret = i2c_master_send(client, tx_buf, 2); + kfree(tx_buf); + + return (ret > 0) ? 0 : ret; +} + +int max1363_set_scan_mode(struct max1363_state *st) +{ + st->configbyte &= ~(MAX1363_CHANNEL_SEL_MASK + | MAX1363_SCAN_MASK + | MAX1363_SE_DE_MASK); + st->configbyte |= st->current_mode->conf; + + return max1363_write_basic_config(st->client, + st->setupbyte, + st->configbyte); +} + +static ssize_t max1363_read_single_channel(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct iio_dev *dev_info = dev_get_drvdata(dev); + struct max1363_state *st = iio_dev_get_devdata(dev_info); + struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); + struct i2c_client *client = st->client; + int ret = 0, len = 0; + s32 data ; + char rxbuf[2]; + long mask; + + mutex_lock(&dev_info->mlock); + /* If ring buffer capture is occuring, query the buffer */ + if (iio_ring_enabled(dev_info)) { + mask = max1363_mode_table[this_attr->address].modemask; + data = max1363_single_channel_from_ring(mask, st); + if (data < 0) { + ret = data; + goto error_ret; + } + } else { + /* Check to see if current scan mode is correct */ + if (st->current_mode != + &max1363_mode_table[this_attr->address]) { + /* Update scan mode if needed */ + st->current_mode + = &max1363_mode_table[this_attr->address]; + ret = max1363_set_scan_mode(st); + if (ret) + goto error_ret; + } + /* 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; + } + /* 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); +} + +IIO_DEVICE_ATTR(in_scale, S_IRUGO, max1363_show_scale, NULL, 0); + +static ssize_t max1363_show_name(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct iio_dev *dev_info = dev_get_drvdata(dev); + struct max1363_state *st = iio_dev_get_devdata(dev_info); + return sprintf(buf, "%s\n", st->chip_info->name); +} + +IIO_DEVICE_ATTR(name, S_IRUGO, max1363_show_name, NULL, 0); + /* Applies to max1363 */ static const enum max1363_modes max1363_mode_list[] = { _s0, _s1, _s2, _s3, - se0, se1, se2, se3, s0to1, s0to2, s0to3, d0m1, d2m3, d1m0, d3m2, - de0m1, de2m3, de1m0, de3m2, d0m1to2m3, d1m0to3m2, }; +static struct attribute *max1363_device_attrs[] = { + &iio_dev_attr_in0_raw.dev_attr.attr, + &iio_dev_attr_in1_raw.dev_attr.attr, + &iio_dev_attr_in2_raw.dev_attr.attr, + &iio_dev_attr_in3_raw.dev_attr.attr, + &iio_dev_attr_in0min1_raw.dev_attr.attr, + &iio_dev_attr_in2min3_raw.dev_attr.attr, + &iio_dev_attr_in1min0_raw.dev_attr.attr, + &iio_dev_attr_in3min2_raw.dev_attr.attr, + &iio_dev_attr_name.dev_attr.attr, + &iio_dev_attr_in_scale.dev_attr.attr, + NULL +}; + +static struct attribute_group max1363_dev_attr_group = { + .attrs = max1363_device_attrs, +}; + +static struct attribute *max1363_scan_el_attrs[] = { + &iio_scan_el_in0.dev_attr.attr, + &iio_scan_el_in1.dev_attr.attr, + &iio_scan_el_in2.dev_attr.attr, + &iio_scan_el_in3.dev_attr.attr, + &iio_scan_el_in0min1.dev_attr.attr, + &iio_scan_el_in2min3.dev_attr.attr, + &iio_scan_el_in1min0.dev_attr.attr, + &iio_scan_el_in3min2.dev_attr.attr, + &iio_dev_attr_in_precision.dev_attr.attr, + NULL, +}; + +static struct attribute_group max1363_scan_el_group = { + .name = "scan_elements", + .attrs = max1363_scan_el_attrs, +}; + /* Appies to max1236, max1237 */ static const enum max1363_modes max1236_mode_list[] = { _s0, _s1, _s2, _s3, - se0, se1, se2, se3, s0to1, s0to2, s0to3, d0m1, d2m3, d1m0, d3m2, - de0m1, de2m3, de1m0, de3m2, d0m1to2m3, d1m0to3m2, s2to3, }; @@ -157,19 +356,83 @@ static const enum max1363_modes max1236_mode_list[] = { /* Applies to max1238, max1239 */ static const enum max1363_modes max1238_mode_list[] = { _s0, _s1, _s2, _s3, _s4, _s5, _s6, _s7, _s8, _s9, _s10, _s11, - se0, se1, se2, se3, se4, se5, se6, se7, se8, se9, se10, se11, s0to1, s0to2, s0to3, s0to4, s0to5, s0to6, s0to7, s0to8, s0to9, s0to10, s0to11, d0m1, d2m3, d4m5, d6m7, d8m9, d10m11, d1m0, d3m2, d5m4, d7m6, d9m8, d11m10, - de0m1, de2m3, de4m5, de6m7, de8m9, de10m11, - de1m0, de3m2, de5m4, de7m6, de9m8, de11m10, d0m1to2m3, d0m1to4m5, d0m1to6m7, d0m1to8m9, d0m1to10m11, d1m0to3m2, d1m0to5m4, d1m0to7m6, d1m0to9m8, d1m0to11m10, s6to7, s6to8, s6to9, s6to10, s6to11, - s6m7to8m9, s6m7to10m11, s7m6to9m8, s7m6to11m10, + d6m7to8m9, d6m7to10m11, d7m6to9m8, d7m6to11m10, }; +static struct attribute *max1238_device_attrs[] = { + &iio_dev_attr_in0_raw.dev_attr.attr, + &iio_dev_attr_in1_raw.dev_attr.attr, + &iio_dev_attr_in2_raw.dev_attr.attr, + &iio_dev_attr_in3_raw.dev_attr.attr, + &iio_dev_attr_in4_raw.dev_attr.attr, + &iio_dev_attr_in5_raw.dev_attr.attr, + &iio_dev_attr_in6_raw.dev_attr.attr, + &iio_dev_attr_in7_raw.dev_attr.attr, + &iio_dev_attr_in8_raw.dev_attr.attr, + &iio_dev_attr_in9_raw.dev_attr.attr, + &iio_dev_attr_in10_raw.dev_attr.attr, + &iio_dev_attr_in11_raw.dev_attr.attr, + &iio_dev_attr_in0min1_raw.dev_attr.attr, + &iio_dev_attr_in2min3_raw.dev_attr.attr, + &iio_dev_attr_in4min5_raw.dev_attr.attr, + &iio_dev_attr_in6min7_raw.dev_attr.attr, + &iio_dev_attr_in8min9_raw.dev_attr.attr, + &iio_dev_attr_in10min11_raw.dev_attr.attr, + &iio_dev_attr_in1min0_raw.dev_attr.attr, + &iio_dev_attr_in3min2_raw.dev_attr.attr, + &iio_dev_attr_in5min4_raw.dev_attr.attr, + &iio_dev_attr_in7min6_raw.dev_attr.attr, + &iio_dev_attr_in9min8_raw.dev_attr.attr, + &iio_dev_attr_in11min10_raw.dev_attr.attr, + &iio_dev_attr_name.dev_attr.attr, + &iio_dev_attr_in_scale.dev_attr.attr, + NULL +}; + +static struct attribute_group max1238_dev_attr_group = { + .attrs = max1238_device_attrs, +}; + +static struct attribute *max1238_scan_el_attrs[] = { + &iio_scan_el_in0.dev_attr.attr, + &iio_scan_el_in1.dev_attr.attr, + &iio_scan_el_in2.dev_attr.attr, + &iio_scan_el_in3.dev_attr.attr, + &iio_scan_el_in4.dev_attr.attr, + &iio_scan_el_in5.dev_attr.attr, + &iio_scan_el_in6.dev_attr.attr, + &iio_scan_el_in7.dev_attr.attr, + &iio_scan_el_in8.dev_attr.attr, + &iio_scan_el_in9.dev_attr.attr, + &iio_scan_el_in10.dev_attr.attr, + &iio_scan_el_in11.dev_attr.attr, + &iio_scan_el_in0min1.dev_attr.attr, + &iio_scan_el_in2min3.dev_attr.attr, + &iio_scan_el_in4min5.dev_attr.attr, + &iio_scan_el_in6min7.dev_attr.attr, + &iio_scan_el_in8min9.dev_attr.attr, + &iio_scan_el_in10min11.dev_attr.attr, + &iio_scan_el_in1min0.dev_attr.attr, + &iio_scan_el_in3min2.dev_attr.attr, + &iio_scan_el_in5min4.dev_attr.attr, + &iio_scan_el_in7min6.dev_attr.attr, + &iio_scan_el_in9min8.dev_attr.attr, + &iio_scan_el_in11min10.dev_attr.attr, + &iio_dev_attr_in_precision.dev_attr.attr, + NULL, +}; + +static struct attribute_group max1238_scan_el_group = { + .name = "scan_elements", + .attrs = max1238_scan_el_attrs, +}; enum { max1361, max1362, @@ -190,118 +453,130 @@ static const struct max1363_chip_info max1363_chip_info_tbl[] = { { .name = "max1361", .num_inputs = 4, + .bits = 10, + .int_vref_mv = 2048, .monitor_mode = 1, .mode_list = max1363_mode_list, .num_modes = ARRAY_SIZE(max1363_mode_list), .default_mode = s0to3, + .dev_attrs = &max1363_dev_attr_group, + .scan_attrs = &max1363_scan_el_group, }, { .name = "max1362", .num_inputs = 4, + .bits = 10, + .int_vref_mv = 4096, .monitor_mode = 1, .mode_list = max1363_mode_list, .num_modes = ARRAY_SIZE(max1363_mode_list), .default_mode = s0to3, + .dev_attrs = &max1363_dev_attr_group, + .scan_attrs = &max1363_scan_el_group, }, { .name = "max1363", .num_inputs = 4, + .bits = 12, + .int_vref_mv = 2048, .monitor_mode = 1, .mode_list = max1363_mode_list, .num_modes = ARRAY_SIZE(max1363_mode_list), .default_mode = s0to3, + .dev_attrs = &max1363_dev_attr_group, + .scan_attrs = &max1363_scan_el_group, }, { .name = "max1364", .num_inputs = 4, + .bits = 12, + .int_vref_mv = 4096, .monitor_mode = 1, .mode_list = max1363_mode_list, .num_modes = ARRAY_SIZE(max1363_mode_list), .default_mode = s0to3, + .dev_attrs = &max1363_dev_attr_group, + .scan_attrs = &max1363_scan_el_group, }, { .name = "max1136", .num_inputs = 4, + .bits = 10, .int_vref_mv = 4096, .mode_list = max1236_mode_list, .num_modes = ARRAY_SIZE(max1236_mode_list), .default_mode = s0to3, + .dev_attrs = &max1363_dev_attr_group, + .scan_attrs = &max1363_scan_el_group, }, { .name = "max1137", .num_inputs = 4, + .bits = 10, .int_vref_mv = 2048, .mode_list = max1236_mode_list, .num_modes = ARRAY_SIZE(max1236_mode_list), .default_mode = s0to3, + .dev_attrs = &max1363_dev_attr_group, + .scan_attrs = &max1363_scan_el_group, }, { .name = "max1138", .num_inputs = 12, + .bits = 10, .int_vref_mv = 4096, .mode_list = max1238_mode_list, .num_modes = ARRAY_SIZE(max1238_mode_list), .default_mode = s0to11, + .dev_attrs = &max1238_dev_attr_group, + .scan_attrs = &max1238_scan_el_group, }, { .name = "max1139", .num_inputs = 12, + .bits = 10, .int_vref_mv = 2048, .mode_list = max1238_mode_list, .num_modes = ARRAY_SIZE(max1238_mode_list), .default_mode = s0to11, + .dev_attrs = &max1238_dev_attr_group, + .scan_attrs = &max1238_scan_el_group, }, { .name = "max1236", .num_inputs = 4, + .bits = 12, .int_vref_mv = 4096, .mode_list = max1236_mode_list, .num_modes = ARRAY_SIZE(max1236_mode_list), .default_mode = s0to3, + .dev_attrs = &max1363_dev_attr_group, + .scan_attrs = &max1363_scan_el_group, }, { .name = "max1237", .num_inputs = 4, + .bits = 12, .int_vref_mv = 2048, .mode_list = max1236_mode_list, .num_modes = ARRAY_SIZE(max1236_mode_list), .default_mode = s0to3, + .dev_attrs = &max1363_dev_attr_group, + .scan_attrs = &max1363_scan_el_group, }, { .name = "max1238", .num_inputs = 12, + .bits = 12, .int_vref_mv = 4096, .mode_list = max1238_mode_list, .num_modes = ARRAY_SIZE(max1238_mode_list), .default_mode = s0to11, + .dev_attrs = &max1238_dev_attr_group, + .scan_attrs = &max1238_scan_el_group, }, { .name = "max1239", .num_inputs = 12, + .bits = 12, .int_vref_mv = 2048, .mode_list = max1238_mode_list, .num_modes = ARRAY_SIZE(max1238_mode_list), .default_mode = s0to11, + .dev_attrs = &max1238_dev_attr_group, + .scan_attrs = &max1238_scan_el_group, }, }; -static int max1363_write_basic_config(struct i2c_client *client, - unsigned char d1, - unsigned char d2) -{ - int ret; - u8 *tx_buf = kmalloc(2 , GFP_KERNEL); - if (!tx_buf) - return -ENOMEM; - tx_buf[0] = d1; - tx_buf[1] = d2; - - ret = i2c_master_send(client, tx_buf, 2); - kfree(tx_buf); - return (ret > 0) ? 0 : ret; -} - -static int max1363_set_scan_mode(struct max1363_state *st) -{ - st->configbyte &= ~(MAX1363_CHANNEL_SEL_MASK - | MAX1363_SCAN_MASK - | MAX1363_SE_DE_MASK); - st->configbyte |= st->current_mode->conf; - - return max1363_write_basic_config(st->client, - st->setupbyte, - st->configbyte); -} - static int max1363_initial_setup(struct max1363_state *st) { st->setupbyte = MAX1363_SETUP_AIN3_IS_AIN3_REF_IS_VDD @@ -318,167 +593,6 @@ static int max1363_initial_setup(struct max1363_state *st) return max1363_set_scan_mode(st); } -static ssize_t max1363_show_av_scan_modes(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - struct iio_dev *dev_info = dev_get_drvdata(dev); - struct max1363_state *st = dev_info->dev_data; - int i, len = 0; - - for (i = 0; i < st->chip_info->num_modes; i++) - len += sprintf(buf + len, "%s ", - max1363_mode_table[st->chip_info - ->mode_list[i]].name); - len += sprintf(buf + len, "\n"); - - return len; -} - - -/* The dev here is the sysfs related one, not the underlying i2c one */ -static ssize_t max1363_scan_direct(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - struct iio_dev *dev_info = dev_get_drvdata(dev); - struct max1363_state *st = dev_info->dev_data; - int len = 0, ret, i; - struct i2c_client *client = st->client; - char *rxbuf; - - if (st->current_mode->numvals == 0) - return 0; - rxbuf = kmalloc(st->current_mode->numvals*2, GFP_KERNEL); - if (rxbuf == NULL) - return -ENOMEM; - - /* Interpretation depends on whether these are signed or not!*/ - /* Assume not for now */ - ret = i2c_master_recv(client, rxbuf, st->current_mode->numvals*2); - - if (ret < 0) - return ret; - for (i = 0; i < st->current_mode->numvals; i++) - len += sprintf(buf+len, "%d ", - ((int)(rxbuf[i*2+0]&0x0F) << 8) - + ((int)(rxbuf[i*2+1]))); - kfree(rxbuf); - len += sprintf(buf + len, "\n"); - - return len; -} - -static ssize_t max1363_scan(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - struct iio_dev *dev_info = dev_get_drvdata(dev); - int ret; - - mutex_lock(&dev_info->mlock); - if (dev_info->currentmode == INDIO_RING_TRIGGERED) - ret = max1363_scan_from_ring(dev, attr, buf); - else - ret = max1363_scan_direct(dev, attr, buf); - mutex_unlock(&dev_info->mlock); - - return ret; -} - -/* Cannot query the device, so use local copy of state */ -static ssize_t max1363_show_scan_mode(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - struct iio_dev *dev_info = dev_get_drvdata(dev); - struct max1363_state *st = dev_info->dev_data; - - return sprintf(buf, "%s\n", st->current_mode->name); -} - -static const struct max1363_mode -*__max1363_find_mode_in_ci(const struct max1363_chip_info *info, - const char *buf) -{ - int i; - for (i = 0; i < info->num_modes; i++) - if (strcmp(max1363_mode_table[info->mode_list[i]].name, buf) - == 0) - return &max1363_mode_table[info->mode_list[i]]; - return NULL; -} - -static ssize_t max1363_store_scan_mode(struct device *dev, - struct device_attribute *attr, - const char *buf, - size_t len) -{ - struct iio_dev *dev_info = dev_get_drvdata(dev); - struct max1363_state *st = dev_info->dev_data; - const struct max1363_mode *new_mode; - int ret; - - mutex_lock(&dev_info->mlock); - new_mode = NULL; - /* Avoid state changes if a ring buffer is enabled */ - if (!iio_ring_enabled(dev_info)) { - new_mode - = __max1363_find_mode_in_ci(st->chip_info, buf); - if (!new_mode) { - ret = -EINVAL; - goto error_ret; - } - st->current_mode = new_mode; - ret = max1363_set_scan_mode(st); - if (ret) - goto error_ret; - } else { - ret = -EBUSY; - goto error_ret; - } - mutex_unlock(&dev_info->mlock); - - return len; - -error_ret: - mutex_unlock(&dev_info->mlock); - - return ret; -} - -IIO_DEV_ATTR_AVAIL_SCAN_MODES(max1363_show_av_scan_modes); -IIO_DEV_ATTR_SCAN_MODE(S_IRUGO | S_IWUSR, - max1363_show_scan_mode, - max1363_store_scan_mode); - -IIO_DEV_ATTR_SCAN(max1363_scan); - -static ssize_t max1363_show_name(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - struct iio_dev *dev_info = dev_get_drvdata(dev); - struct max1363_state *st = dev_info->dev_data; - return sprintf(buf, "%s\n", st->chip_info->name); -} - -IIO_DEVICE_ATTR(name, S_IRUGO, max1363_show_name, NULL, 0); - -/*name export */ - -static struct attribute *max1363_attributes[] = { - &iio_dev_attr_available_scan_modes.dev_attr.attr, - &iio_dev_attr_scan_mode.dev_attr.attr, - &iio_dev_attr_scan.dev_attr.attr, - &iio_dev_attr_name.dev_attr.attr, - NULL, -}; - -static const struct attribute_group max1363_attribute_group = { - .attrs = max1363_attributes, -}; - static int __devinit max1363_probe(struct i2c_client *client, const struct i2c_device_id *id) { @@ -506,6 +620,7 @@ static int __devinit max1363_probe(struct i2c_client *client, ret = -ENODEV; goto error_free_st; } + st->reg = regulator_get(&client->dev, "vcc"); if (!IS_ERR(st->reg)) { ret = regulator_enable(st->reg); @@ -520,20 +635,36 @@ static int __devinit max1363_probe(struct i2c_client *client, goto error_disable_reg; } + st->indio_dev->available_scan_masks + = kzalloc(GFP_KERNEL, + sizeof(*st->indio_dev->available_scan_masks)* + (st->chip_info->num_modes + 1)); + if (!st->indio_dev->available_scan_masks) { + ret = -ENOMEM; + goto error_free_device; + } + + for (i = 0; i < st->chip_info->num_modes; i++) + st->indio_dev->available_scan_masks[i] = + max1363_mode_table[st->chip_info->mode_list[i]] + .modemask; /* Estabilish that the iio_dev is a child of the i2c device */ st->indio_dev->dev.parent = &client->dev; - st->indio_dev->attrs = &max1363_attribute_group; + st->indio_dev->attrs = st->chip_info->dev_attrs; + + /* Todo: this shouldn't be here. */ + st->indio_dev->scan_el_attrs = st->chip_info->scan_attrs; st->indio_dev->dev_data = (void *)(st); st->indio_dev->driver_module = THIS_MODULE; st->indio_dev->modes = INDIO_DIRECT_MODE; ret = max1363_initial_setup(st); if (ret) - goto error_free_device; + goto error_free_available_scan_masks; ret = max1363_register_ring_funcs_and_init(st->indio_dev); if (ret) - goto error_free_device; + goto error_free_available_scan_masks; ret = iio_device_register(st->indio_dev); if (ret) @@ -545,6 +676,8 @@ static int __devinit max1363_probe(struct i2c_client *client, return 0; error_cleanup_ring: max1363_ring_cleanup(st->indio_dev); +error_free_available_scan_masks: + kfree(st->indio_dev->available_scan_masks); error_free_device: if (!regdone) iio_free_device(st->indio_dev); @@ -569,6 +702,7 @@ static int max1363_remove(struct i2c_client *client) struct iio_dev *indio_dev = st->indio_dev; max1363_uninitialize_ring(indio_dev->ring); max1363_ring_cleanup(indio_dev); + kfree(st->indio_dev->available_scan_masks); iio_device_unregister(indio_dev); if (!IS_ERR(st->reg)) { regulator_disable(st->reg); diff --git a/drivers/staging/iio/adc/max1363_ring.c b/drivers/staging/iio/adc/max1363_ring.c index f94fe2d..6003f7e 100644 --- a/drivers/staging/iio/adc/max1363_ring.c +++ b/drivers/staging/iio/adc/max1363_ring.c @@ -17,6 +17,7 @@ #include #include #include +#include #include "../iio.h" #include "../ring_generic.h" @@ -26,32 +27,36 @@ #include "max1363.h" -ssize_t max1363_scan_from_ring(struct device *dev, - struct device_attribute *attr, - char *buf) +/* Todo: test this */ +int max1363_single_channel_from_ring(long mask, struct max1363_state *st) { - struct iio_dev *dev_info = dev_get_drvdata(dev); - struct max1363_state *info = dev_info->dev_data; - int i, ret, len = 0; - char *ring_data; + unsigned long numvals; + int count = 0, ret; + u8 *ring_data; + if (!(st->current_mode->modemask & mask)) { + ret = -EBUSY; + goto error_ret; + } + numvals = hweight_long(st->current_mode->modemask); - ring_data = kmalloc(info->current_mode->numvals*2, GFP_KERNEL); + ring_data = kmalloc(numvals*2, GFP_KERNEL); if (ring_data == NULL) { ret = -ENOMEM; goto error_ret; } - ret = dev_info->ring->access.read_last(dev_info->ring, ring_data); + ret = st->indio_dev->ring->access.read_last(st->indio_dev->ring, + ring_data); if (ret) goto error_free_ring_data; - len += sprintf(buf+len, "ring "); - for (i = 0; i < info->current_mode->numvals; i++) - len += sprintf(buf + len, "%d ", - ((int)(ring_data[i*2 + 0] & 0x0F) << 8) - + ((int)(ring_data[i*2 + 1]))); - len += sprintf(buf + len, "\n"); - kfree(ring_data); - - return len; + /* Need a count of channels prior to this one */ + mask >>= 1; + while (mask) { + if (mask && st->current_mode->modemask) + count++; + mask >>= 1; + } + return ((int)(ring_data[count*2 + 0] & 0x0F) << 8) + + (int)(ring_data[count*2 + 1]); error_free_ring_data: kfree(ring_data); @@ -70,9 +75,22 @@ static int max1363_ring_preenable(struct iio_dev *indio_dev) { struct max1363_state *st = indio_dev->dev_data; size_t d_size; + unsigned long numvals; + + /* + * Need to figure out the current mode based upon the requested + * scan mask in iio_dev + */ + st->current_mode = max1363_match_mode(st->indio_dev->scan_mask, + st->chip_info); + if (!st->current_mode) + return -EINVAL; + + max1363_set_scan_mode(st); + numvals = hweight_long(st->current_mode->modemask); if (indio_dev->ring->access.set_bpd) { - d_size = st->current_mode->numvals*2 + sizeof(s64); + d_size = numvals*2 + sizeof(s64); if (d_size % 8) d_size += 8 - (d_size % 8); indio_dev->ring->access.set_bpd(indio_dev->ring, d_size); @@ -145,9 +163,10 @@ static void max1363_poll_bh_to_ring(struct work_struct *work_s) __u8 *rxbuf; int b_sent; size_t d_size; + unsigned long numvals = hweight_long(st->current_mode->modemask); /* Ensure the timestamp is 8 byte aligned */ - d_size = st->current_mode->numvals*2 + sizeof(s64); + d_size = numvals*2 + sizeof(s64); if (d_size % sizeof(s64)) d_size += sizeof(s64) - (d_size % sizeof(s64)); @@ -159,16 +178,14 @@ static void max1363_poll_bh_to_ring(struct work_struct *work_s) * might as well have this test in here in the meantime as it does * no harm. */ - if (st->current_mode->numvals == 0) + if (numvals == 0) return; rxbuf = kmalloc(d_size, GFP_KERNEL); if (rxbuf == NULL) return; - b_sent = i2c_master_recv(st->client, - rxbuf, - st->current_mode->numvals*2); + b_sent = i2c_master_recv(st->client, rxbuf, numvals*2); if (b_sent < 0) goto done; -- cgit v1.1 From eaf86ff9390d4d9c34d88d75fc7bcb784afc697a Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Tue, 4 May 2010 14:43:04 +0100 Subject: staging:iio: Documentation, update iio_utils.h for the move to a bus Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/staging/iio/Documentation/iio_utils.h | 31 ++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) (limited to 'drivers/staging/iio') diff --git a/drivers/staging/iio/Documentation/iio_utils.h b/drivers/staging/iio/Documentation/iio_utils.h index 74d3124..d24006a 100644 --- a/drivers/staging/iio/Documentation/iio_utils.h +++ b/drivers/staging/iio/Documentation/iio_utils.h @@ -47,7 +47,7 @@ inline char *find_ring_subelement(const char *directory, const char *subelement) char *find_type_by_name(const char *name, const char *type) { - const char *iio_dir = "/sys/class/iio/"; + const char *iio_dir = "/sys/bus/iio/devices/"; const struct dirent *ent; int cnt, pos, pos2; @@ -112,6 +112,35 @@ int write_sysfs_int(char *filename, char *basedir, int val) return 0; } +int write_sysfs_int_and_verify(char *filename, char *basedir, int val) +{ + int ret; + FILE *sysfsfp; + char temp[100]; + int test; + + sprintf(temp, "%s%s", basedir, filename); + sysfsfp = fopen(temp, "w"); + if (sysfsfp == NULL) + return -1; + fprintf(sysfsfp, "%d", val); + fclose(sysfsfp); + + sysfsfp = fopen(temp, "r"); + if (sysfsfp == NULL) + return -1; + fscanf(sysfsfp, "%d", &test); + if (test != val) { + printf("Possible failure in int write %d to %s%s\n", + val, + basedir, + filename); + return -1; + } + + return 0; +} + /** * write_sysfs_string_and_verify() - string write, readback and verify * @filename: name of file to write to -- cgit v1.1 From e34d2c5fa2254197b0a01925cc6f77e12552f9b9 Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Tue, 4 May 2010 14:43:05 +0100 Subject: staging:iio: ABI documentation (partial) Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/staging/iio/Documentation/sysfs-class-iio | 285 ++++++++++++++++++++++ 1 file changed, 285 insertions(+) create mode 100644 drivers/staging/iio/Documentation/sysfs-class-iio (limited to 'drivers/staging/iio') diff --git a/drivers/staging/iio/Documentation/sysfs-class-iio b/drivers/staging/iio/Documentation/sysfs-class-iio new file mode 100644 index 0000000..7238582 --- /dev/null +++ b/drivers/staging/iio/Documentation/sysfs-class-iio @@ -0,0 +1,285 @@ + +What: /sys/bus/iio/devices/device[n] +KernelVersion: 2.6.35 +Contact: linux-iio@vger.kernel.org +Description: + Hardware chip or device accessed by on communication port. + Corresponds to a grouping of sensor channels. + +What: /sys/bus/iio/devices/trigger[n] +KernelVersion: 2.6.35 +Contact: linux-iio@vger.kernel.org +Description: + An event driven driver of data capture to an in kernel buffer. + May be provided by a device driver that also has an IIO device + based on hardware generated events (e.g. data ready) or + provided by a separate driver for other hardware (e.g. + periodic timer, gpio or high resolution timer). + Contains trigger type specific elements. These do not + generalize well and hence are not documented in this file. + +What: /sys/bus/iio/devices/device[n]:buffer +KernelVersion: 2.6.35 +Contact: linux-iio@vger.kernel.org +Description: + Link to /sys/class/iio/device[n]/device[n]:buffer. n indicates the + device with which this buffer buffer is associated. + +What: /sys/.../device[n]/name +KernelVersion: 2.6.35 +Contact: linux-iio@vger.kernel.org +Description: + Description of the physical chip / device. Typically a part + number. + +What: /sys/.../device[n]/sampling_frequency +KernelVersion: 2.6.35 +Contact: linux-iio@vger.kernel.org +Description: + Some devices have internal clocks. This parameter sets the + resulting sampling frequency. In many devices this + parameter has an effect on input filters etc rather than + simply controlling when the input is sampled. As this + effects datardy triggers, hardware buffers and the sysfs + direct access interfaces, it may be found in any of the + relevant directories. If it effects all of the above + then it is to be found in the base device directory as here. + +What: /sys/.../device[n]/sampling_frequency_available +KernelVersion: 2.6.35 +Contact: linux-iio@vger.kernel.org +Description: + When the internal sampling clock can only take a small + discrete set of values, this file lists those availale. + +What: /sys/.../device[n]/in[_name][m]_raw +KernelVersion: 2.6.35 +Contact: linux-iio@vger.kernel.org +Description: + Raw (unscaled no bias removal etc) voltage measurement from + channel m. name is used in special cases where this does + not correspond to externally available input (e.g. supply + voltage monitoring in which case the file is in_supply_raw). + +What: /sys/.../device[n]/in[_name][m]_offset +KernelVersion: 2.6.35 +Contact: linux-iio@vger.kernel.org +Description: + If known for a device, offset to be added to in[m]_raw prior + to scaling by volt[m]_scale in order to obtain voltage in + millivolts. Not present if the offset is always 0 or unknown. + If m is not present, then voltage offset applies to all in + channels. May be writable if a variable offset is controlled + by the device. Note that this is different to calibbias which + is for devices that apply offsets to compensate for variation + between different instances of the part, typically adjusted by + using some hardware supported calibration procedure. + +What: /sys/.../device[n]/in[_name][m]_offset_available +KernelVersion: 2.6.35 +Contact: linux-iio@vger.kernel.org +Description: + If a small number of discrete offset values are available, this + will be a space separated list. If these are independant (but + options the same) for individual offsets then m should not be + present. + +What: /sys/.../device[n]/in[_name][m]_offset_[min|max] +KernelVersion: 2.6.35 +Contact: linux-iio@vger.kernel.org +Description: + If a more or less continuous range of voltage offsets are supported + then these specify the minimum and maximum. If shared by all + in channels then m is not present. + +What: /sys/.../device[n]/in[_name][m]_calibbias +KernelVersion: 2.6.35 +Contact: linux-iio@vger.kernel.org +Description: + Hardware applied calibration offset. (assumed to fix production + inaccuracies) + +What /sys/.../device[n]/in[_name][m]_calibscale +KernelVersion: 2.6.35 +Contact: linux-iio@vger.kernel.org +Description: + Hardware applied calibration scale factor. (assumed to fix production + inaccuracies) + +What: /sys/.../device[n]/in[_name][m]_scale +KernelVersion: 2.6.35 +Contact: linux-iio@vger.kernel.org +Description: + If known for a device, scale to be applied to volt[m]_raw post + addition of volt[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]/mag[_x|_y|_z][m]_raw +KernelVersion: 2.6.35 +Contact: linux-iio@vger.kernel.org +Description: + Magnetic field along axis x, y or z (may be arbitrarily assigned) + channel m (not present if only one magnetometer at this orientation). + Data converted by application of offset then scale to Gauss + Has all the equivalent modifiers as per in[m]. + +What: /sys/.../device[n]/device[n]:event[m] +KernelVersion: 2.6.35 +Contact: linux-iio@vger.kernel.org +Description: + Configuration of which hardware generated events are passed up to + userspace. Some of these are a bit complex to generalize so this + section is a work in progress. + +What: /sys/.../device[n]:event[m]/dev +KernelVersion: 2.6.35 +Contact: linux-iio@vger.kernel.org +Description: + major:minor character device numbers for the event line. + +Taking accel_x0 as an example + +What: /sys/.../device[n]:event[m]/accel_x0_thresh[_high|_low]_en +KernelVersion: 2.6.35 +Contact: linux-iio@vger.kernel.org +Description: + Event generated when accel_x0 passes a threshold in correction direction + (or stays beyond one). If direction isn't specified, either triggers it. + Note driver will assume last p events requested are enabled where p is + however many it supports. So if you want to be sure you have + set what you think you have, check the contents of these. Drivers + may have to buffer any parameters so that they are consistent when a + given event type is enabled a future point (and not those for whatever + alarm was previously enabled). + +What: /sys/.../device[n]:event[m]/accel_x0_roc[_high|_low]_en +KernelVersion: 2.6.35 +Contact: linux-iio@vger.kernel.org +Description: + Same as above but based on the first differential of the value. + + +What: /sys/.../device[n]:event[m]/accel_x0[_thresh|_roc][_high|_low]_period +KernelVersion: 2.6.35 +Contact: linux-iio@vger.kernel.org +Description: + A period of time (microsecs) for which the condition must be broken + before an interrupt is triggered. Applies to all alarms if type is not + specified. + +What: /sys/.../device[n]:event[m]/accel_x0[_thresh|_roc][_high|_low]_value +KernelVersion: 2.6.35 +Contact: linux-iio@vger.kernel.org +Description: + The actual value of the threshold in raw device units obtained by + reverse application of scale and offfset to the acceleration in m/s^2. + +What: /sys/.../device[n]/scan_elements +KernelVersion: 2.6.35 +Contact: linux-iio@vger.kernel.org +Description: + Directory containing interfaces for elements that will be captured + for a single triggered sample set in the buffer. + +What: /sys/.../device[n]/scan_elements/[m]_accel_x0_en +KernelVersion: 2.6.35 +Contact: linux-iio@vger.kernel.org +Description: + Scan element control for triggered data capture. m implies the + ordering within the buffer. Next the type is specified with + modifier and channel number as per the sysfs single channel + access above. + +What: /sys/.../device[n]/scan_elements/accel[_x0]_precision +KernelVersion: 2.6.35 +Contact: linux-iio@vger.kernel.org +Description: + Scan element precision within the buffer. Note that the + data alignment must restrictions must be read from within + buffer to work out full data alignment for data read + via buffer_access chrdev. _x0 dropped if shared across all + acceleration channels. + +What: /sys/.../device[n]/scan_elements/accel[_x0]_shift +KernelVersion: 2.6.35 +Contact: linux-iio@vger.kernel.org +Description: + A bit shift (to right) that must be applied prior to + extracting the bits specified by accel[_x0]_precision. + +What: /sys/.../device[n]/device[n]:buffer:event/dev +KernelVersion: 2.6.35 +Contact: linux-iio@vger.kernel.org +Description: + Buffer for device n event character device major:minor numbers. + +What: /sys/.../device[n]/device[n]:buffer:access/dev +KernelVersion: 2.6.35 +Contact: linux-iio@vger.kernel.org +Description: + Buffer for device n access character device o major:minor numbers. + +What: /sys/.../device[n]:buffer/trigger +KernelVersion: 2.6.35 +Contact: linux-iio@vger.kernel.org +Description: + The name of the trigger source being used, as per string given + in /sys/class/iio/trigger[n]/name. + +What: /sys/.../device[n]:buffer/length +KernelVersion: 2.6.35 +Contact: linux-iio@vger.kernel.org +Description: + Number of scans contained by the buffer. + +What: /sys/.../device[n]:buffer/bps +KernelVersion: 2.6.35 +Contact: linux-iio@vger.kernel.org +Description: + Bytes per scan. Due to alignment fun, the scan may be larger + than implied directly by the scan_element parameters. + +What: /sys/.../device[n]:buffer/enable +KernelVersion: 2.6.35 +Contact: linux-iio@vger.kernel.org +Description: + Actually start the buffer capture up. Will start trigger + if first device and appropriate. + +What: /sys/.../device[n]:buffer/alignment +KernelVersion: 2.6.35 +Contact: linux-iio@vger.kernel.org +Description: + Minimum data alignment. Scan elements larger than this are aligned + to the nearest power of 2 times this. (may not be true in weird + hardware buffers that pack data well) + -- cgit v1.1 From ba5c6fbac6682240ac209a209b88a30be4a464fb Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Tue, 4 May 2010 14:43:06 +0100 Subject: staging:iio: Directory name changes to match new ABI. Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/staging/iio/chrdev.h | 2 +- drivers/staging/iio/industrialio-core.c | 5 +++-- drivers/staging/iio/industrialio-ring.c | 13 +++++++++---- 3 files changed, 13 insertions(+), 7 deletions(-) (limited to 'drivers/staging/iio') diff --git a/drivers/staging/iio/chrdev.h b/drivers/staging/iio/chrdev.h index f42bafb..3f96f86 100644 --- a/drivers/staging/iio/chrdev.h +++ b/drivers/staging/iio/chrdev.h @@ -94,7 +94,7 @@ struct iio_event_interface { struct iio_chrdev_minor_attr attr; struct module *owner; void *private; - char _name[20]; + char _name[35]; char _attrname[20]; }; diff --git a/drivers/staging/iio/industrialio-core.c b/drivers/staging/iio/industrialio-core.c index a4ce221..c55d0f3 100644 --- a/drivers/staging/iio/industrialio-core.c +++ b/drivers/staging/iio/industrialio-core.c @@ -660,8 +660,9 @@ static int iio_device_register_eventset(struct iio_dev *dev_info) dev_info->event_interfaces[i].id = ret; snprintf(dev_info->event_interfaces[i]._name, 20, - "event_line%d", - dev_info->event_interfaces[i].id); + "%s:event%d", + dev_name(&dev_info->dev), + dev_info->event_interfaces[i].id); ret = iio_setup_ev_int(&dev_info->event_interfaces[i], (const char *)(dev_info diff --git a/drivers/staging/iio/industrialio-ring.c b/drivers/staging/iio/industrialio-ring.c index 690df91..0f19bd1 100644 --- a/drivers/staging/iio/industrialio-ring.c +++ b/drivers/staging/iio/industrialio-ring.c @@ -164,8 +164,9 @@ __iio_request_ring_buffer_event_chrdev(struct iio_ring_buffer *buf, else buf->ev_int.id = ret; - snprintf(buf->ev_int._name, 20, - "ring_event_line%d", + snprintf(buf->ev_int._name, sizeof(buf->ev_int._name), + "%s:event%d", + dev_name(&buf->dev), buf->ev_int.id); ret = iio_setup_ev_int(&(buf->ev_int), buf->ev_int._name, @@ -226,7 +227,9 @@ __iio_request_ring_buffer_access_chrdev(struct iio_ring_buffer *buf, goto error_device_put; else buf->access_id = ret; - dev_set_name(&buf->access_dev, "ring_access%d", buf->access_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"); @@ -280,7 +283,9 @@ int iio_ring_buffer_register(struct iio_ring_buffer *ring) else ring->id = ret; - dev_set_name(&ring->dev, "ring_buffer%d", ring->id); + dev_set_name(&ring->dev, "%s:buffer%d", + dev_name(ring->dev.parent), + ring->id); ret = device_add(&ring->dev); if (ret) goto error_free_id; -- cgit v1.1 From 2a6a255494605f5906222687fba5b45c03594d71 Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Tue, 4 May 2010 14:43:07 +0100 Subject: staging:iio:tsl2563: change lux to illuminance0_input to match new abi Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/staging/iio/light/tsl2563.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/staging/iio') diff --git a/drivers/staging/iio/light/tsl2563.c b/drivers/staging/iio/light/tsl2563.c index 1ba4aa3..911c898 100644 --- a/drivers/staging/iio/light/tsl2563.c +++ b/drivers/staging/iio/light/tsl2563.c @@ -592,7 +592,7 @@ static ssize_t tsl2563_calib1_store(struct device *dev, * once I understand what they mean */ static DEVICE_ATTR(adc0, S_IRUGO, tsl2563_adc0_show, NULL); static DEVICE_ATTR(adc1, S_IRUGO, tsl2563_adc1_show, NULL); -static DEVICE_ATTR(lux, S_IRUGO, tsl2563_lux_show, NULL); +static DEVICE_ATTR(illuminance0_input, S_IRUGO, tsl2563_lux_show, NULL); static DEVICE_ATTR(calib0, S_IRUGO | S_IWUSR, tsl2563_calib0_show, tsl2563_calib0_store); static DEVICE_ATTR(calib1, S_IRUGO | S_IWUSR, @@ -601,7 +601,7 @@ static DEVICE_ATTR(calib1, S_IRUGO | S_IWUSR, static struct attribute *tsl2563_attributes[] = { &dev_attr_adc0.attr, &dev_attr_adc1.attr, - &dev_attr_lux.attr, + &dev_attr_illuminance0_input.attr, &dev_attr_calib0.attr, &dev_attr_calib1.attr, NULL -- cgit v1.1 From 758d988ce089ac7345bc03a9116b5ebf10dbfa9e Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Tue, 4 May 2010 14:43:08 +0100 Subject: staging:iio: Remove naming via IDR's where no longer necessary under new abi. Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/staging/iio/accel/lis3l02dq_ring.c | 2 +- drivers/staging/iio/accel/sca3000_core.c | 2 +- drivers/staging/iio/adc/max1363_ring.c | 2 +- drivers/staging/iio/industrialio-ring.c | 48 ++++++++---------------------- drivers/staging/iio/ring_generic.h | 2 +- 5 files changed, 16 insertions(+), 40 deletions(-) (limited to 'drivers/staging/iio') diff --git a/drivers/staging/iio/accel/lis3l02dq_ring.c b/drivers/staging/iio/accel/lis3l02dq_ring.c index bba4b09..7617da8 100644 --- a/drivers/staging/iio/accel/lis3l02dq_ring.c +++ b/drivers/staging/iio/accel/lis3l02dq_ring.c @@ -581,7 +581,7 @@ error_iio_sw_rb_free: int lis3l02dq_initialize_ring(struct iio_ring_buffer *ring) { - return iio_ring_buffer_register(ring); + return iio_ring_buffer_register(ring, 0); } void lis3l02dq_uninitialize_ring(struct iio_ring_buffer *ring) diff --git a/drivers/staging/iio/accel/sca3000_core.c b/drivers/staging/iio/accel/sca3000_core.c index 45e4777..9485b13 100644 --- a/drivers/staging/iio/accel/sca3000_core.c +++ b/drivers/staging/iio/accel/sca3000_core.c @@ -1338,7 +1338,7 @@ static int __devinit __sca3000_probe(struct spi_device *spi, if (ret < 0) goto error_free_dev; regdone = 1; - ret = iio_ring_buffer_register(st->indio_dev->ring); + ret = iio_ring_buffer_register(st->indio_dev->ring, 0); if (ret < 0) goto error_unregister_dev; if (spi->irq && gpio_is_valid(irq_to_gpio(spi->irq)) > 0) { diff --git a/drivers/staging/iio/adc/max1363_ring.c b/drivers/staging/iio/adc/max1363_ring.c index 6003f7e..f1e37f2 100644 --- a/drivers/staging/iio/adc/max1363_ring.c +++ b/drivers/staging/iio/adc/max1363_ring.c @@ -255,5 +255,5 @@ void max1363_uninitialize_ring(struct iio_ring_buffer *ring) int max1363_initialize_ring(struct iio_ring_buffer *ring) { - return iio_ring_buffer_register(ring); + return iio_ring_buffer_register(ring, 0); }; diff --git a/drivers/staging/iio/industrialio-ring.c b/drivers/staging/iio/industrialio-ring.c index 0f19bd1..fc27d22 100644 --- a/drivers/staging/iio/industrialio-ring.c +++ b/drivers/staging/iio/industrialio-ring.c @@ -20,19 +20,11 @@ #include #include #include -#include #include #include "iio.h" #include "ring_generic.h" -/* IDR for ring buffer identifier */ -static DEFINE_IDR(iio_ring_idr); -/* IDR for ring event identifier */ -static DEFINE_IDR(iio_ring_event_idr); -/* IDR for ring access identifier */ -static DEFINE_IDR(iio_ring_access_idr); - int iio_push_ring_event(struct iio_ring_buffer *ring_buf, int event_code, s64 timestamp) @@ -158,11 +150,8 @@ __iio_request_ring_buffer_event_chrdev(struct iio_ring_buffer *buf, struct device *dev) { int ret; - ret = iio_get_new_idr_val(&iio_ring_event_idr); - if (ret < 0) - goto error_ret; - else - buf->ev_int.id = ret; + + buf->ev_int.id = id; snprintf(buf->ev_int._name, sizeof(buf->ev_int._name), "%s:event%d", @@ -173,11 +162,9 @@ __iio_request_ring_buffer_event_chrdev(struct iio_ring_buffer *buf, owner, dev); if (ret) - goto error_free_id; + goto error_ret; return 0; -error_free_id: - iio_free_idr_val(&iio_ring_event_idr, buf->ev_int.id); error_ret: return ret; } @@ -186,7 +173,6 @@ static inline void __iio_free_ring_buffer_event_chrdev(struct iio_ring_buffer *buf) { iio_free_ev_int(&(buf->ev_int)); - iio_free_idr_val(&iio_ring_event_idr, buf->ev_int.id); } static void iio_ring_access_release(struct device *dev) @@ -222,18 +208,16 @@ __iio_request_ring_buffer_access_chrdev(struct iio_ring_buffer *buf, } buf->access_dev.devt = MKDEV(MAJOR(iio_devt), minor); - ret = iio_get_new_idr_val(&iio_ring_access_idr); - if (ret < 0) - goto error_device_put; - else - buf->access_id = ret; + + buf->access_id = id; + dev_set_name(&buf->access_dev, "%s:access%d", dev_name(&buf->dev), buf->access_id); ret = device_add(&buf->access_dev); if (ret < 0) { printk(KERN_ERR "failed to add the ring access dev\n"); - goto error_free_idr; + goto error_device_put; } cdev_init(&buf->access_handler.chrdev, &iio_ring_fileops); @@ -245,10 +229,9 @@ __iio_request_ring_buffer_access_chrdev(struct iio_ring_buffer *buf, goto error_device_unregister; } return 0; + error_device_unregister: device_unregister(&buf->access_dev); -error_free_idr: - iio_free_idr_val(&iio_ring_access_idr, buf->access_id); error_device_put: put_device(&buf->access_dev); @@ -257,7 +240,6 @@ error_device_put: static void __iio_free_ring_buffer_access_chrdev(struct iio_ring_buffer *buf) { - iio_free_idr_val(&iio_ring_access_idr, buf->access_id); device_unregister(&buf->access_dev); } @@ -274,21 +256,18 @@ void iio_ring_buffer_init(struct iio_ring_buffer *ring, } EXPORT_SYMBOL(iio_ring_buffer_init); -int iio_ring_buffer_register(struct iio_ring_buffer *ring) +int iio_ring_buffer_register(struct iio_ring_buffer *ring, int id) { int ret; - ret = iio_get_new_idr_val(&iio_ring_idr); - if (ret < 0) - goto error_ret; - else - ring->id = ret; + + ring->id = id; dev_set_name(&ring->dev, "%s:buffer%d", dev_name(ring->dev.parent), ring->id); ret = device_add(&ring->dev); if (ret) - goto error_free_id; + goto error_ret; ret = __iio_request_ring_buffer_event_chrdev(ring, 0, @@ -309,8 +288,6 @@ error_free_ring_buffer_event_chrdev: __iio_free_ring_buffer_event_chrdev(ring); error_remove_device: device_del(&ring->dev); -error_free_id: - iio_free_idr_val(&iio_ring_idr, ring->id); error_ret: return ret; } @@ -321,7 +298,6 @@ void iio_ring_buffer_unregister(struct iio_ring_buffer *ring) __iio_free_ring_buffer_access_chrdev(ring); __iio_free_ring_buffer_event_chrdev(ring); device_del(&ring->dev); - iio_free_idr_val(&iio_ring_idr, ring->id); } EXPORT_SYMBOL(iio_ring_buffer_unregister); diff --git a/drivers/staging/iio/ring_generic.h b/drivers/staging/iio/ring_generic.h index d74b5ae..0e44375 100644 --- a/drivers/staging/iio/ring_generic.h +++ b/drivers/staging/iio/ring_generic.h @@ -259,7 +259,7 @@ static inline void iio_put_ring_buffer(struct iio_ring_buffer *ring) container_of(d, struct iio_ring_buffer, dev) #define access_dev_to_iio_ring_buffer(d) \ container_of(d, struct iio_ring_buffer, access_dev) -int iio_ring_buffer_register(struct iio_ring_buffer *ring); +int iio_ring_buffer_register(struct iio_ring_buffer *ring, int id); void iio_ring_buffer_unregister(struct iio_ring_buffer *ring); ssize_t iio_read_ring_length(struct device *dev, -- cgit v1.1 From c3fa0fddd6fed10eda57de8912a634890ee27a31 Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Tue, 4 May 2010 14:43:09 +0100 Subject: staging:iio:max1363 add support for max11606-max11617 Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/staging/iio/adc/Kconfig | 4 +- drivers/staging/iio/adc/max1363_core.c | 216 ++++++++++++++++++++++++++++++++- 2 files changed, 218 insertions(+), 2 deletions(-) (limited to 'drivers/staging/iio') diff --git a/drivers/staging/iio/adc/Kconfig b/drivers/staging/iio/adc/Kconfig index 101ea4b..afc8318 100644 --- a/drivers/staging/iio/adc/Kconfig +++ b/drivers/staging/iio/adc/Kconfig @@ -12,7 +12,9 @@ config MAX1363 Say yes here to build support for many MAXIM i2c analog to digital convertors (ADC). (max1361, max1362, max1363, max1364, max1136, max1136, max1137, max1138, max1139, max1236, max1237, max11238, - max1239) Provides direct access via sysfs. + max1239, max11606, max11607, max11608, max11609, max11610, + max11611, max11612, max11613, max11614, max11615, max11616, + max11617) Provides direct access via sysfs. config MAX1363_RING_BUFFER bool "MAXIM max1363: use ring buffer" diff --git a/drivers/staging/iio/adc/max1363_core.c b/drivers/staging/iio/adc/max1363_core.c index e82f3e7..c53bf6d 100644 --- a/drivers/staging/iio/adc/max1363_core.c +++ b/drivers/staging/iio/adc/max1363_core.c @@ -434,6 +434,76 @@ static struct attribute_group max1238_scan_el_group = { .attrs = max1238_scan_el_attrs, }; + +static const enum max1363_modes max11607_mode_list[] = { + _s0, _s1, _s2, _s3, + s0to1, s0to2, s0to3, + s2to3, + d0m1, d2m3, d1m0, d3m2, + d0m1to2m3, d1m0to3m2, +}; + +static const enum max1363_modes max11608_mode_list[] = { + _s0, _s1, _s2, _s3, _s4, _s5, _s6, _s7, + s0to1, s0to2, s0to3, s0to4, s0to5, s0to6, s0to7, + s6to7, + d0m1, d2m3, d4m5, d6m7, + d1m0, d3m2, d5m4, d7m6, + d0m1to2m3, d0m1to4m5, d0m1to6m7, + d1m0to3m2, d1m0to5m4, d1m0to7m6, +}; + +static struct attribute *max11608_device_attrs[] = { + &iio_dev_attr_in0_raw.dev_attr.attr, + &iio_dev_attr_in1_raw.dev_attr.attr, + &iio_dev_attr_in2_raw.dev_attr.attr, + &iio_dev_attr_in3_raw.dev_attr.attr, + &iio_dev_attr_in4_raw.dev_attr.attr, + &iio_dev_attr_in5_raw.dev_attr.attr, + &iio_dev_attr_in6_raw.dev_attr.attr, + &iio_dev_attr_in7_raw.dev_attr.attr, + &iio_dev_attr_in0min1_raw.dev_attr.attr, + &iio_dev_attr_in2min3_raw.dev_attr.attr, + &iio_dev_attr_in4min5_raw.dev_attr.attr, + &iio_dev_attr_in6min7_raw.dev_attr.attr, + &iio_dev_attr_in1min0_raw.dev_attr.attr, + &iio_dev_attr_in3min2_raw.dev_attr.attr, + &iio_dev_attr_in5min4_raw.dev_attr.attr, + &iio_dev_attr_in7min6_raw.dev_attr.attr, + &iio_dev_attr_name.dev_attr.attr, + &iio_dev_attr_in_scale.dev_attr.attr, + NULL +}; + +static struct attribute_group max11608_dev_attr_group = { + .attrs = max11608_device_attrs, +}; + +static struct attribute *max11608_scan_el_attrs[] = { + &iio_scan_el_in0.dev_attr.attr, + &iio_scan_el_in1.dev_attr.attr, + &iio_scan_el_in2.dev_attr.attr, + &iio_scan_el_in3.dev_attr.attr, + &iio_scan_el_in4.dev_attr.attr, + &iio_scan_el_in5.dev_attr.attr, + &iio_scan_el_in6.dev_attr.attr, + &iio_scan_el_in7.dev_attr.attr, + &iio_scan_el_in0min1.dev_attr.attr, + &iio_scan_el_in2min3.dev_attr.attr, + &iio_scan_el_in4min5.dev_attr.attr, + &iio_scan_el_in6min7.dev_attr.attr, + &iio_scan_el_in1min0.dev_attr.attr, + &iio_scan_el_in3min2.dev_attr.attr, + &iio_scan_el_in5min4.dev_attr.attr, + &iio_scan_el_in7min6.dev_attr.attr, + &iio_dev_attr_in_precision.dev_attr.attr, +}; + +static struct attribute_group max11608_scan_el_group = { + .name = "scan_elements", + .attrs = max11608_scan_el_attrs, +}; + enum { max1361, max1362, max1363, @@ -446,6 +516,18 @@ enum { max1361, max1237, max1238, max1239, + max11606, + max11607, + max11608, + max11609, + max11610, + max11611, + max11612, + max11613, + max11614, + max11615, + max11616, + max11617, }; /* max1363 and max1368 tested - rest from data sheet */ @@ -574,7 +656,127 @@ static const struct max1363_chip_info max1363_chip_info_tbl[] = { .default_mode = s0to11, .dev_attrs = &max1238_dev_attr_group, .scan_attrs = &max1238_scan_el_group, - }, + }, { + .name = "max11606", + .num_inputs = 4, + .bits = 10, + .int_vref_mv = 4096, + .mode_list = max11607_mode_list, + .num_modes = ARRAY_SIZE(max11607_mode_list), + .default_mode = s0to3, + .dev_attrs = &max1363_dev_attr_group, + .scan_attrs = &max1363_scan_el_group, + }, { + .name = "max11607", + .num_inputs = 4, + .bits = 10, + .int_vref_mv = 2048, + .mode_list = max11607_mode_list, + .num_modes = ARRAY_SIZE(max11607_mode_list), + .default_mode = s0to3, + .dev_attrs = &max1363_dev_attr_group, + .scan_attrs = &max1363_scan_el_group, + }, { + .name = "max11608", + .num_inputs = 8, + .bits = 10, + .int_vref_mv = 4096, + .mode_list = max11608_mode_list, + .num_modes = ARRAY_SIZE(max11608_mode_list), + .default_mode = s0to7, + .dev_attrs = &max11608_dev_attr_group, + .scan_attrs = &max11608_scan_el_group, + }, { + .name = "max11609", + .num_inputs = 8, + .bits = 10, + .int_vref_mv = 2048, + .mode_list = max11608_mode_list, + .num_modes = ARRAY_SIZE(max11608_mode_list), + .default_mode = s0to7, + .dev_attrs = &max11608_dev_attr_group, + .scan_attrs = &max11608_scan_el_group, + }, { + .name = "max11610", + .num_inputs = 12, + .bits = 10, + .int_vref_mv = 4098, + .mode_list = max1238_mode_list, + .num_modes = ARRAY_SIZE(max1238_mode_list), + .default_mode = s0to11, + .dev_attrs = &max1238_dev_attr_group, + .scan_attrs = &max1238_scan_el_group, + }, { + .name = "max11611", + .num_inputs = 12, + .bits = 10, + .int_vref_mv = 2048, + .mode_list = max1238_mode_list, + .num_modes = ARRAY_SIZE(max1238_mode_list), + .default_mode = s0to11, + .dev_attrs = &max1238_dev_attr_group, + .scan_attrs = &max1238_scan_el_group, + }, { + .name = "max11612", + .num_inputs = 4, + .bits = 12, + .int_vref_mv = 4096, + .mode_list = max11607_mode_list, + .num_modes = ARRAY_SIZE(max11607_mode_list), + .default_mode = s0to3, + .dev_attrs = &max1363_dev_attr_group, + .scan_attrs = &max1363_scan_el_group, + }, { + .name = "max11613", + .num_inputs = 4, + .bits = 12, + .int_vref_mv = 2048, + .mode_list = max11607_mode_list, + .num_modes = ARRAY_SIZE(max11607_mode_list), + .default_mode = s0to3, + .dev_attrs = &max1363_dev_attr_group, + .scan_attrs = &max1363_scan_el_group, + }, { + .name = "max11614", + .num_inputs = 8, + .bits = 12, + .int_vref_mv = 4096, + .mode_list = max11608_mode_list, + .num_modes = ARRAY_SIZE(max11608_mode_list), + .default_mode = s0to7, + .dev_attrs = &max11608_dev_attr_group, + .scan_attrs = &max11608_scan_el_group, + }, { + .name = "max11615", + .num_inputs = 8, + .bits = 12, + .int_vref_mv = 2048, + .mode_list = max11608_mode_list, + .num_modes = ARRAY_SIZE(max11608_mode_list), + .default_mode = s0to7, + .dev_attrs = &max11608_dev_attr_group, + .scan_attrs = &max11608_scan_el_group, + }, { + .name = "max11616", + .num_inputs = 12, + .bits = 12, + .int_vref_mv = 4098, + .mode_list = max1238_mode_list, + .num_modes = ARRAY_SIZE(max1238_mode_list), + .default_mode = s0to11, + .dev_attrs = &max1238_dev_attr_group, + .scan_attrs = &max1238_scan_el_group, + }, { + .name = "max11617", + .num_inputs = 12, + .bits = 12, + .int_vref_mv = 2048, + .mode_list = max1238_mode_list, + .num_modes = ARRAY_SIZE(max1238_mode_list), + .default_mode = s0to11, + .dev_attrs = &max1238_dev_attr_group, + .scan_attrs = &max1238_scan_el_group, + } }; static int max1363_initial_setup(struct max1363_state *st) @@ -726,6 +928,18 @@ static const struct i2c_device_id max1363_id[] = { { "max1237", max1237 }, { "max1238", max1238 }, { "max1239", max1239 }, + { "max11606", max11606 }, + { "max11607", max11607 }, + { "max11608", max11608 }, + { "max11609", max11609 }, + { "max11610", max11610 }, + { "max11611", max11611 }, + { "max11612", max11612 }, + { "max11613", max11613 }, + { "max11614", max11614 }, + { "max11615", max11615 }, + { "max11616", max11616 }, + { "max11617", max11617 }, {} }; -- cgit v1.1 From 3bf877c1cc0d17ef471f21ae319fcb9c9211df61 Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Tue, 4 May 2010 14:43:10 +0100 Subject: staging:iio:max1363 add support for 8 bit equivalent devices, max1036-9, max11600-5 Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/staging/iio/adc/Kconfig | 12 +-- drivers/staging/iio/adc/max1363_core.c | 143 +++++++++++++++++++++++++++++++-- drivers/staging/iio/adc/max1363_ring.c | 23 ++++-- 3 files changed, 161 insertions(+), 17 deletions(-) (limited to 'drivers/staging/iio') diff --git a/drivers/staging/iio/adc/Kconfig b/drivers/staging/iio/adc/Kconfig index afc8318..0835fbc 100644 --- a/drivers/staging/iio/adc/Kconfig +++ b/drivers/staging/iio/adc/Kconfig @@ -10,11 +10,13 @@ config MAX1363 select MAX1363_RING_BUFFER help Say yes here to build support for many MAXIM i2c analog to digital - convertors (ADC). (max1361, max1362, max1363, max1364, max1136, - max1136, max1137, max1138, max1139, max1236, max1237, max11238, - max1239, max11606, max11607, max11608, max11609, max11610, - max11611, max11612, max11613, max11614, max11615, max11616, - max11617) Provides direct access via sysfs. + convertors (ADC). (max1361, max1362, max1363, max1364, max1036, + max1037, max1038, max1039, max1136, max1136, max1137, max1138, + max1139, max1236, max1237, max11238, max1239, max11600, max11601, + max11602, max11603, max11604, max11605, max11606, max11607, + max11608, max11609, max11610, max11611, max11612, max11613, + max11614, max11615, max11616, max11617) Provides direct access + via sysfs. config MAX1363_RING_BUFFER bool "MAXIM max1363: use ring buffer" diff --git a/drivers/staging/iio/adc/max1363_core.c b/drivers/staging/iio/adc/max1363_core.c index c53bf6d..fa91cc1 100644 --- a/drivers/staging/iio/adc/max1363_core.c +++ b/drivers/staging/iio/adc/max1363_core.c @@ -226,13 +226,24 @@ static ssize_t max1363_read_single_channel(struct device *dev, if (ret) goto error_ret; } - /* Get reading */ - data = i2c_master_recv(client, rxbuf, 2); - if (data < 0) { - ret = data; - 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]; } - data = (s32)(rxbuf[1]) | ((s32)(rxbuf[0] & 0x0F)) << 8; } /* Pretty print the result */ len = sprintf(buf, "%u\n", data); @@ -508,6 +519,10 @@ enum { max1361, max1362, max1363, max1364, + max1036, + max1037, + max1038, + max1039, max1136, max1137, max1138, @@ -516,6 +531,12 @@ enum { max1361, max1237, max1238, max1239, + max11600, + max11601, + max11602, + max11603, + max11604, + max11605, max11606, max11607, max11608, @@ -577,6 +598,46 @@ static const struct max1363_chip_info max1363_chip_info_tbl[] = { .dev_attrs = &max1363_dev_attr_group, .scan_attrs = &max1363_scan_el_group, }, { + .name = "max1036", + .num_inputs = 4, + .bits = 8, + .int_vref_mv = 4096, + .mode_list = max1236_mode_list, + .num_modes = ARRAY_SIZE(max1236_mode_list), + .default_mode = s0to3, + .dev_attrs = &max1363_dev_attr_group, + .scan_attrs = &max1363_scan_el_group, + }, { + .name = "max1037", + .num_inputs = 4, + .bits = 8, + .int_vref_mv = 2048, + .mode_list = max1236_mode_list, + .num_modes = ARRAY_SIZE(max1236_mode_list), + .default_mode = s0to3, + .dev_attrs = &max1363_dev_attr_group, + .scan_attrs = &max1363_scan_el_group, + }, { + .name = "max1038", + .num_inputs = 12, + .bits = 8, + .int_vref_mv = 4096, + .mode_list = max1238_mode_list, + .num_modes = ARRAY_SIZE(max1238_mode_list), + .default_mode = s0to11, + .dev_attrs = &max1238_dev_attr_group, + .scan_attrs = &max1238_scan_el_group, + }, { + .name = "max1039", + .num_inputs = 12, + .bits = 8, + .int_vref_mv = 2048, + .mode_list = max1238_mode_list, + .num_modes = ARRAY_SIZE(max1238_mode_list), + .default_mode = s0to11, + .dev_attrs = &max1238_dev_attr_group, + .scan_attrs = &max1238_scan_el_group, + }, { .name = "max1136", .num_inputs = 4, .bits = 10, @@ -657,6 +718,66 @@ static const struct max1363_chip_info max1363_chip_info_tbl[] = { .dev_attrs = &max1238_dev_attr_group, .scan_attrs = &max1238_scan_el_group, }, { + .name = "max11600", + .num_inputs = 4, + .bits = 8, + .int_vref_mv = 4096, + .mode_list = max11607_mode_list, + .num_modes = ARRAY_SIZE(max11607_mode_list), + .default_mode = s0to3, + .dev_attrs = &max1363_dev_attr_group, + .scan_attrs = &max1363_scan_el_group, + }, { + .name = "max11601", + .num_inputs = 4, + .bits = 8, + .int_vref_mv = 2048, + .mode_list = max11607_mode_list, + .num_modes = ARRAY_SIZE(max11607_mode_list), + .default_mode = s0to3, + .dev_attrs = &max1363_dev_attr_group, + .scan_attrs = &max1363_scan_el_group, + }, { + .name = "max11602", + .num_inputs = 8, + .bits = 8, + .int_vref_mv = 4096, + .mode_list = max11608_mode_list, + .num_modes = ARRAY_SIZE(max11608_mode_list), + .default_mode = s0to7, + .dev_attrs = &max11608_dev_attr_group, + .scan_attrs = &max11608_scan_el_group, + }, { + .name = "max11603", + .num_inputs = 8, + .bits = 8, + .int_vref_mv = 2048, + .mode_list = max11608_mode_list, + .num_modes = ARRAY_SIZE(max11608_mode_list), + .default_mode = s0to7, + .dev_attrs = &max11608_dev_attr_group, + .scan_attrs = &max11608_scan_el_group, + }, { + .name = "max11604", + .num_inputs = 12, + .bits = 8, + .int_vref_mv = 4098, + .mode_list = max1238_mode_list, + .num_modes = ARRAY_SIZE(max1238_mode_list), + .default_mode = s0to11, + .dev_attrs = &max1238_dev_attr_group, + .scan_attrs = &max1238_scan_el_group, + }, { + .name = "max11605", + .num_inputs = 12, + .bits = 8, + .int_vref_mv = 2048, + .mode_list = max1238_mode_list, + .num_modes = ARRAY_SIZE(max1238_mode_list), + .default_mode = s0to11, + .dev_attrs = &max1238_dev_attr_group, + .scan_attrs = &max1238_scan_el_group, + }, { .name = "max11606", .num_inputs = 4, .bits = 10, @@ -920,6 +1041,10 @@ static const struct i2c_device_id max1363_id[] = { { "max1362", max1362 }, { "max1363", max1363 }, { "max1364", max1364 }, + { "max1036", max1036 }, + { "max1037", max1037 }, + { "max1038", max1038 }, + { "max1039", max1039 }, { "max1136", max1136 }, { "max1137", max1137 }, { "max1138", max1138 }, @@ -928,6 +1053,12 @@ static const struct i2c_device_id max1363_id[] = { { "max1237", max1237 }, { "max1238", max1238 }, { "max1239", max1239 }, + { "max11600", max11600 }, + { "max11601", max11601 }, + { "max11602", max11602 }, + { "max11603", max11603 }, + { "max11604", max11604 }, + { "max11605", max11605 }, { "max11606", max11606 }, { "max11607", max11607 }, { "max11608", max11608 }, diff --git a/drivers/staging/iio/adc/max1363_ring.c b/drivers/staging/iio/adc/max1363_ring.c index f1e37f2..85fde75 100644 --- a/drivers/staging/iio/adc/max1363_ring.c +++ b/drivers/staging/iio/adc/max1363_ring.c @@ -55,8 +55,11 @@ int max1363_single_channel_from_ring(long mask, struct max1363_state *st) count++; mask >>= 1; } - return ((int)(ring_data[count*2 + 0] & 0x0F) << 8) - + (int)(ring_data[count*2 + 1]); + if (st->chip_info->bits != 8) + return ((int)(ring_data[count*2 + 0] & 0x0F) << 8) + + (int)(ring_data[count*2 + 1]); + else + return ring_data[count]; error_free_ring_data: kfree(ring_data); @@ -90,7 +93,10 @@ static int max1363_ring_preenable(struct iio_dev *indio_dev) numvals = hweight_long(st->current_mode->modemask); if (indio_dev->ring->access.set_bpd) { - d_size = numvals*2 + sizeof(s64); + if (st->chip_info->bits != 8) + d_size = numvals*2 + sizeof(s64); + else + d_size = numvals + sizeof(s64); if (d_size % 8) d_size += 8 - (d_size % 8); indio_dev->ring->access.set_bpd(indio_dev->ring, d_size); @@ -166,7 +172,10 @@ static void max1363_poll_bh_to_ring(struct work_struct *work_s) unsigned long numvals = hweight_long(st->current_mode->modemask); /* Ensure the timestamp is 8 byte aligned */ - d_size = numvals*2 + sizeof(s64); + if (st->chip_info->bits != 8) + d_size = numvals*2 + sizeof(s64); + else + d_size = numvals + sizeof(s64); if (d_size % sizeof(s64)) d_size += sizeof(s64) - (d_size % sizeof(s64)); @@ -184,8 +193,10 @@ static void max1363_poll_bh_to_ring(struct work_struct *work_s) rxbuf = kmalloc(d_size, GFP_KERNEL); if (rxbuf == NULL) return; - - b_sent = i2c_master_recv(st->client, rxbuf, numvals*2); + if (st->chip_info->bits != 8) + b_sent = i2c_master_recv(st->client, rxbuf, numvals*2); + else + b_sent = i2c_master_recv(st->client, rxbuf, numvals); if (b_sent < 0) goto done; -- cgit v1.1 From 8474ddd7cba04c2e8d8691abda32ae0f4a684137 Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Tue, 4 May 2010 14:43:11 +0100 Subject: staging:iio:ring_sw: Fix incorrect test on successful read of last value, causes infinite loop Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/staging/iio/ring_sw.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/staging/iio') diff --git a/drivers/staging/iio/ring_sw.c b/drivers/staging/iio/ring_sw.c index f8de45d..5ee3e45 100644 --- a/drivers/staging/iio/ring_sw.c +++ b/drivers/staging/iio/ring_sw.c @@ -295,7 +295,7 @@ again: return -EAGAIN; memcpy(data, last_written_p_copy, ring->buf.bpd); - if (unlikely(ring->last_written_p >= last_written_p_copy)) + if (unlikely(ring->last_written_p != last_written_p_copy)) goto again; iio_unmark_sw_rb_in_use(&ring->buf); -- cgit v1.1 From e9124afad3d419213b1cd80e40bcf65445ba11f9 Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Tue, 4 May 2010 14:43:12 +0100 Subject: staging:iio:tsl2563 add a name attribute under the iio Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/staging/iio/light/tsl2563.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'drivers/staging/iio') diff --git a/drivers/staging/iio/light/tsl2563.c b/drivers/staging/iio/light/tsl2563.c index 911c898..da3d51c 100644 --- a/drivers/staging/iio/light/tsl2563.c +++ b/drivers/staging/iio/light/tsl2563.c @@ -598,12 +598,24 @@ static DEVICE_ATTR(calib0, S_IRUGO | S_IWUSR, static DEVICE_ATTR(calib1, S_IRUGO | S_IWUSR, tsl2563_calib1_show, tsl2563_calib1_store); +static ssize_t tsl2563_show_name(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct tsl2563_chip *chip = indio_dev->dev_data; + return sprintf(buf, "%s\n", chip->client->name); +} + +DEVICE_ATTR(name, S_IRUGO, tsl2563_show_name, NULL); + static struct attribute *tsl2563_attributes[] = { &dev_attr_adc0.attr, &dev_attr_adc1.attr, &dev_attr_illuminance0_input.attr, &dev_attr_calib0.attr, &dev_attr_calib1.attr, + &dev_attr_name.attr, NULL }; -- cgit v1.1 From 9d8ae6c884ef855c4cb0e423caa993e73b29ef67 Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Tue, 4 May 2010 14:43:13 +0100 Subject: staging:iio:Documentation: Rewrite example for new abi. Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/staging/iio/Documentation/iio_utils.h | 277 ++++++++++++--------- .../iio/Documentation/lis3l02dqbuffersimple.c | 234 +++++++++++------ 2 files changed, 313 insertions(+), 198 deletions(-) (limited to 'drivers/staging/iio') diff --git a/drivers/staging/iio/Documentation/iio_utils.h b/drivers/staging/iio/Documentation/iio_utils.h index d24006a..a4555e6 100644 --- a/drivers/staging/iio/Documentation/iio_utils.h +++ b/drivers/staging/iio/Documentation/iio_utils.h @@ -7,140 +7,173 @@ * the Free Software Foundation. */ +/* Made up value to limit allocation sizes */ +#include +#include + +#define IIO_MAX_NAME_LENGTH 30 + #define IIO_EVENT_CODE_RING_50_FULL 200 #define IIO_EVENT_CODE_RING_75_FULL 201 #define IIO_EVENT_CODE_RING_100_FULL 202 +const char *iio_dir = "/sys/bus/iio/devices/"; + struct iio_event_data { int id; __s64 timestamp; }; - -inline char *find_ring_subelement(const char *directory, const char *subelement) -{ - DIR *dp; - const struct dirent *ent; - int pos; - char temp[100]; - char *returnstring; - dp = opendir(directory); - if (dp == NULL) { - printf("could not directory: %s\n", directory); - return NULL; - } - while (ent = readdir(dp), ent != NULL) { - if (strcmp(ent->d_name, ".") != 0 && - strcmp(ent->d_name, "..") != 0) { - if (strncmp(ent->d_name, subelement, strlen(subelement)) == 0) { - int length = sprintf(temp, "%s%s%s", directory, ent->d_name, "/"); - returnstring = malloc(length+1); - strncpy(returnstring, temp, length+1); - return returnstring; - - } - } - } - return 0; -} - - -char *find_type_by_name(const char *name, const char *type) +/** + * find_type_by_name() - function to match top level types by name + * @name: top level type instance name + * @type: the type of top level instance being sort + * + * Typical types this is used for are device and trigger. + **/ +inline int find_type_by_name(const char *name, const char *type) { - const char *iio_dir = "/sys/bus/iio/devices/"; const struct dirent *ent; - int cnt, pos, pos2; + int number, numstrlen; FILE *nameFile; DIR *dp; - char thisname[100]; - char temp[100]; - - char *returnstring = NULL; + char thisname[IIO_MAX_NAME_LENGTH]; + char *filename; struct stat Stat; - pos = sprintf(temp, "%s", iio_dir); + dp = opendir(iio_dir); if (dp == NULL) { printf("No industrialio devices available"); - return NULL; + return -ENODEV; } + while (ent = readdir(dp), ent != NULL) { - cnt++; - /*reject . and .. */ if (strcmp(ent->d_name, ".") != 0 && - strcmp(ent->d_name, "..") != 0) { - /*make sure it isn't a trigger!*/ - if (strncmp(ent->d_name, type, strlen(type)) == 0) { - /* build full path to new file */ - pos2 = pos + sprintf(temp + pos, "%s/", ent->d_name); - sprintf(temp + pos2, "name"); - printf("search location %s\n", temp); - nameFile = fopen(temp, "r"); - if (!nameFile) { - sprintf(temp + pos2, "modalias", ent->d_name); - nameFile = fopen(temp, "r"); - if (!nameFile) { - printf("Failed to find a name for device\n"); - return NULL; - } - } + strcmp(ent->d_name, "..") != 0 && + strlen(ent->d_name) > strlen(type) && + strncmp(ent->d_name, type, strlen(type)) == 0) { + numstrlen = sscanf(ent->d_name + strlen(type), + "%d", + &number); + /* verify the next character is not a colon */ + if (strncmp(ent->d_name + strlen(type) + numstrlen, + ":", + 1) != 0) { + filename = malloc(strlen(iio_dir) + + strlen(type) + + 1 + + numstrlen + + 1); + if (filename == NULL) + return -ENOMEM; + sprintf(filename, "%s%s%d/name", + iio_dir, + type, + number); + nameFile = fopen(filename, "r"); + if (!nameFile) + continue; + free(filename); fscanf(nameFile, "%s", thisname); - if (strcmp(name, thisname) == 0) { - returnstring = malloc(strlen(temp) + 1); - sprintf(temp + pos2, ""); - strcpy(returnstring, temp); - return returnstring; - } + if (strcmp(name, thisname) == 0) + return number; fclose(nameFile); - } } } + return -ENODEV; } -int write_sysfs_int(char *filename, char *basedir, int val) +inline int _write_sysfs_int(char *filename, char *basedir, int val, int verify) { int ret; - FILE *sysfsfp; - char temp[100]; - sprintf(temp, "%s%s", basedir, filename); + FILE *sysfsfp; + int test; + char *temp = malloc(strlen(basedir) + strlen(filename) + 2); + if (temp == NULL) + return -ENOMEM; + sprintf(temp, "%s/%s", basedir, filename); sysfsfp = fopen(temp, "w"); - if (sysfsfp == NULL) - return -1; + if (sysfsfp == NULL) { + printf("failed to open %s\n", temp); + ret = -errno; + goto error_free; + } fprintf(sysfsfp, "%d", val); fclose(sysfsfp); - return 0; + if (verify) { + sysfsfp = fopen(temp, "r"); + if (sysfsfp == NULL) { + printf("failed to open %s\n", temp); + ret = -errno; + goto error_free; + } + fscanf(sysfsfp, "%d", &test); + if (test != val) { + printf("Possible failure in int write %d to %s%s\n", + val, + basedir, + filename); + ret = -1; + } + } +error_free: + free(temp); + return ret; +} + +int write_sysfs_int(char *filename, char *basedir, int val) +{ + return _write_sysfs_int(filename, basedir, val, 0); } int write_sysfs_int_and_verify(char *filename, char *basedir, int val) { + return _write_sysfs_int(filename, basedir, val, 1); +} + +int _write_sysfs_string(char *filename, char *basedir, char *val, int verify) +{ int ret; FILE *sysfsfp; - char temp[100]; - int test; - - sprintf(temp, "%s%s", basedir, filename); + char *temp = malloc(strlen(basedir) + strlen(filename) + 2); + if (temp == NULL) { + printf("Memory allocation failed\n"); + return -ENOMEM; + } + sprintf(temp, "%s/%s", basedir, filename); sysfsfp = fopen(temp, "w"); - if (sysfsfp == NULL) - return -1; - fprintf(sysfsfp, "%d", val); + if (sysfsfp == NULL) { + printf("Could not open %s\n", temp); + ret = -errno; + goto error_free; + } + fprintf(sysfsfp, "%s", val); fclose(sysfsfp); - - sysfsfp = fopen(temp, "r"); - if (sysfsfp == NULL) - return -1; - fscanf(sysfsfp, "%d", &test); - if (test != val) { - printf("Possible failure in int write %d to %s%s\n", - val, - basedir, - filename); - return -1; + if (verify) { + sysfsfp = fopen(temp, "r"); + if (sysfsfp == NULL) { + ret = -errno; + goto error_free; + } + fscanf(sysfsfp, "%s", temp); + if (strcmp(temp, val) != 0) { + printf("Possible failure in string write of %s " + "Should be %s " + "writen to %s\%s\n", + temp, + val, + basedir, + filename); + ret = -1; + } } +error_free: + free(temp); - return 0; + return ret; } - /** * write_sysfs_string_and_verify() - string write, readback and verify * @filename: name of file to write to @@ -149,40 +182,54 @@ int write_sysfs_int_and_verify(char *filename, char *basedir, int val) **/ int write_sysfs_string_and_verify(char *filename, char *basedir, char *val) { - int ret; - FILE *sysfsfp; - char temp[100]; - sprintf(temp, "%s%s", basedir, filename); - sysfsfp = fopen(temp, "w"); - if (sysfsfp == NULL) - return -1; - fprintf(sysfsfp, "%s", val); - fclose(sysfsfp); + return _write_sysfs_string(filename, basedir, val, 1); +} - sysfsfp = fopen(temp, "r"); - if (sysfsfp == NULL) - return -1; - fscanf(sysfsfp, "%s", temp); - if (strcmp(temp, val) != 0) { - printf("Possible failure in string write %s to %s%s \n", - val, - basedir, - filename); - return -1; - } - return 0; +int write_sysfs_string(char *filename, char *basedir, char *val) +{ + return _write_sysfs_string(filename, basedir, val, 0); } int read_sysfs_posint(char *filename, char *basedir) { int ret; FILE *sysfsfp; - char temp[100]; - sprintf(temp, "%s%s", basedir, filename); + char *temp = malloc(strlen(basedir) + strlen(filename) + 2); + if (temp == NULL) { + printf("Memory allocation failed"); + return -ENOMEM; + } + sprintf(temp, "%s/%s", basedir, filename); sysfsfp = fopen(temp, "r"); - if (sysfsfp == NULL) - return -1; + if (sysfsfp == NULL) { + ret = -errno; + goto error_free; + } fscanf(sysfsfp, "%d\n", &ret); fclose(sysfsfp); +error_free: + free(temp); + return ret; +} + +int read_sysfs_float(char *filename, char *basedir, float *val) +{ + float ret = 0; + FILE *sysfsfp; + char *temp = malloc(strlen(basedir) + strlen(filename) + 2); + if (temp == NULL) { + printf("Memory allocation failed"); + return -ENOMEM; + } + sprintf(temp, "%s/%s", basedir, filename); + sysfsfp = fopen(temp, "r"); + if (sysfsfp == NULL) { + ret = -errno; + goto error_free; + } + fscanf(sysfsfp, "%f\n", val); + fclose(sysfsfp); +error_free: + free(temp); return ret; } diff --git a/drivers/staging/iio/Documentation/lis3l02dqbuffersimple.c b/drivers/staging/iio/Documentation/lis3l02dqbuffersimple.c index 08e012f..3a58028 100644 --- a/drivers/staging/iio/Documentation/lis3l02dqbuffersimple.c +++ b/drivers/staging/iio/Documentation/lis3l02dqbuffersimple.c @@ -1,4 +1,4 @@ -/* Industrialio test ring buffer with a lis3l02dq acceleromter +/* Industrialio ring buffer with a lis3l02dq accelerometer * * Copyright (c) 2008 Jonathan Cameron * @@ -6,125 +6,181 @@ * under the terms of the GNU General Public License version 2 as published by * the Free Software Foundation. * - * Assumes suitable udev rules are used to create the dev nodes as named here. + * This program is primarily intended as an example application. */ #include #include #include #include -#include -#include #include #include - #include #include "iio_utils.h" -static const char *ring_access = "/dev/iio/lis3l02dq_ring_access"; -static const char *ring_event = "/dev/iio/lis3l02dq_ring_event"; -static const char *device_name = "lis3l02dq"; -static const char *trigger_name = "lis3l02dq-dev0"; -static int NumVals = 3; -static int scan_ts = 1; -static int RingLength = 128; +const char *device_name = "lis3l02dq"; +const char *trigger_name_base = "lis3l02dq-dev"; +const int num_vals = 3; +const int scan_ts = 1; +const int buf_len = 128; +const int num_loops = 10; /* * Could get this from ring bps, but only after starting the ring - * which is a bit late for it to be useful + * which is a bit late for it to be useful. + * + * Todo: replace with much more generic version based on scan_elements + * directory. */ -int size_from_scanmode(int numVals, int timestamp) +int size_from_scanmode(int num_vals, int timestamp) { - if (numVals && timestamp) + if (num_vals && timestamp) return 16; else if (timestamp) return 8; else - return numVals*2; + return num_vals*2; } int main(int argc, char **argv) { + int ret; int i, j, k, toread; FILE *fp_ev; int fp; + + char *trigger_name, *dev_dir_name, *buf_dir_name; char *data; size_t read_size; struct iio_event_data dat; + int dev_num, trig_num; + + char *buffer_access, *buffer_event; + const char *iio_dir = "/sys/bus/iio/devices/"; + int scan_size; + float gain = 1; - char *BaseDirectoryName, - *TriggerDirectoryName, - *RingBufferDirectoryName; - BaseDirectoryName = find_type_by_name(device_name, "device"); - if (BaseDirectoryName == NULL) { - printf("Failed to find the %s \n", device_name); - return -1; + /* Find out which iio device is the accelerometer. */ + dev_num = find_type_by_name(device_name, "device"); + if (dev_num < 0) { + printf("Failed to find the %s\n", device_name); + ret = -ENODEV; + goto error_ret; } - TriggerDirectoryName = find_type_by_name(trigger_name, "trigger"); - if (TriggerDirectoryName == NULL) { + printf("iio device number being used is %d\n", dev_num); + asprintf(&dev_dir_name, "%sdevice%d", iio_dir, dev_num); + + /* + * Build the trigger name. + * In this case we want the lis3l02dq's data ready trigger + * for this lis3l02dq. The naming is lis3l02dq_dev[n], where + * n matches the device number found above. + */ + ret = asprintf(&trigger_name, "%s%d", trigger_name_base, dev_num); + if (ret < 0) { + ret = -ENOMEM; + goto error_free_dev_dir_name; + } + + /* + * Find the trigger by name. + * This is techically unecessary here as we only need to + * refer to the trigger by name and that name is already + * known. + */ + trig_num = find_type_by_name(trigger_name, "trigger"); + if (trig_num < 0) { printf("Failed to find the %s\n", trigger_name); - return -1; + ret = -ENODEV; + goto error_free_triggername; } - RingBufferDirectoryName = find_ring_subelement(BaseDirectoryName, - "ring_buffer"); - if (RingBufferDirectoryName == NULL) { - printf("Failed to find ring buffer\n"); - return -1; + printf("iio trigger number being used is %d\n", trig_num); + + /* + * Read in the scale value - in a more generic case, first + * check for accel_scale, then the indivual channel scales + */ + ret = read_sysfs_float("accel_scale", dev_dir_name, &gain); + if (ret) + goto error_free_triggername;; + + /* + * Construct the directory name for the associated buffer. + * As we know that the lis3l02dq has only one buffer this may + * be built rather than found. + */ + ret = asprintf(&buf_dir_name, "%sdevice%d:buffer0", iio_dir, dev_num); + if (ret < 0) { + ret = -ENOMEM; + goto error_free_triggername; } - - if (write_sysfs_string_and_verify("trigger/current_trigger", - BaseDirectoryName, - (char *)trigger_name) < 0) { - printf("Failed to write current_trigger file \n"); - return -1; + /* Set the device trigger to be the data rdy trigger found above */ + ret = write_sysfs_string_and_verify("trigger/current_trigger", + dev_dir_name, + trigger_name); + if (ret < 0) { + printf("Failed to write current_trigger file\n"); + goto error_free_buf_dir_name; } /* Setup ring buffer parameters */ - if (write_sysfs_int("length", RingBufferDirectoryName, - RingLength) < 0) { - printf("Failed to open the ring buffer length file \n"); - return -1; - } + ret = write_sysfs_int("length", buf_dir_name, buf_len); + if (ret < 0) + goto error_free_buf_dir_name; - /* Enable the ring buffer */ - if (write_sysfs_int("ring_enable", RingBufferDirectoryName, 1) < 0) { - printf("Failed to open the ring buffer control file \n"); - return -1; - }; + /* Enable the buffer */ + ret = write_sysfs_int("ring_enable", buf_dir_name, 1); + if (ret < 0) + goto error_free_buf_dir_name; - data = malloc(size_from_scanmode(NumVals, scan_ts)*RingLength); + data = malloc(size_from_scanmode(num_vals, scan_ts)*buf_len); if (!data) { - printf("Could not allocate space for usespace data store\n"); - return -1; + ret = -ENOMEM; + goto error_free_buf_dir_name; + } + + ret = asprintf(&buffer_access, + "/dev/device%d:buffer0:access0", + dev_num); + if (ret < 0) { + ret = -ENOMEM; + goto error_free_data; } + ret = asprintf(&buffer_event, "/dev/device%d:buffer0:event0", dev_num); + if (ret < 0) { + ret = -ENOMEM; + goto error_free_data; + } /* Attempt to open non blocking the access dev */ - fp = open(ring_access, O_RDONLY | O_NONBLOCK); + fp = open(buffer_access, O_RDONLY | O_NONBLOCK); if (fp == -1) { /*If it isn't there make the node */ - printf("Failed to open %s\n", ring_access); - return -1; + printf("Failed to open %s\n", buffer_access); + ret = -errno; + goto error_free_buffer_event; } /* Attempt to open the event access dev (blocking this time) */ - fp_ev = fopen(ring_event, "rb"); + fp_ev = fopen(buffer_event, "rb"); if (fp_ev == NULL) { - printf("Failed to open %s\n", ring_event); - return -1; + printf("Failed to open %s\n", buffer_event); + ret = -errno; + goto error_close_buffer_access; } /* Wait for events 10 times */ - for (j = 0; j < 10; j++) { + for (j = 0; j < num_loops; j++) { read_size = fread(&dat, 1, sizeof(struct iio_event_data), fp_ev); switch (dat.id) { case IIO_EVENT_CODE_RING_100_FULL: - toread = RingLength; + toread = buf_len; break; case IIO_EVENT_CODE_RING_75_FULL: - toread = RingLength*3/4; + toread = buf_len*3/4; break; case IIO_EVENT_CODE_RING_50_FULL: - toread = RingLength/2; + toread = buf_len/2; break; default: printf("Unexpecteded event code\n"); @@ -132,39 +188,51 @@ int main(int argc, char **argv) } read_size = read(fp, data, - toread*size_from_scanmode(NumVals, scan_ts)); + toread*size_from_scanmode(num_vals, scan_ts)); if (read_size == -EAGAIN) { - printf("nothing available \n"); + printf("nothing available\n"); continue; } - - for (i = 0; - i < read_size/size_from_scanmode(NumVals, scan_ts); - i++) { - for (k = 0; k < NumVals; k++) { - __s16 val = *(__s16 *)(&data[i*size_from_scanmode(NumVals, scan_ts) + scan_size = size_from_scanmode(num_vals, scan_ts); + for (i = 0; i < read_size/scan_size; i++) { + for (k = 0; k < num_vals; k++) { + __s16 val = *(__s16 *)(&data[i*scan_size + (k)*2]); - printf("%05d ", val); + printf("%05f ", (float)val*gain); } printf(" %lld\n", - *(__s64 *)(&data[(i+1)*size_from_scanmode(NumVals, scan_ts) + *(__s64 *)(&data[(i + 1) + *size_from_scanmode(num_vals, + scan_ts) - sizeof(__s64)])); } } /* Stop the ring buffer */ - if (write_sysfs_int("ring_enable", RingBufferDirectoryName, 0) < 0) { - printf("Failed to open the ring buffer control file \n"); - return -1; - }; - - /* Disconnect from the trigger - writing something that doesn't exist.*/ - write_sysfs_string_and_verify("trigger/current_trigger", - BaseDirectoryName, "NULL"); - free(BaseDirectoryName); - free(TriggerDirectoryName); - free(RingBufferDirectoryName); + ret = write_sysfs_int("ring_enable", buf_dir_name, 0); + if (ret < 0) + goto error_close_buffer_event; + + /* Disconnect from the trigger - just write a dummy name.*/ + write_sysfs_string("trigger/current_trigger", + dev_dir_name, "NULL"); + +error_close_buffer_event: + fclose(fp_ev); +error_close_buffer_access: + close(fp); +error_free_data: free(data); - - return 0; +error_free_buffer_access: + free(buffer_access); +error_free_buffer_event: + free(buffer_event); +error_free_buf_dir_name: + free(buf_dir_name); +error_free_triggername: + free(trigger_name); +error_free_dev_dir_name: + free(dev_dir_name); +error_ret: + return ret; } -- cgit v1.1 From 1e3864e63576e33dd1b26ab750ffaab3066aaba9 Mon Sep 17 00:00:00 2001 From: Barry Song Date: Tue, 4 May 2010 14:43:14 +0100 Subject: staging:iio:imu ADIS16300 driver Signed-off-by: Barry Song Acked-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/staging/iio/Kconfig | 1 + drivers/staging/iio/Makefile | 1 + drivers/staging/iio/adc/adc.h | 3 + drivers/staging/iio/gyro/gyro.h | 76 +++ drivers/staging/iio/imu/Kconfig | 14 + drivers/staging/iio/imu/Makefile | 6 + drivers/staging/iio/imu/adis16300.h | 203 +++++++ drivers/staging/iio/imu/adis16300_core.c | 833 ++++++++++++++++++++++++++++ drivers/staging/iio/imu/adis16300_ring.c | 233 ++++++++ drivers/staging/iio/imu/adis16300_trigger.c | 127 +++++ 10 files changed, 1497 insertions(+) create mode 100644 drivers/staging/iio/gyro/gyro.h create mode 100644 drivers/staging/iio/imu/Kconfig create mode 100644 drivers/staging/iio/imu/Makefile create mode 100644 drivers/staging/iio/imu/adis16300.h create mode 100644 drivers/staging/iio/imu/adis16300_core.c create mode 100644 drivers/staging/iio/imu/adis16300_ring.c create mode 100644 drivers/staging/iio/imu/adis16300_trigger.c (limited to 'drivers/staging/iio') diff --git a/drivers/staging/iio/Kconfig b/drivers/staging/iio/Kconfig index ace99f6..ed38ef4 100644 --- a/drivers/staging/iio/Kconfig +++ b/drivers/staging/iio/Kconfig @@ -41,6 +41,7 @@ config IIO_TRIGGER source "drivers/staging/iio/accel/Kconfig" source "drivers/staging/iio/adc/Kconfig" +source "drivers/staging/iio/imu/Kconfig" source "drivers/staging/iio/light/Kconfig" source "drivers/staging/iio/trigger/Kconfig" diff --git a/drivers/staging/iio/Makefile b/drivers/staging/iio/Makefile index 7ec0218..92b81c2 100644 --- a/drivers/staging/iio/Makefile +++ b/drivers/staging/iio/Makefile @@ -11,6 +11,7 @@ obj-$(CONFIG_IIO_SW_RING) += ring_sw.o obj-y += accel/ obj-y += adc/ +obj-y += imu/ obj-y += light/ obj-y += trigger/ \ No newline at end of file diff --git a/drivers/staging/iio/adc/adc.h b/drivers/staging/iio/adc/adc.h index 46f0d08..04eb16f 100644 --- a/drivers/staging/iio/adc/adc.h +++ b/drivers/staging/iio/adc/adc.h @@ -16,6 +16,9 @@ #define IIO_DEV_ATTR_IN_RAW(_num, _show, _addr) \ IIO_DEVICE_ATTR(in##_num##_raw, S_IRUGO, _show, NULL, _addr) +#define IIO_DEV_ATTR_IN_NAMED_RAW(_name, _show, _addr) \ + IIO_DEVICE_ATTR(in_##_name##_raw, S_IRUGO, _show, NULL, _addr) + #define IIO_DEV_ATTR_IN_DIFF_RAW(_nump, _numn, _show, _addr) \ IIO_DEVICE_ATTR_NAMED(in##_nump##min##_numn##_raw, \ in##_nump-in##_numn##_raw, \ diff --git a/drivers/staging/iio/gyro/gyro.h b/drivers/staging/iio/gyro/gyro.h new file mode 100644 index 0000000..7c4dcf2 --- /dev/null +++ b/drivers/staging/iio/gyro/gyro.h @@ -0,0 +1,76 @@ + +#include "../sysfs.h" + +/* Gyroscope types of attribute */ + +#define IIO_DEV_ATTR_GYRO_OFFSET(_mode, _show, _store, _addr) \ + IIO_DEVICE_ATTR(gyro_offset, _mode, _show, _store, _addr) + +#define IIO_DEV_ATTR_GYRO_X_OFFSET(_mode, _show, _store, _addr) \ + IIO_DEVICE_ATTR(gyro_x_offset, _mode, _show, _store, _addr) + +#define IIO_DEV_ATTR_GYRO_Y_OFFSET(_mode, _show, _store, _addr) \ + IIO_DEVICE_ATTR(gyro_y_offset, _mode, _show, _store, _addr) + +#define IIO_DEV_ATTR_GYRO_Z_OFFSET(_mode, _show, _store, _addr) \ + IIO_DEVICE_ATTR(gyro_z_offset, _mode, _show, _store, _addr) + +#define IIO_DEV_ATTR_GYRO_X_GAIN(_mode, _show, _store, _addr) \ + IIO_DEVICE_ATTR(gyro_x_gain, _mode, _show, _store, _addr) + +#define IIO_DEV_ATTR_GYRO_Y_GAIN(_mode, _show, _store, _addr) \ + IIO_DEVICE_ATTR(gyro_y_gain, _mode, _show, _store, _addr) + +#define IIO_DEV_ATTR_GYRO_Z_GAIN(_mode, _show, _store, _addr) \ + IIO_DEVICE_ATTR(gyro_z_gain, _mode, _show, _store, _addr) + +#define IIO_DEV_ATTR_GYRO_SCALE(_mode, _show, _store, _addr) \ + IIO_DEVICE_ATTR(gyro_scale, S_IRUGO, _show, _store, _addr) + +#define IIO_DEV_ATTR_GYRO(_show, _addr) \ + IIO_DEVICE_ATTR(gyro, S_IRUGO, _show, NULL, _addr) + +#define IIO_DEV_ATTR_GYRO_X(_show, _addr) \ + IIO_DEVICE_ATTR(gyro_x, S_IRUGO, _show, NULL, _addr) + +#define IIO_DEV_ATTR_GYRO_Y(_show, _addr) \ + IIO_DEVICE_ATTR(gyro_y, S_IRUGO, _show, NULL, _addr) + +#define IIO_DEV_ATTR_GYRO_Z(_show, _addr) \ + IIO_DEVICE_ATTR(gyro_z, S_IRUGO, _show, NULL, _addr) + +#define IIO_DEV_ATTR_TEMP_X(_show, _addr) \ + IIO_DEVICE_ATTR(temp_x, S_IRUGO, _show, NULL, _addr) + +#define IIO_DEV_ATTR_TEMP_Y(_show, _addr) \ + IIO_DEVICE_ATTR(temp_y, S_IRUGO, _show, NULL, _addr) + +#define IIO_DEV_ATTR_TEMP_Z(_show, _addr) \ + IIO_DEVICE_ATTR(temp_z, S_IRUGO, _show, NULL, _addr) + +#define IIO_DEV_ATTR_INCLI_X(_show, _addr) \ + IIO_DEVICE_ATTR(incli_x, S_IRUGO, _show, NULL, _addr) + +#define IIO_DEV_ATTR_INCLI_Y(_show, _addr) \ + IIO_DEVICE_ATTR(incli_y, S_IRUGO, _show, NULL, _addr) + +#define IIO_DEV_ATTR_INCLI_Z(_show, _addr) \ + IIO_DEVICE_ATTR(incli_z, 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_DEV_ATTR_ROT(_show, _addr) \ + IIO_DEVICE_ATTR(rot, S_IRUGO, _show, NULL, _addr) + +#define IIO_DEV_ATTR_ROT_OFFSET(_mode, _show, _store, _addr) \ + IIO_DEVICE_ATTR(rot_offset, _mode, _show, _store, _addr) + +#define IIO_DEV_ATTR_ANGL(_show, _addr) \ + IIO_DEVICE_ATTR(angl, S_IRUGO, _show, NULL, _addr) diff --git a/drivers/staging/iio/imu/Kconfig b/drivers/staging/iio/imu/Kconfig new file mode 100644 index 0000000..3fe4716 --- /dev/null +++ b/drivers/staging/iio/imu/Kconfig @@ -0,0 +1,14 @@ +# +# IIO imu drivers configuration +# +comment "Inertial measurement units" + +config ADIS16300 + tristate "Analog Devices ADIS16300 IMU SPI driver" + depends on SPI + select IIO_SW_RING + select IIO_RING_BUFFER + select IIO_TRIGGER + help + Say yes here to build support for Analog Devices adis16300 four degrees + of freedom inertial sensor. diff --git a/drivers/staging/iio/imu/Makefile b/drivers/staging/iio/imu/Makefile new file mode 100644 index 0000000..4cd5984 --- /dev/null +++ b/drivers/staging/iio/imu/Makefile @@ -0,0 +1,6 @@ +# +# 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 diff --git a/drivers/staging/iio/imu/adis16300.h b/drivers/staging/iio/imu/adis16300.h new file mode 100644 index 0000000..77d890d --- /dev/null +++ b/drivers/staging/iio/imu/adis16300.h @@ -0,0 +1,203 @@ +#ifndef SPI_ADIS16300_H_ +#define SPI_ADIS16300_H_ + +#define ADIS16300_STARTUP_DELAY 220 /* ms */ + +#define ADIS16300_READ_REG(a) a +#define ADIS16300_WRITE_REG(a) ((a) | 0x80) + +#define ADIS16300_FLASH_CNT 0x00 /* Flash memory write count */ +#define ADIS16300_SUPPLY_OUT 0x02 /* Power supply measurement */ +#define ADIS16300_XGYRO_OUT 0x04 /* X-axis gyroscope output */ +#define ADIS16300_XACCL_OUT 0x0A /* X-axis accelerometer output */ +#define ADIS16300_YACCL_OUT 0x0C /* Y-axis accelerometer output */ +#define ADIS16300_ZACCL_OUT 0x0E /* Z-axis accelerometer output */ +#define ADIS16300_TEMP_OUT 0x10 /* Temperature output */ +#define ADIS16300_XINCLI_OUT 0x12 /* X-axis inclinometer output measurement */ +#define ADIS16300_YINCLI_OUT 0x14 /* Y-axis inclinometer output measurement */ +#define ADIS16300_AUX_ADC 0x16 /* Auxiliary ADC measurement */ + +/* Calibration parameters */ +#define ADIS16300_XGYRO_OFF 0x1A /* X-axis gyroscope bias offset factor */ +#define ADIS16300_XACCL_OFF 0x20 /* X-axis acceleration bias offset factor */ +#define ADIS16300_YACCL_OFF 0x22 /* Y-axis acceleration bias offset factor */ +#define ADIS16300_ZACCL_OFF 0x24 /* Z-axis acceleration bias offset factor */ + +#define ADIS16300_GPIO_CTRL 0x32 /* Auxiliary digital input/output control */ +#define ADIS16300_MSC_CTRL 0x34 /* Miscellaneous control */ +#define ADIS16300_SMPL_PRD 0x36 /* Internal sample period (rate) control */ +#define ADIS16300_SENS_AVG 0x38 /* Dynamic range and digital filter control */ +#define ADIS16300_SLP_CNT 0x3A /* Sleep mode control */ +#define ADIS16300_DIAG_STAT 0x3C /* System status */ + +/* Alarm functions */ +#define ADIS16300_GLOB_CMD 0x3E /* System command */ +#define ADIS16300_ALM_MAG1 0x26 /* Alarm 1 amplitude threshold */ +#define ADIS16300_ALM_MAG2 0x28 /* Alarm 2 amplitude threshold */ +#define ADIS16300_ALM_SMPL1 0x2A /* Alarm 1 sample size */ +#define ADIS16300_ALM_SMPL2 0x2C /* Alarm 2 sample size */ +#define ADIS16300_ALM_CTRL 0x2E /* Alarm control */ +#define ADIS16300_AUX_DAC 0x30 /* Auxiliary DAC data */ + +#define ADIS16300_ERROR_ACTIVE (1<<14) +#define ADIS16300_NEW_DATA (1<<15) + +/* MSC_CTRL */ +#define ADIS16300_MSC_CTRL_MEM_TEST (1<<11) +#define ADIS16300_MSC_CTRL_INT_SELF_TEST (1<<10) +#define ADIS16300_MSC_CTRL_NEG_SELF_TEST (1<<9) +#define ADIS16300_MSC_CTRL_POS_SELF_TEST (1<<8) +#define ADIS16300_MSC_CTRL_GYRO_BIAS (1<<7) +#define ADIS16300_MSC_CTRL_ACCL_ALIGN (1<<6) +#define ADIS16300_MSC_CTRL_DATA_RDY_EN (1<<2) +#define ADIS16300_MSC_CTRL_DATA_RDY_POL_HIGH (1<<1) +#define ADIS16300_MSC_CTRL_DATA_RDY_DIO2 (1<<0) + +/* SMPL_PRD */ +#define ADIS16300_SMPL_PRD_TIME_BASE (1<<7) +#define ADIS16300_SMPL_PRD_DIV_MASK 0x7F + +/* DIAG_STAT */ +#define ADIS16300_DIAG_STAT_ZACCL_FAIL (1<<15) +#define ADIS16300_DIAG_STAT_YACCL_FAIL (1<<14) +#define ADIS16300_DIAG_STAT_XACCL_FAIL (1<<13) +#define ADIS16300_DIAG_STAT_XGYRO_FAIL (1<<10) +#define ADIS16300_DIAG_STAT_ALARM2 (1<<9) +#define ADIS16300_DIAG_STAT_ALARM1 (1<<8) +#define ADIS16300_DIAG_STAT_FLASH_CHK (1<<6) +#define ADIS16300_DIAG_STAT_SELF_TEST (1<<5) +#define ADIS16300_DIAG_STAT_OVERFLOW (1<<4) +#define ADIS16300_DIAG_STAT_SPI_FAIL (1<<3) +#define ADIS16300_DIAG_STAT_FLASH_UPT (1<<2) +#define ADIS16300_DIAG_STAT_POWER_HIGH (1<<1) +#define ADIS16300_DIAG_STAT_POWER_LOW (1<<0) + +/* GLOB_CMD */ +#define ADIS16300_GLOB_CMD_SW_RESET (1<<7) +#define ADIS16300_GLOB_CMD_P_AUTO_NULL (1<<4) +#define ADIS16300_GLOB_CMD_FLASH_UPD (1<<3) +#define ADIS16300_GLOB_CMD_DAC_LATCH (1<<2) +#define ADIS16300_GLOB_CMD_FAC_CALIB (1<<1) +#define ADIS16300_GLOB_CMD_AUTO_NULL (1<<0) + +/* SLP_CNT */ +#define ADIS16300_SLP_CNT_POWER_OFF (1<<8) + +#define ADIS16300_MAX_TX 18 +#define ADIS16300_MAX_RX 18 + +#define ADIS16300_SPI_SLOW (u32)(300 * 1000) +#define ADIS16300_SPI_BURST (u32)(1000 * 1000) +#define ADIS16300_SPI_FAST (u32)(2000 * 1000) + +/** + * struct adis16300_state - device instance specific data + * @us: actual spi_device + * @work_trigger_to_ring: bh for triggered event handling + * @work_cont_thresh: CLEAN + * @inter: used to check if new interrupt has been triggered + * @last_timestamp: passing timestamp from th to bh of interrupt handler + * @indio_dev: industrial I/O device structure + * @trig: data ready trigger registered with iio + * @tx: transmit buffer + * @rx: recieve buffer + * @buf_lock: mutex to protect tx and rx + **/ +struct adis16300_state { + struct spi_device *us; + struct work_struct work_trigger_to_ring; + struct iio_work_cont work_cont_thresh; + s64 last_timestamp; + struct iio_dev *indio_dev; + struct iio_trigger *trig; + u8 *tx; + u8 *rx; + struct mutex buf_lock; +}; + +int adis16300_spi_write_reg_8(struct device *dev, + u8 reg_address, + u8 val); + +int adis16300_spi_read_burst(struct device *dev, u8 *rx); + +int adis16300_spi_read_sequence(struct device *dev, + u8 *tx, u8 *rx, int num); + +int adis16300_set_irq(struct device *dev, bool enable); + +int adis16300_reset(struct device *dev); + +int adis16300_stop_device(struct device *dev); + +int adis16300_check_status(struct device *dev); + +#ifdef CONFIG_IIO_RING_BUFFER +/* At the moment triggers are only used for ring buffer + * filling. This may change! + */ + +enum adis16300_scan { + ADIS16300_SCAN_SUPPLY, + ADIS16300_SCAN_GYRO_X, + ADIS16300_SCAN_ACC_X, + ADIS16300_SCAN_ACC_Y, + ADIS16300_SCAN_ACC_Z, + ADIS16300_SCAN_TEMP, + ADIS16300_SCAN_ADC_0, + ADIS16300_SCAN_INCLI_X, + ADIS16300_SCAN_INCLI_Y, +}; + +void adis16300_remove_trigger(struct iio_dev *indio_dev); +int adis16300_probe_trigger(struct iio_dev *indio_dev); + +ssize_t adis16300_read_data_from_ring(struct device *dev, + struct device_attribute *attr, + char *buf); + + +int adis16300_configure_ring(struct iio_dev *indio_dev); +void adis16300_unconfigure_ring(struct iio_dev *indio_dev); + +int adis16300_initialize_ring(struct iio_ring_buffer *ring); +void adis16300_uninitialize_ring(struct iio_ring_buffer *ring); +#else /* CONFIG_IIO_RING_BUFFER */ + +static inline void adis16300_remove_trigger(struct iio_dev *indio_dev) +{ +} + +static inline int adis16300_probe_trigger(struct iio_dev *indio_dev) +{ + return 0; +} + +static inline ssize_t +adis16300_read_data_from_ring(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return 0; +} + +static int adis16300_configure_ring(struct iio_dev *indio_dev) +{ + return 0; +} + +static inline void adis16300_unconfigure_ring(struct iio_dev *indio_dev) +{ +} + +static inline int adis16300_initialize_ring(struct iio_ring_buffer *ring) +{ + return 0; +} + +static inline void adis16300_uninitialize_ring(struct iio_ring_buffer *ring) +{ +} + +#endif /* CONFIG_IIO_RING_BUFFER */ +#endif /* SPI_ADIS16300_H_ */ diff --git a/drivers/staging/iio/imu/adis16300_core.c b/drivers/staging/iio/imu/adis16300_core.c new file mode 100644 index 0000000..9f5f8cb --- /dev/null +++ b/drivers/staging/iio/imu/adis16300_core.c @@ -0,0 +1,833 @@ +/* + * ADIS16300 Four Degrees of Freedom Inertial Sensor Driver + * + * Copyright 2010 Analog Devices Inc. + * + * Licensed under the GPL-2 or later. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "../iio.h" +#include "../sysfs.h" +#include "../accel/accel.h" +#include "../gyro/gyro.h" +#include "../adc/adc.h" + +#include "adis16300.h" + +#define DRIVER_NAME "adis16300" + +/* At the moment the spi framework doesn't allow global setting of cs_change. + * It's in the likely to be added comment at the top of spi.h. + * This means that use cannot be made of spi_write etc. + */ + +/** + * adis16300_spi_write_reg_8() - write single byte to a register + * @dev: device associated with child of actual device (iio_dev or iio_trig) + * @reg_address: the address of the register to be written + * @val: the value to write + **/ +int adis16300_spi_write_reg_8(struct device *dev, + u8 reg_address, + u8 val) +{ + int ret; + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct adis16300_state *st = iio_dev_get_devdata(indio_dev); + + mutex_lock(&st->buf_lock); + st->tx[0] = ADIS16300_WRITE_REG(reg_address); + st->tx[1] = val; + + ret = spi_write(st->us, st->tx, 2); + mutex_unlock(&st->buf_lock); + + return ret; +} + +/** + * adis16300_spi_write_reg_16() - write 2 bytes to a pair of registers + * @dev: device associated with child of actual device (iio_dev or iio_trig) + * @reg_address: the address of the lower of the two registers. Second register + * is assumed to have address one greater. + * @val: value to be written + **/ +static int adis16300_spi_write_reg_16(struct device *dev, + u8 lower_reg_address, + u16 value) +{ + int ret; + struct spi_message msg; + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct adis16300_state *st = iio_dev_get_devdata(indio_dev); + struct spi_transfer xfers[] = { + { + .tx_buf = st->tx, + .bits_per_word = 8, + .len = 2, + .cs_change = 1, + }, { + .tx_buf = st->tx + 2, + .bits_per_word = 8, + .len = 2, + .cs_change = 1, + }, + }; + + mutex_lock(&st->buf_lock); + st->tx[0] = ADIS16300_WRITE_REG(lower_reg_address); + st->tx[1] = value & 0xFF; + st->tx[2] = ADIS16300_WRITE_REG(lower_reg_address + 1); + st->tx[3] = (value >> 8) & 0xFF; + + spi_message_init(&msg); + spi_message_add_tail(&xfers[0], &msg); + spi_message_add_tail(&xfers[1], &msg); + ret = spi_sync(st->us, &msg); + mutex_unlock(&st->buf_lock); + + return ret; +} + +/** + * adis16300_spi_read_reg_16() - read 2 bytes from a 16-bit register + * @dev: device associated with child of actual device (iio_dev or iio_trig) + * @reg_address: the address of the lower of the two registers. Second register + * is assumed to have address one greater. + * @val: somewhere to pass back the value read + **/ +static int adis16300_spi_read_reg_16(struct device *dev, + u8 lower_reg_address, + u16 *val) +{ + struct spi_message msg; + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct adis16300_state *st = iio_dev_get_devdata(indio_dev); + int ret; + struct spi_transfer xfers[] = { + { + .tx_buf = st->tx, + .bits_per_word = 8, + .len = 2, + .cs_change = 0, + }, { + .rx_buf = st->rx, + .bits_per_word = 8, + .len = 2, + .cs_change = 0, + }, + }; + + mutex_lock(&st->buf_lock); + st->tx[0] = ADIS16300_READ_REG(lower_reg_address); + st->tx[1] = 0; + st->tx[2] = 0; + st->tx[3] = 0; + + spi_message_init(&msg); + spi_message_add_tail(&xfers[0], &msg); + spi_message_add_tail(&xfers[1], &msg); + ret = spi_sync(st->us, &msg); + if (ret) { + dev_err(&st->us->dev, + "problem when reading 16 bit register 0x%02X", + lower_reg_address); + goto error_ret; + } + *val = (st->rx[0] << 8) | st->rx[1]; + +error_ret: + mutex_unlock(&st->buf_lock); + return ret; +} + +/** + * adis16300_spi_read_burst() - read all data registers + * @dev: device associated with child of actual device (iio_dev or iio_trig) + * @rx: somewhere to pass back the value read (min size is 24 bytes) + **/ +int adis16300_spi_read_burst(struct device *dev, u8 *rx) +{ + struct spi_message msg; + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct adis16300_state *st = iio_dev_get_devdata(indio_dev); + u32 old_speed_hz = st->us->max_speed_hz; + int ret; + + struct spi_transfer xfers[] = { + { + .tx_buf = st->tx, + .bits_per_word = 8, + .len = 2, + .cs_change = 0, + }, { + .rx_buf = rx, + .bits_per_word = 8, + .len = 18, + .cs_change = 0, + }, + }; + + mutex_lock(&st->buf_lock); + st->tx[0] = ADIS16300_READ_REG(ADIS16300_GLOB_CMD); + st->tx[1] = 0; + + spi_message_init(&msg); + spi_message_add_tail(&xfers[0], &msg); + spi_message_add_tail(&xfers[1], &msg); + + st->us->max_speed_hz = min(ADIS16300_SPI_BURST, old_speed_hz); + spi_setup(st->us); + + ret = spi_sync(st->us, &msg); + if (ret) + dev_err(&st->us->dev, "problem when burst reading"); + + st->us->max_speed_hz = old_speed_hz; + spi_setup(st->us); + mutex_unlock(&st->buf_lock); + return ret; +} + +/** + * adis16300_spi_read_sequence() - read a sequence of 16-bit registers + * @dev: device associated with child of actual device (iio_dev or iio_trig) + * @tx: register addresses in bytes 0,2,4,6... (min size is 2*num bytes) + * @rx: somewhere to pass back the value read (min size is 2*num bytes) + **/ +int adis16300_spi_read_sequence(struct device *dev, + u8 *tx, u8 *rx, int num) +{ + struct spi_message msg; + struct spi_transfer *xfers; + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct adis16300_state *st = iio_dev_get_devdata(indio_dev); + int ret, i; + + xfers = kzalloc(num + 1, GFP_KERNEL); + if (xfers == NULL) { + dev_err(&st->us->dev, "memory alloc failed"); + ret = -ENOMEM; + goto error_ret; + } + + /* tx: |add1|addr2|addr3|...|addrN |zero| + * rx: |zero|res1 |res2 |...|resN-1|resN| */ + spi_message_init(&msg); + for (i = 0; i < num + 1; i++) { + if (i > 0) + xfers[i].rx_buf = st->rx + 2*(i - 1); + if (i < num) + xfers[i].tx_buf = st->tx + 2*i; + xfers[i].bits_per_word = 8; + xfers[i].len = 2; + xfers[i].cs_change = 1; + spi_message_add_tail(&xfers[i], &msg); + } + + mutex_lock(&st->buf_lock); + + ret = spi_sync(st->us, &msg); + if (ret) + dev_err(&st->us->dev, "problem when reading sequence"); + + mutex_unlock(&st->buf_lock); + kfree(xfers); + +error_ret: + return ret; +} + +static ssize_t adis16300_spi_read_signed(struct device *dev, + struct device_attribute *attr, + char *buf, + unsigned bits) +{ + int ret; + s16 val = 0; + unsigned shift = 16 - bits; + struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); + + ret = adis16300_spi_read_reg_16(dev, this_attr->address, (u16 *)&val); + if (ret) + return ret; + + if (val & ADIS16300_ERROR_ACTIVE) + adis16300_check_status(dev); + val = ((s16)(val << shift) >> shift); + return sprintf(buf, "%d\n", val); +} + +static ssize_t adis16300_read_12bit_unsigned(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int ret; + u16 val = 0; + struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); + + ret = adis16300_spi_read_reg_16(dev, this_attr->address, &val); + if (ret) + return ret; + + if (val & ADIS16300_ERROR_ACTIVE) + adis16300_check_status(dev); + + return sprintf(buf, "%u\n", val & 0x0FFF); +} + +static ssize_t adis16300_read_14bit_signed(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + ssize_t ret; + + /* Take the iio_dev status lock */ + mutex_lock(&indio_dev->mlock); + ret = adis16300_spi_read_signed(dev, attr, buf, 14); + mutex_unlock(&indio_dev->mlock); + + return ret; +} + +static ssize_t adis16300_read_12bit_signed(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + ssize_t ret; + + /* Take the iio_dev status lock */ + mutex_lock(&indio_dev->mlock); + ret = adis16300_spi_read_signed(dev, attr, buf, 12); + mutex_unlock(&indio_dev->mlock); + + return ret; +} + +static ssize_t adis16300_read_13bit_signed(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + ssize_t ret; + + /* Take the iio_dev status lock */ + mutex_lock(&indio_dev->mlock); + ret = adis16300_spi_read_signed(dev, attr, buf, 13); + mutex_unlock(&indio_dev->mlock); + + return ret; +} + +static ssize_t adis16300_write_16bit(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t len) +{ + struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); + int ret; + long val; + + ret = strict_strtol(buf, 10, &val); + if (ret) + goto error_ret; + ret = adis16300_spi_write_reg_16(dev, this_attr->address, val); + +error_ret: + return ret ? ret : len; +} + +static ssize_t adis16300_read_frequency(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int ret, len = 0; + u16 t; + int sps; + ret = adis16300_spi_read_reg_16(dev, + ADIS16300_SMPL_PRD, + &t); + if (ret) + return ret; + sps = (t & ADIS16300_SMPL_PRD_TIME_BASE) ? 53 : 1638; + sps /= (t & ADIS16300_SMPL_PRD_DIV_MASK) + 1; + len = sprintf(buf, "%d SPS\n", sps); + return len; +} + +static ssize_t adis16300_write_frequency(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t len) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct adis16300_state *st = iio_dev_get_devdata(indio_dev); + long val; + int ret; + u8 t; + + ret = strict_strtol(buf, 10, &val); + if (ret) + return ret; + + mutex_lock(&indio_dev->mlock); + + t = (1638 / val); + if (t > 0) + t--; + t &= ADIS16300_SMPL_PRD_DIV_MASK; + if ((t & ADIS16300_SMPL_PRD_DIV_MASK) >= 0x0A) + st->us->max_speed_hz = ADIS16300_SPI_SLOW; + else + st->us->max_speed_hz = ADIS16300_SPI_FAST; + + ret = adis16300_spi_write_reg_8(dev, + ADIS16300_SMPL_PRD, + t); + + mutex_unlock(&indio_dev->mlock); + + return ret ? ret : len; +} + +static ssize_t adis16300_write_reset(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + if (len < 1) + return -1; + switch (buf[0]) { + case '1': + case 'y': + case 'Y': + return adis16300_reset(dev); + } + return -1; +} + + + +int adis16300_set_irq(struct device *dev, bool enable) +{ + int ret; + u16 msc; + ret = adis16300_spi_read_reg_16(dev, ADIS16300_MSC_CTRL, &msc); + if (ret) + goto error_ret; + + msc |= ADIS16300_MSC_CTRL_DATA_RDY_POL_HIGH; + msc &= ~ADIS16300_MSC_CTRL_DATA_RDY_DIO2; + if (enable) + msc |= ADIS16300_MSC_CTRL_DATA_RDY_EN; + else + msc &= ~ADIS16300_MSC_CTRL_DATA_RDY_EN; + + ret = adis16300_spi_write_reg_16(dev, ADIS16300_MSC_CTRL, msc); + if (ret) + goto error_ret; + +error_ret: + return ret; +} + +int adis16300_reset(struct device *dev) +{ + int ret; + ret = adis16300_spi_write_reg_8(dev, + ADIS16300_GLOB_CMD, + ADIS16300_GLOB_CMD_SW_RESET); + if (ret) + dev_err(dev, "problem resetting device"); + + return ret; +} + +/* Power down the device */ +int adis16300_stop_device(struct device *dev) +{ + int ret; + u16 val = ADIS16300_SLP_CNT_POWER_OFF; + + ret = adis16300_spi_write_reg_16(dev, ADIS16300_SLP_CNT, val); + if (ret) + dev_err(dev, "problem with turning device off: SLP_CNT"); + + return ret; +} + +int adis16300_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; +} + +int adis16300_check_status(struct device *dev) +{ + u16 status; + int ret; + + ret = adis16300_spi_read_reg_16(dev, ADIS16300_DIAG_STAT, &status); + + if (ret < 0) { + dev_err(dev, "Reading status failed\n"); + goto error_ret; + } + ret = status; + if (status & ADIS16300_DIAG_STAT_ZACCL_FAIL) + dev_err(dev, "Z-axis accelerometer self-test failure\n"); + if (status & ADIS16300_DIAG_STAT_YACCL_FAIL) + dev_err(dev, "Y-axis accelerometer self-test failure\n"); + if (status & ADIS16300_DIAG_STAT_XACCL_FAIL) + dev_err(dev, "X-axis accelerometer self-test failure\n"); + if (status & ADIS16300_DIAG_STAT_XGYRO_FAIL) + dev_err(dev, "X-axis gyroscope self-test failure\n"); + if (status & ADIS16300_DIAG_STAT_ALARM2) + dev_err(dev, "Alarm 2 active\n"); + if (status & ADIS16300_DIAG_STAT_ALARM1) + dev_err(dev, "Alarm 1 active\n"); + if (status & ADIS16300_DIAG_STAT_FLASH_CHK) + dev_err(dev, "Flash checksum error\n"); + if (status & ADIS16300_DIAG_STAT_SELF_TEST) + dev_err(dev, "Self test error\n"); + if (status & ADIS16300_DIAG_STAT_OVERFLOW) + dev_err(dev, "Sensor overrange\n"); + if (status & ADIS16300_DIAG_STAT_SPI_FAIL) + dev_err(dev, "SPI failure\n"); + if (status & ADIS16300_DIAG_STAT_FLASH_UPT) + dev_err(dev, "Flash update failed\n"); + if (status & ADIS16300_DIAG_STAT_POWER_HIGH) + dev_err(dev, "Power supply above 5.25V\n"); + if (status & ADIS16300_DIAG_STAT_POWER_LOW) + dev_err(dev, "Power supply below 4.75V\n"); + +error_ret: + return ret; +} + +static int adis16300_initial_setup(struct adis16300_state *st) +{ + int ret; + u16 smp_prd; + struct device *dev = &st->indio_dev->dev; + + /* use low spi speed for init */ + st->us->max_speed_hz = ADIS16300_SPI_SLOW; + st->us->mode = SPI_MODE_3; + spi_setup(st->us); + + /* Disable IRQ */ + ret = adis16300_set_irq(dev, false); + if (ret) { + dev_err(dev, "disable irq failed"); + goto err_ret; + } + + /* Do self test */ + + /* Read status register to check the result */ + ret = adis16300_check_status(dev); + if (ret) { + adis16300_reset(dev); + dev_err(dev, "device not playing ball -> reset"); + msleep(ADIS16300_STARTUP_DELAY); + ret = adis16300_check_status(dev); + if (ret) { + dev_err(dev, "giving up"); + goto err_ret; + } + } + + printk(KERN_INFO DRIVER_NAME ": at CS%d (irq %d)\n", + st->us->chip_select, st->us->irq); + + /* use high spi speed if possible */ + ret = adis16300_spi_read_reg_16(dev, ADIS16300_SMPL_PRD, &smp_prd); + if (!ret && (smp_prd & ADIS16300_SMPL_PRD_DIV_MASK) < 0x0A) { + st->us->max_speed_hz = ADIS16300_SPI_SLOW; + spi_setup(st->us); + } + +err_ret: + return ret; +} + +static IIO_DEV_ATTR_ACCEL_X_OFFSET(S_IWUSR | S_IRUGO, + adis16300_read_12bit_signed, + adis16300_write_16bit, + ADIS16300_XACCL_OFF); + +static IIO_DEV_ATTR_ACCEL_Y_OFFSET(S_IWUSR | S_IRUGO, + adis16300_read_12bit_signed, + adis16300_write_16bit, + ADIS16300_YACCL_OFF); + +static IIO_DEV_ATTR_ACCEL_Z_OFFSET(S_IWUSR | S_IRUGO, + adis16300_read_12bit_signed, + adis16300_write_16bit, + ADIS16300_ZACCL_OFF); + +static IIO_DEV_ATTR_IN_NAMED_RAW(supply, adis16300_read_14bit_signed, + ADIS16300_SUPPLY_OUT); +static IIO_CONST_ATTR(in_supply_scale, "0.00242"); + +static IIO_DEV_ATTR_GYRO_X(adis16300_read_14bit_signed, + ADIS16300_XGYRO_OUT); +static IIO_CONST_ATTR(gyro_scale, "0.05 deg/s"); + +static IIO_DEV_ATTR_ACCEL_X(adis16300_read_14bit_signed, + ADIS16300_XACCL_OUT); +static IIO_DEV_ATTR_ACCEL_Y(adis16300_read_14bit_signed, + ADIS16300_YACCL_OUT); +static IIO_DEV_ATTR_ACCEL_Z(adis16300_read_14bit_signed, + ADIS16300_ZACCL_OUT); +static IIO_CONST_ATTR(accel_scale, "0.0006 g"); + +static IIO_DEV_ATTR_INCLI_X(adis16300_read_13bit_signed, + ADIS16300_XINCLI_OUT); +static IIO_DEV_ATTR_INCLI_Y(adis16300_read_13bit_signed, + ADIS16300_YINCLI_OUT); +static IIO_CONST_ATTR(incli_scale, "0.044 d"); + +static IIO_DEV_ATTR_TEMP(adis16300_read_12bit_signed); +static IIO_CONST_ATTR(temp_offset, "198.16 K"); +static IIO_CONST_ATTR(temp_scale, "0.14 K"); + +static IIO_DEV_ATTR_IN_RAW(0, adis16300_read_12bit_unsigned, + ADIS16300_AUX_ADC); +static IIO_CONST_ATTR(in0_scale, "0.000806"); + +static IIO_DEV_ATTR_SAMP_FREQ(S_IWUSR | S_IRUGO, + adis16300_read_frequency, + adis16300_write_frequency); + +static IIO_DEVICE_ATTR(reset, S_IWUSR, NULL, adis16300_write_reset, 0); + +static IIO_CONST_ATTR_AVAIL_SAMP_FREQ("409 546 819 1638"); + +static IIO_CONST_ATTR(name, "adis16300"); + +static struct attribute *adis16300_event_attributes[] = { + NULL +}; + +static struct attribute_group adis16300_event_attribute_group = { + .attrs = adis16300_event_attributes, +}; + +static struct attribute *adis16300_attributes[] = { + &iio_dev_attr_accel_x_offset.dev_attr.attr, + &iio_dev_attr_accel_y_offset.dev_attr.attr, + &iio_dev_attr_accel_z_offset.dev_attr.attr, + &iio_dev_attr_in_supply_raw.dev_attr.attr, + &iio_const_attr_in_supply_scale.dev_attr.attr, + &iio_dev_attr_gyro_x.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.dev_attr.attr, + &iio_dev_attr_incli_y.dev_attr.attr, + &iio_const_attr_incli_scale.dev_attr.attr, + &iio_dev_attr_temp.dev_attr.attr, + &iio_const_attr_temp_offset.dev_attr.attr, + &iio_const_attr_temp_scale.dev_attr.attr, + &iio_dev_attr_in0_raw.dev_attr.attr, + &iio_const_attr_in0_scale.dev_attr.attr, + &iio_dev_attr_sampling_frequency.dev_attr.attr, + &iio_const_attr_available_sampling_frequency.dev_attr.attr, + &iio_dev_attr_reset.dev_attr.attr, + &iio_const_attr_name.dev_attr.attr, + NULL +}; + +static const struct attribute_group adis16300_attribute_group = { + .attrs = adis16300_attributes, +}; + +static int __devinit adis16300_probe(struct spi_device *spi) +{ + int ret, regdone = 0; + struct adis16300_state *st = kzalloc(sizeof *st, GFP_KERNEL); + if (!st) { + ret = -ENOMEM; + goto error_ret; + } + /* this is only used for removal purposes */ + spi_set_drvdata(spi, st); + + /* Allocate the comms buffers */ + st->rx = kzalloc(sizeof(*st->rx)*ADIS16300_MAX_RX, GFP_KERNEL); + if (st->rx == NULL) { + ret = -ENOMEM; + goto error_free_st; + } + st->tx = kzalloc(sizeof(*st->tx)*ADIS16300_MAX_TX, GFP_KERNEL); + if (st->tx == NULL) { + ret = -ENOMEM; + goto error_free_rx; + } + st->us = spi; + mutex_init(&st->buf_lock); + /* setup the industrialio driver allocated elements */ + st->indio_dev = iio_allocate_device(); + if (st->indio_dev == NULL) { + ret = -ENOMEM; + goto error_free_tx; + } + + st->indio_dev->dev.parent = &spi->dev; + st->indio_dev->num_interrupt_lines = 1; + st->indio_dev->event_attrs = &adis16300_event_attribute_group; + st->indio_dev->attrs = &adis16300_attribute_group; + st->indio_dev->dev_data = (void *)(st); + st->indio_dev->driver_module = THIS_MODULE; + st->indio_dev->modes = INDIO_DIRECT_MODE; + + ret = adis16300_configure_ring(st->indio_dev); + if (ret) + goto error_free_dev; + + ret = iio_device_register(st->indio_dev); + if (ret) + goto error_unreg_ring_funcs; + regdone = 1; + + ret = adis16300_initialize_ring(st->indio_dev->ring); + if (ret) { + printk(KERN_ERR "failed to initialize the ring\n"); + goto error_unreg_ring_funcs; + } + + if (spi->irq && gpio_is_valid(irq_to_gpio(spi->irq)) > 0) { +#if 0 /* fixme: here we should support */ + iio_init_work_cont(&st->work_cont_thresh, + NULL, + adis16300_thresh_handler_bh_no_check, + 0, + 0, + st); +#endif + ret = iio_register_interrupt_line(spi->irq, + st->indio_dev, + 0, + IRQF_TRIGGER_RISING, + "adis16300"); + if (ret) + goto error_uninitialize_ring; + + ret = adis16300_probe_trigger(st->indio_dev); + if (ret) + goto error_unregister_line; + } + + /* Get the device into a sane initial state */ + ret = adis16300_initial_setup(st); + if (ret) + goto error_remove_trigger; + return 0; + +error_remove_trigger: + if (st->indio_dev->modes & INDIO_RING_TRIGGERED) + adis16300_remove_trigger(st->indio_dev); +error_unregister_line: + if (st->indio_dev->modes & INDIO_RING_TRIGGERED) + iio_unregister_interrupt_line(st->indio_dev, 0); +error_uninitialize_ring: + adis16300_uninitialize_ring(st->indio_dev->ring); +error_unreg_ring_funcs: + adis16300_unconfigure_ring(st->indio_dev); +error_free_dev: + if (regdone) + iio_device_unregister(st->indio_dev); + else + iio_free_device(st->indio_dev); +error_free_tx: + kfree(st->tx); +error_free_rx: + kfree(st->rx); +error_free_st: + kfree(st); +error_ret: + return ret; +} + +/* fixme, confirm ordering in this function */ +static int adis16300_remove(struct spi_device *spi) +{ + int ret; + struct adis16300_state *st = spi_get_drvdata(spi); + struct iio_dev *indio_dev = st->indio_dev; + + ret = adis16300_stop_device(&(indio_dev->dev)); + if (ret) + goto err_ret; + + flush_scheduled_work(); + + adis16300_remove_trigger(indio_dev); + if (spi->irq && gpio_is_valid(irq_to_gpio(spi->irq)) > 0) + iio_unregister_interrupt_line(indio_dev, 0); + + adis16300_uninitialize_ring(indio_dev->ring); + adis16300_unconfigure_ring(indio_dev); + iio_device_unregister(indio_dev); + kfree(st->tx); + kfree(st->rx); + kfree(st); + + return 0; + +err_ret: + return ret; +} + +static struct spi_driver adis16300_driver = { + .driver = { + .name = "adis16300", + .owner = THIS_MODULE, + }, + .probe = adis16300_probe, + .remove = __devexit_p(adis16300_remove), +}; + +static __init int adis16300_init(void) +{ + return spi_register_driver(&adis16300_driver); +} +module_init(adis16300_init); + +static __exit void adis16300_exit(void) +{ + spi_unregister_driver(&adis16300_driver); +} +module_exit(adis16300_exit); + +MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>"); +MODULE_DESCRIPTION("Analog Devices ADIS16300 IMU SPI driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/iio/imu/adis16300_ring.c b/drivers/staging/iio/imu/adis16300_ring.c new file mode 100644 index 0000000..76cf8a6 --- /dev/null +++ b/drivers/staging/iio/imu/adis16300_ring.c @@ -0,0 +1,233 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../iio.h" +#include "../sysfs.h" +#include "../ring_sw.h" +#include "../accel/accel.h" +#include "../trigger.h" +#include "adis16300.h" + +/** + * combine_8_to_16() utility function to munge to u8s into u16 + **/ +static inline u16 combine_8_to_16(u8 lower, u8 upper) +{ + u16 _lower = lower; + u16 _upper = upper; + return _lower | (_upper << 8); +} + +static IIO_SCAN_EL_C(supply, ADIS16300_SCAN_SUPPLY, IIO_SIGNED(14), + ADIS16300_SUPPLY_OUT, NULL); + +static IIO_SCAN_EL_C(gyro_x, ADIS16300_SCAN_GYRO_X, IIO_SIGNED(14), + ADIS16300_XGYRO_OUT, NULL); + +static IIO_SCAN_EL_C(accel_x, ADIS16300_SCAN_ACC_X, IIO_SIGNED(14), + ADIS16300_XACCL_OUT, NULL); +static IIO_SCAN_EL_C(accel_y, ADIS16300_SCAN_ACC_Y, IIO_SIGNED(14), + ADIS16300_YACCL_OUT, NULL); +static IIO_SCAN_EL_C(accel_z, ADIS16300_SCAN_ACC_Z, IIO_SIGNED(14), + ADIS16300_ZACCL_OUT, NULL); + +static IIO_SCAN_EL_C(temp, ADIS16300_SCAN_TEMP, IIO_SIGNED(12), + ADIS16300_TEMP_OUT, NULL); +static IIO_SCAN_EL_C(adc_0, ADIS16300_SCAN_ADC_0, IIO_SIGNED(12), + ADIS16300_AUX_ADC, NULL); + +static IIO_SCAN_EL_C(incli_x, ADIS16300_SCAN_INCLI_X, IIO_SIGNED(12), + ADIS16300_XINCLI_OUT, NULL); +static IIO_SCAN_EL_C(incli_y, ADIS16300_SCAN_INCLI_Y, IIO_SIGNED(12), + ADIS16300_YINCLI_OUT, NULL); + +static IIO_SCAN_EL_TIMESTAMP(9); + +static struct attribute *adis16300_scan_el_attrs[] = { + &iio_scan_el_supply.dev_attr.attr, + &iio_scan_el_gyro_x.dev_attr.attr, + &iio_scan_el_temp.dev_attr.attr, + &iio_scan_el_accel_x.dev_attr.attr, + &iio_scan_el_accel_y.dev_attr.attr, + &iio_scan_el_accel_z.dev_attr.attr, + &iio_scan_el_incli_x.dev_attr.attr, + &iio_scan_el_incli_y.dev_attr.attr, + &iio_scan_el_adc_0.dev_attr.attr, + &iio_scan_el_timestamp.dev_attr.attr, + NULL, +}; + +static struct attribute_group adis16300_scan_el_group = { + .attrs = adis16300_scan_el_attrs, + .name = "scan_elements", +}; + +/** + * adis16300_poll_func_th() top half interrupt handler called by trigger + * @private_data: iio_dev + **/ +static void adis16300_poll_func_th(struct iio_dev *indio_dev) +{ + struct adis16300_state *st = iio_dev_get_devdata(indio_dev); + st->last_timestamp = indio_dev->trig->timestamp; + schedule_work(&st->work_trigger_to_ring); + /* Indicate that this interrupt is being handled */ + + /* Technically this is trigger related, but without this + * handler running there is currently no way for the interrupt + * to clear. + */ +} + +/* Whilst this makes a lot of calls to iio_sw_ring functions - it is to device + * specific to be rolled into the core. + */ +static void adis16300_trigger_bh_to_ring(struct work_struct *work_s) +{ + struct adis16300_state *st + = container_of(work_s, struct adis16300_state, + work_trigger_to_ring); + + int i = 0; + s16 *data; + size_t datasize = st->indio_dev + ->ring->access.get_bpd(st->indio_dev->ring); + + data = kmalloc(datasize , GFP_KERNEL); + if (data == NULL) { + dev_err(&st->us->dev, "memory alloc failed in ring bh"); + return; + } + + if (st->indio_dev->scan_count) + if (adis16300_spi_read_burst(&st->indio_dev->dev, st->rx) >= 0) + for (; i < st->indio_dev->scan_count; i++) { + data[i] = combine_8_to_16(st->rx[i*2+1], + st->rx[i*2]); + } + + /* Guaranteed to be aligned with 8 byte boundary */ + if (st->indio_dev->scan_timestamp) + *((s64 *)(data + ((i + 3)/4)*4)) = st->last_timestamp; + + st->indio_dev->ring->access.store_to(st->indio_dev->ring, + (u8 *)data, + st->last_timestamp); + + iio_trigger_notify_done(st->indio_dev->trig); + kfree(data); + + return; +} +/* in these circumstances is it better to go with unaligned packing and + * deal with the cost?*/ +static int adis16300_data_rdy_ring_preenable(struct iio_dev *indio_dev) +{ + size_t size; + dev_dbg(&indio_dev->dev, "%s\n", __func__); + /* Check if there are any scan elements enabled, if not fail*/ + if (!(indio_dev->scan_count || indio_dev->scan_timestamp)) + return -EINVAL; + + if (indio_dev->ring->access.set_bpd) { + if (indio_dev->scan_timestamp) + if (indio_dev->scan_count) /* Timestamp and data */ + size = 4*sizeof(s64); + else /* Timestamp only */ + size = sizeof(s64); + else /* Data only */ + size = indio_dev->scan_count*sizeof(s16); + indio_dev->ring->access.set_bpd(indio_dev->ring, size); + } + + return 0; +} + +static int adis16300_data_rdy_ring_postenable(struct iio_dev *indio_dev) +{ + return indio_dev->trig + ? iio_trigger_attach_poll_func(indio_dev->trig, + indio_dev->pollfunc) + : 0; +} + +static int adis16300_data_rdy_ring_predisable(struct iio_dev *indio_dev) +{ + return indio_dev->trig + ? iio_trigger_dettach_poll_func(indio_dev->trig, + indio_dev->pollfunc) + : 0; +} + +void adis16300_unconfigure_ring(struct iio_dev *indio_dev) +{ + kfree(indio_dev->pollfunc); + iio_sw_rb_free(indio_dev->ring); +} + +int adis16300_configure_ring(struct iio_dev *indio_dev) +{ + int ret = 0; + struct adis16300_state *st = indio_dev->dev_data; + struct iio_ring_buffer *ring; + INIT_WORK(&st->work_trigger_to_ring, adis16300_trigger_bh_to_ring); + /* Set default scan mode */ + + iio_scan_mask_set(indio_dev, iio_scan_el_supply.number); + iio_scan_mask_set(indio_dev, iio_scan_el_gyro_x.number); + iio_scan_mask_set(indio_dev, iio_scan_el_accel_x.number); + iio_scan_mask_set(indio_dev, iio_scan_el_accel_y.number); + iio_scan_mask_set(indio_dev, iio_scan_el_accel_z.number); + iio_scan_mask_set(indio_dev, iio_scan_el_temp.number); + iio_scan_mask_set(indio_dev, iio_scan_el_adc_0.number); + iio_scan_mask_set(indio_dev, iio_scan_el_incli_x.number); + iio_scan_mask_set(indio_dev, iio_scan_el_incli_y.number); + indio_dev->scan_timestamp = true; + + indio_dev->scan_el_attrs = &adis16300_scan_el_group; + + ring = iio_sw_rb_allocate(indio_dev); + if (!ring) { + ret = -ENOMEM; + return ret; + } + indio_dev->ring = ring; + /* Effectively select the ring buffer implementation */ + iio_ring_sw_register_funcs(&ring->access); + ring->preenable = &adis16300_data_rdy_ring_preenable; + ring->postenable = &adis16300_data_rdy_ring_postenable; + ring->predisable = &adis16300_data_rdy_ring_predisable; + ring->owner = THIS_MODULE; + + indio_dev->pollfunc = kzalloc(sizeof(*indio_dev->pollfunc), GFP_KERNEL); + if (indio_dev->pollfunc == NULL) { + ret = -ENOMEM; + goto error_iio_sw_rb_free;; + } + indio_dev->pollfunc->poll_func_main = &adis16300_poll_func_th; + indio_dev->pollfunc->private_data = indio_dev; + indio_dev->modes |= INDIO_RING_TRIGGERED; + return 0; + +error_iio_sw_rb_free: + iio_sw_rb_free(indio_dev->ring); + return ret; +} + +int adis16300_initialize_ring(struct iio_ring_buffer *ring) +{ + return iio_ring_buffer_register(ring, 0); +} + +void adis16300_uninitialize_ring(struct iio_ring_buffer *ring) +{ + iio_ring_buffer_unregister(ring); +} diff --git a/drivers/staging/iio/imu/adis16300_trigger.c b/drivers/staging/iio/imu/adis16300_trigger.c new file mode 100644 index 0000000..54edb20b --- /dev/null +++ b/drivers/staging/iio/imu/adis16300_trigger.c @@ -0,0 +1,127 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../iio.h" +#include "../sysfs.h" +#include "../trigger.h" +#include "adis16300.h" + +/** + * adis16300_data_rdy_trig_poll() the event handler for the data rdy trig + **/ +static int adis16300_data_rdy_trig_poll(struct iio_dev *dev_info, + int index, + s64 timestamp, + int no_test) +{ + struct adis16300_state *st = iio_dev_get_devdata(dev_info); + struct iio_trigger *trig = st->trig; + + trig->timestamp = timestamp; + iio_trigger_poll(trig); + + return IRQ_HANDLED; +} + +IIO_EVENT_SH(data_rdy_trig, &adis16300_data_rdy_trig_poll); + +static DEVICE_ATTR(name, S_IRUGO, iio_trigger_read_name, NULL); + +static struct attribute *adis16300_trigger_attrs[] = { + &dev_attr_name.attr, + NULL, +}; + +static const struct attribute_group adis16300_trigger_attr_group = { + .attrs = adis16300_trigger_attrs, +}; + +/** + * adis16300_data_rdy_trigger_set_state() set datardy interrupt state + **/ +static int adis16300_data_rdy_trigger_set_state(struct iio_trigger *trig, + bool state) +{ + struct adis16300_state *st = trig->private_data; + struct iio_dev *indio_dev = st->indio_dev; + int ret = 0; + + dev_dbg(&indio_dev->dev, "%s (%d)\n", __func__, state); + ret = adis16300_set_irq(&st->indio_dev->dev, state); + if (state == false) { + iio_remove_event_from_list(&iio_event_data_rdy_trig, + &indio_dev->interrupts[0] + ->ev_list); + /* possible quirk with handler currently worked around + by ensuring the work queue is empty */ + flush_scheduled_work(); + } else { + iio_add_event_to_list(&iio_event_data_rdy_trig, + &indio_dev->interrupts[0]->ev_list); + } + return ret; +} + +/** + * adis16300_trig_try_reen() try renabling irq for data rdy trigger + * @trig: the datardy trigger + **/ +static int adis16300_trig_try_reen(struct iio_trigger *trig) +{ + struct adis16300_state *st = trig->private_data; + enable_irq(st->us->irq); + /* irq reenabled so success! */ + return 0; +} + +int adis16300_probe_trigger(struct iio_dev *indio_dev) +{ + int ret; + struct adis16300_state *st = indio_dev->dev_data; + + st->trig = iio_allocate_trigger(); + st->trig->name = kmalloc(IIO_TRIGGER_NAME_LENGTH, GFP_KERNEL); + if (!st->trig->name) { + ret = -ENOMEM; + goto error_free_trig; + } + snprintf((char *)st->trig->name, + IIO_TRIGGER_NAME_LENGTH, + "adis16300-dev%d", indio_dev->id); + st->trig->dev.parent = &st->us->dev; + st->trig->owner = THIS_MODULE; + st->trig->private_data = st; + st->trig->set_trigger_state = &adis16300_data_rdy_trigger_set_state; + st->trig->try_reenable = &adis16300_trig_try_reen; + st->trig->control_attrs = &adis16300_trigger_attr_group; + ret = iio_trigger_register(st->trig); + + /* select default trigger */ + indio_dev->trig = st->trig; + if (ret) + goto error_free_trig_name; + + return 0; + +error_free_trig_name: + kfree(st->trig->name); +error_free_trig: + iio_free_trigger(st->trig); + + return ret; +} + +void adis16300_remove_trigger(struct iio_dev *indio_dev) +{ + struct adis16300_state *state = indio_dev->dev_data; + + iio_trigger_unregister(state->trig); + kfree(state->trig->name); + iio_free_trigger(state->trig); +} -- cgit v1.1 From a9d26f00b8b1b2a79e42cfa4ca20ec9a1c11c1d2 Mon Sep 17 00:00:00 2001 From: Barry Song Date: Tue, 4 May 2010 14:43:15 +0100 Subject: staging:iio:imu ADIS16400 and ADIS16405 driver Signed-off-by: Manuel Stahl Signed-off-by: Barry Song Acked-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/staging/iio/imu/Kconfig | 10 + drivers/staging/iio/imu/Makefile | 4 + drivers/staging/iio/imu/adis16400.h | 238 ++++++++ drivers/staging/iio/imu/adis16400_core.c | 849 ++++++++++++++++++++++++++++ drivers/staging/iio/imu/adis16400_ring.c | 245 ++++++++ drivers/staging/iio/imu/adis16400_trigger.c | 127 +++++ drivers/staging/iio/magnetometer/magnet.h | 31 + 7 files changed, 1504 insertions(+) create mode 100644 drivers/staging/iio/imu/adis16400.h create mode 100644 drivers/staging/iio/imu/adis16400_core.c create mode 100644 drivers/staging/iio/imu/adis16400_ring.c create mode 100644 drivers/staging/iio/imu/adis16400_trigger.c create mode 100644 drivers/staging/iio/magnetometer/magnet.h (limited to 'drivers/staging/iio') diff --git a/drivers/staging/iio/imu/Kconfig b/drivers/staging/iio/imu/Kconfig index 3fe4716..411804f 100644 --- a/drivers/staging/iio/imu/Kconfig +++ b/drivers/staging/iio/imu/Kconfig @@ -12,3 +12,13 @@ config ADIS16300 help Say yes here to build support for Analog Devices adis16300 four degrees of freedom inertial sensor. + +config ADIS16400 + tristate "Analog Devices ADIS16400/5 IMU SPI driver" + depends on SPI + select IIO_SW_RING + select IIO_RING_BUFFER + select IIO_TRIGGER + help + Say yes here to build support for Analog Devices adis16400/5 triaxial + inertial sensor with Magnetometer. \ No newline at end of file diff --git a/drivers/staging/iio/imu/Makefile b/drivers/staging/iio/imu/Makefile index 4cd5984..de454dd 100644 --- a/drivers/staging/iio/imu/Makefile +++ b/drivers/staging/iio/imu/Makefile @@ -4,3 +4,7 @@ adis16300-y := adis16300_core.o adis16300-$(CONFIG_IIO_RING_BUFFER) += adis16300_ring.o adis16300_trigger.o obj-$(CONFIG_ADIS16300) += adis16300.o + +adis16400-y := adis16400_core.o +adis16400-$(CONFIG_IIO_RING_BUFFER) += adis16400_ring.o adis16400_trigger.o +obj-$(CONFIG_ADIS16400) += adis16400.o \ No newline at end of file diff --git a/drivers/staging/iio/imu/adis16400.h b/drivers/staging/iio/imu/adis16400.h new file mode 100644 index 0000000..a8062f6 --- /dev/null +++ b/drivers/staging/iio/imu/adis16400.h @@ -0,0 +1,238 @@ +/* + * adis16400.h support Analog Devices ADIS16400 + * 3d 18g accelerometers, + * 3d gyroscopes, + * 3d 2.5gauss magnetometers via SPI + * + * Copyright (c) 2009 Manuel Stahl + * Copyright (c) 2007 Jonathan Cameron + * + * Loosely based upon lis3l02dq.h + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef SPI_ADIS16400_H_ +#define SPI_ADIS16400_H_ + +#define ADIS16400_STARTUP_DELAY 220 /* ms */ + +#define ADIS16400_READ_REG(a) a +#define ADIS16400_WRITE_REG(a) ((a) | 0x80) + +#define ADIS16400_FLASH_CNT 0x00 /* Flash memory write count */ +#define ADIS16400_SUPPLY_OUT 0x02 /* Power supply measurement */ +#define ADIS16400_XGYRO_OUT 0x04 /* X-axis gyroscope output */ +#define ADIS16400_YGYRO_OUT 0x06 /* Y-axis gyroscope output */ +#define ADIS16400_ZGYRO_OUT 0x08 /* Z-axis gyroscope output */ +#define ADIS16400_XACCL_OUT 0x0A /* X-axis accelerometer output */ +#define ADIS16400_YACCL_OUT 0x0C /* Y-axis accelerometer output */ +#define ADIS16400_ZACCL_OUT 0x0E /* Z-axis accelerometer output */ +#define ADIS16400_XMAGN_OUT 0x10 /* X-axis magnetometer measurement */ +#define ADIS16400_YMAGN_OUT 0x12 /* Y-axis magnetometer measurement */ +#define ADIS16400_ZMAGN_OUT 0x14 /* Z-axis magnetometer measurement */ +#define ADIS16400_TEMP_OUT 0x16 /* Temperature output */ +#define ADIS16400_AUX_ADC 0x18 /* Auxiliary ADC measurement */ + +/* Calibration parameters */ +#define ADIS16400_XGYRO_OFF 0x1A /* X-axis gyroscope bias offset factor */ +#define ADIS16400_YGYRO_OFF 0x1C /* Y-axis gyroscope bias offset factor */ +#define ADIS16400_ZGYRO_OFF 0x1E /* Z-axis gyroscope bias offset factor */ +#define ADIS16400_XACCL_OFF 0x20 /* X-axis acceleration bias offset factor */ +#define ADIS16400_YACCL_OFF 0x22 /* Y-axis acceleration bias offset factor */ +#define ADIS16400_ZACCL_OFF 0x24 /* Z-axis acceleration bias offset factor */ +#define ADIS16400_XMAGN_HIF 0x26 /* X-axis magnetometer, hard-iron factor */ +#define ADIS16400_YMAGN_HIF 0x28 /* Y-axis magnetometer, hard-iron factor */ +#define ADIS16400_ZMAGN_HIF 0x2A /* Z-axis magnetometer, hard-iron factor */ +#define ADIS16400_XMAGN_SIF 0x2C /* X-axis magnetometer, soft-iron factor */ +#define ADIS16400_YMAGN_SIF 0x2E /* Y-axis magnetometer, soft-iron factor */ +#define ADIS16400_ZMAGN_SIF 0x30 /* Z-axis magnetometer, soft-iron factor */ + +#define ADIS16400_GPIO_CTRL 0x32 /* Auxiliary digital input/output control */ +#define ADIS16400_MSC_CTRL 0x34 /* Miscellaneous control */ +#define ADIS16400_SMPL_PRD 0x36 /* Internal sample period (rate) control */ +#define ADIS16400_SENS_AVG 0x38 /* Dynamic range and digital filter control */ +#define ADIS16400_SLP_CNT 0x3A /* Sleep mode control */ +#define ADIS16400_DIAG_STAT 0x3C /* System status */ + +/* Alarm functions */ +#define ADIS16400_GLOB_CMD 0x3E /* System command */ +#define ADIS16400_ALM_MAG1 0x40 /* Alarm 1 amplitude threshold */ +#define ADIS16400_ALM_MAG2 0x42 /* Alarm 2 amplitude threshold */ +#define ADIS16400_ALM_SMPL1 0x44 /* Alarm 1 sample size */ +#define ADIS16400_ALM_SMPL2 0x46 /* Alarm 2 sample size */ +#define ADIS16400_ALM_CTRL 0x48 /* Alarm control */ +#define ADIS16400_AUX_DAC 0x4A /* Auxiliary DAC data */ + +#define ADIS16400_PRODUCT_ID 0x56 /* Product identifier */ +#define ADIS16400_PRODUCT_ID_DEFAULT 0x4015 /* Datasheet says 0x4105, I get 0x4015 */ + +#define ADIS16400_ERROR_ACTIVE (1<<14) +#define ADIS16400_NEW_DATA (1<<14) + +/* MSC_CTRL */ +#define ADIS16400_MSC_CTRL_MEM_TEST (1<<11) +#define ADIS16400_MSC_CTRL_INT_SELF_TEST (1<<10) +#define ADIS16400_MSC_CTRL_NEG_SELF_TEST (1<<9) +#define ADIS16400_MSC_CTRL_POS_SELF_TEST (1<<8) +#define ADIS16400_MSC_CTRL_GYRO_BIAS (1<<7) +#define ADIS16400_MSC_CTRL_ACCL_ALIGN (1<<6) +#define ADIS16400_MSC_CTRL_DATA_RDY_EN (1<<2) +#define ADIS16400_MSC_CTRL_DATA_RDY_POL_HIGH (1<<1) +#define ADIS16400_MSC_CTRL_DATA_RDY_DIO2 (1<<0) + +/* SMPL_PRD */ +#define ADIS16400_SMPL_PRD_TIME_BASE (1<<7) +#define ADIS16400_SMPL_PRD_DIV_MASK 0x7F + +/* DIAG_STAT */ +#define ADIS16400_DIAG_STAT_ZACCL_FAIL (1<<15) +#define ADIS16400_DIAG_STAT_YACCL_FAIL (1<<14) +#define ADIS16400_DIAG_STAT_XACCL_FAIL (1<<13) +#define ADIS16400_DIAG_STAT_XGYRO_FAIL (1<<12) +#define ADIS16400_DIAG_STAT_YGYRO_FAIL (1<<11) +#define ADIS16400_DIAG_STAT_ZGYRO_FAIL (1<<10) +#define ADIS16400_DIAG_STAT_ALARM2 (1<<9) +#define ADIS16400_DIAG_STAT_ALARM1 (1<<8) +#define ADIS16400_DIAG_STAT_FLASH_CHK (1<<6) +#define ADIS16400_DIAG_STAT_SELF_TEST (1<<5) +#define ADIS16400_DIAG_STAT_OVERFLOW (1<<4) +#define ADIS16400_DIAG_STAT_SPI_FAIL (1<<3) +#define ADIS16400_DIAG_STAT_FLASH_UPT (1<<2) +#define ADIS16400_DIAG_STAT_POWER_HIGH (1<<1) +#define ADIS16400_DIAG_STAT_POWER_LOW (1<<0) + +/* GLOB_CMD */ +#define ADIS16400_GLOB_CMD_SW_RESET (1<<7) +#define ADIS16400_GLOB_CMD_P_AUTO_NULL (1<<4) +#define ADIS16400_GLOB_CMD_FLASH_UPD (1<<3) +#define ADIS16400_GLOB_CMD_DAC_LATCH (1<<2) +#define ADIS16400_GLOB_CMD_FAC_CALIB (1<<1) +#define ADIS16400_GLOB_CMD_AUTO_NULL (1<<0) + +/* SLP_CNT */ +#define ADIS16400_SLP_CNT_POWER_OFF (1<<8) + +#define ADIS16400_MAX_TX 24 +#define ADIS16400_MAX_RX 24 + +#define ADIS16400_SPI_SLOW (u32)(300 * 1000) +#define ADIS16400_SPI_BURST (u32)(1000 * 1000) +#define ADIS16400_SPI_FAST (u32)(2000 * 1000) + +/** + * struct adis16400_state - device instance specific data + * @us: actual spi_device + * @work_trigger_to_ring: bh for triggered event handling + * @work_cont_thresh: CLEAN + * @inter: used to check if new interrupt has been triggered + * @last_timestamp: passing timestamp from th to bh of interrupt handler + * @indio_dev: industrial I/O device structure + * @trig: data ready trigger registered with iio + * @tx: transmit buffer + * @rx: recieve buffer + * @buf_lock: mutex to protect tx and rx + **/ +struct adis16400_state { + struct spi_device *us; + struct work_struct work_trigger_to_ring; + struct iio_work_cont work_cont_thresh; + s64 last_timestamp; + struct iio_dev *indio_dev; + struct iio_trigger *trig; + u8 *tx; + u8 *rx; + struct mutex buf_lock; +}; + +int adis16400_spi_write_reg_8(struct device *dev, + u8 reg_address, + u8 val); + +int adis16400_spi_read_burst(struct device *dev, u8 *rx); + +int adis16400_spi_read_sequence(struct device *dev, + u8 *tx, u8 *rx, int num); + +int adis16400_set_irq(struct device *dev, bool enable); + +int adis16400_reset(struct device *dev); + +int adis16400_stop_device(struct device *dev); + +int adis16400_check_status(struct device *dev); + +#ifdef CONFIG_IIO_RING_BUFFER +/* At the moment triggers are only used for ring buffer + * filling. This may change! + */ + +enum adis16400_scan { + ADIS16400_SCAN_SUPPLY, + ADIS16400_SCAN_GYRO_X, + ADIS16400_SCAN_GYRO_Y, + ADIS16400_SCAN_GYRO_Z, + ADIS16400_SCAN_ACC_X, + ADIS16400_SCAN_ACC_Y, + ADIS16400_SCAN_ACC_Z, + ADIS16400_SCAN_MAGN_X, + ADIS16400_SCAN_MAGN_Y, + ADIS16400_SCAN_MAGN_Z, + ADIS16400_SCAN_TEMP, + ADIS16400_SCAN_ADC_0 +}; + +void adis16400_remove_trigger(struct iio_dev *indio_dev); +int adis16400_probe_trigger(struct iio_dev *indio_dev); + +ssize_t adis16400_read_data_from_ring(struct device *dev, + struct device_attribute *attr, + char *buf); + + +int adis16400_configure_ring(struct iio_dev *indio_dev); +void adis16400_unconfigure_ring(struct iio_dev *indio_dev); + +int adis16400_initialize_ring(struct iio_ring_buffer *ring); +void adis16400_uninitialize_ring(struct iio_ring_buffer *ring); +#else /* CONFIG_IIO_RING_BUFFER */ + +static inline void adis16400_remove_trigger(struct iio_dev *indio_dev) +{ +} + +static inline int adis16400_probe_trigger(struct iio_dev *indio_dev) +{ + return 0; +} + +static inline ssize_t +adis16400_read_data_from_ring(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return 0; +} + +static int adis16400_configure_ring(struct iio_dev *indio_dev) +{ + return 0; +} + +static inline void adis16400_unconfigure_ring(struct iio_dev *indio_dev) +{ +} + +static inline int adis16400_initialize_ring(struct iio_ring_buffer *ring) +{ + return 0; +} + +static inline void adis16400_uninitialize_ring(struct iio_ring_buffer *ring) +{ +} + +#endif /* CONFIG_IIO_RING_BUFFER */ +#endif /* SPI_ADIS16400_H_ */ diff --git a/drivers/staging/iio/imu/adis16400_core.c b/drivers/staging/iio/imu/adis16400_core.c new file mode 100644 index 0000000..27e4a73 --- /dev/null +++ b/drivers/staging/iio/imu/adis16400_core.c @@ -0,0 +1,849 @@ +/* + * adis16400.c support Analog Devices ADIS16400/5 + * 3d 2g Linear Accelerometers, + * 3d Gyroscopes, + * 3d Magnetometers via SPI + * + * Copyright (c) 2009 Manuel Stahl + * Copyright (c) 2007 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 +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "../iio.h" +#include "../sysfs.h" +#include "../accel/accel.h" +#include "../adc/adc.h" +#include "../gyro/gyro.h" +#include "../magnetometer/magnet.h" + +#include "adis16400.h" + +#define DRIVER_NAME "adis16400" + +/* At the moment the spi framework doesn't allow global setting of cs_change. + * It's in the likely to be added comment at the top of spi.h. + * This means that use cannot be made of spi_write etc. + */ + +/** + * adis16400_spi_write_reg_8() - write single byte to a register + * @dev: device associated with child of actual device (iio_dev or iio_trig) + * @reg_address: the address of the register to be written + * @val: the value to write + **/ +int adis16400_spi_write_reg_8(struct device *dev, + u8 reg_address, + u8 val) +{ + int ret; + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct adis16400_state *st = iio_dev_get_devdata(indio_dev); + + mutex_lock(&st->buf_lock); + st->tx[0] = ADIS16400_WRITE_REG(reg_address); + st->tx[1] = val; + + ret = spi_write(st->us, st->tx, 2); + mutex_unlock(&st->buf_lock); + + return ret; +} + +/** + * adis16400_spi_write_reg_16() - write 2 bytes to a pair of registers + * @dev: device associated with child of actual device (iio_dev or iio_trig) + * @reg_address: the address of the lower of the two registers. Second register + * is assumed to have address one greater. + * @val: value to be written + **/ +static int adis16400_spi_write_reg_16(struct device *dev, + u8 lower_reg_address, + u16 value) +{ + int ret; + struct spi_message msg; + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct adis16400_state *st = iio_dev_get_devdata(indio_dev); + struct spi_transfer xfers[] = { + { + .tx_buf = st->tx, + .bits_per_word = 8, + .len = 2, + .cs_change = 1, + }, { + .tx_buf = st->tx + 2, + .bits_per_word = 8, + .len = 2, + .cs_change = 1, + }, + }; + + mutex_lock(&st->buf_lock); + st->tx[0] = ADIS16400_WRITE_REG(lower_reg_address); + st->tx[1] = value & 0xFF; + st->tx[2] = ADIS16400_WRITE_REG(lower_reg_address + 1); + st->tx[3] = (value >> 8) & 0xFF; + + spi_message_init(&msg); + spi_message_add_tail(&xfers[0], &msg); + spi_message_add_tail(&xfers[1], &msg); + ret = spi_sync(st->us, &msg); + mutex_unlock(&st->buf_lock); + + return ret; +} + +/** + * adis16400_spi_read_reg_16() - read 2 bytes from a 16-bit register + * @dev: device associated with child of actual device (iio_dev or iio_trig) + * @reg_address: the address of the lower of the two registers. Second register + * is assumed to have address one greater. + * @val: somewhere to pass back the value read + **/ +static int adis16400_spi_read_reg_16(struct device *dev, + u8 lower_reg_address, + u16 *val) +{ + struct spi_message msg; + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct adis16400_state *st = iio_dev_get_devdata(indio_dev); + int ret; + struct spi_transfer xfers[] = { + { + .tx_buf = st->tx, + .bits_per_word = 8, + .len = 2, + .cs_change = 1, + }, { + .rx_buf = st->rx, + .bits_per_word = 8, + .len = 2, + .cs_change = 1, + }, + }; + + mutex_lock(&st->buf_lock); + st->tx[0] = ADIS16400_READ_REG(lower_reg_address); + st->tx[1] = 0; + st->tx[2] = 0; + st->tx[3] = 0; + + spi_message_init(&msg); + spi_message_add_tail(&xfers[0], &msg); + spi_message_add_tail(&xfers[1], &msg); + ret = spi_sync(st->us, &msg); + if (ret) { + dev_err(&st->us->dev, + "problem when reading 16 bit register 0x%02X", + lower_reg_address); + goto error_ret; + } + *val = (st->rx[0] << 8) | st->rx[1]; + +error_ret: + mutex_unlock(&st->buf_lock); + return ret; +} + +/** + * adis16400_spi_read_burst() - read all data registers + * @dev: device associated with child of actual device (iio_dev or iio_trig) + * @rx: somewhere to pass back the value read (min size is 24 bytes) + **/ +int adis16400_spi_read_burst(struct device *dev, u8 *rx) +{ + struct spi_message msg; + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct adis16400_state *st = iio_dev_get_devdata(indio_dev); + u32 old_speed_hz = st->us->max_speed_hz; + int ret; + + struct spi_transfer xfers[] = { + { + .tx_buf = st->tx, + .bits_per_word = 8, + .len = 2, + .cs_change = 0, + }, { + .rx_buf = rx, + .bits_per_word = 8, + .len = 24, + .cs_change = 1, + }, + }; + + mutex_lock(&st->buf_lock); + st->tx[0] = ADIS16400_READ_REG(ADIS16400_GLOB_CMD); + st->tx[1] = 0; + + spi_message_init(&msg); + spi_message_add_tail(&xfers[0], &msg); + spi_message_add_tail(&xfers[1], &msg); + + st->us->max_speed_hz = min(ADIS16400_SPI_BURST, old_speed_hz); + spi_setup(st->us); + + ret = spi_sync(st->us, &msg); + if (ret) + dev_err(&st->us->dev, "problem when burst reading"); + + st->us->max_speed_hz = old_speed_hz; + spi_setup(st->us); + mutex_unlock(&st->buf_lock); + return ret; +} + +/** + * adis16400_spi_read_sequence() - read a sequence of 16-bit registers + * @dev: device associated with child of actual device (iio_dev or iio_trig) + * @tx: register addresses in bytes 0,2,4,6... (min size is 2*num bytes) + * @rx: somewhere to pass back the value read (min size is 2*num bytes) + **/ +int adis16400_spi_read_sequence(struct device *dev, + u8 *tx, u8 *rx, int num) +{ + struct spi_message msg; + struct spi_transfer *xfers; + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct adis16400_state *st = iio_dev_get_devdata(indio_dev); + int ret, i; + + xfers = kzalloc(num + 1, GFP_KERNEL); + if (xfers == NULL) { + dev_err(&st->us->dev, "memory alloc failed"); + ret = -ENOMEM; + goto error_ret; + } + + /* tx: |add1|addr2|addr3|...|addrN |zero| + * rx: |zero|res1 |res2 |...|resN-1|resN| */ + spi_message_init(&msg); + for (i = 0; i < num + 1; i++) { + if (i > 0) + xfers[i].rx_buf = st->rx + 2*(i - 1); + if (i < num) + xfers[i].tx_buf = st->tx + 2*i; + xfers[i].bits_per_word = 8; + xfers[i].len = 2; + xfers[i].cs_change = 1; + spi_message_add_tail(&xfers[i], &msg); + } + + mutex_lock(&st->buf_lock); + + ret = spi_sync(st->us, &msg); + if (ret) + dev_err(&st->us->dev, "problem when reading sequence"); + + mutex_unlock(&st->buf_lock); + kfree(xfers); + +error_ret: + return ret; +} + +static ssize_t adis16400_spi_read_signed(struct device *dev, + struct device_attribute *attr, + char *buf, + unsigned bits) +{ + int ret; + s16 val = 0; + unsigned shift = 16 - bits; + struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); + + ret = adis16400_spi_read_reg_16(dev, this_attr->address, (u16 *)&val); + if (ret) + return ret; + + if (val & ADIS16400_ERROR_ACTIVE) + adis16400_check_status(dev); + val = ((s16)(val << shift) >> shift); + return sprintf(buf, "%d\n", val); +} + +static ssize_t adis16400_read_12bit_unsigned(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int ret; + u16 val = 0; + struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); + + ret = adis16400_spi_read_reg_16(dev, this_attr->address, &val); + if (ret) + return ret; + + if (val & ADIS16400_ERROR_ACTIVE) + adis16400_check_status(dev); + + return sprintf(buf, "%u\n", val & 0x0FFF); +} + +static ssize_t adis16400_read_14bit_signed(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + ssize_t ret; + + /* Take the iio_dev status lock */ + mutex_lock(&indio_dev->mlock); + ret = adis16400_spi_read_signed(dev, attr, buf, 14); + mutex_unlock(&indio_dev->mlock); + + return ret; +} + +static ssize_t adis16400_read_12bit_signed(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + ssize_t ret; + + /* Take the iio_dev status lock */ + mutex_lock(&indio_dev->mlock); + ret = adis16400_spi_read_signed(dev, attr, buf, 12); + mutex_unlock(&indio_dev->mlock); + + return ret; +} + + +static ssize_t adis16400_write_16bit(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t len) +{ + struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); + int ret; + long val; + + ret = strict_strtol(buf, 10, &val); + if (ret) + goto error_ret; + ret = adis16400_spi_write_reg_16(dev, this_attr->address, val); + +error_ret: + return ret ? ret : len; +} + +static ssize_t adis16400_read_frequency(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int ret, len = 0; + u16 t; + int sps; + ret = adis16400_spi_read_reg_16(dev, + ADIS16400_SMPL_PRD, + &t); + if (ret) + return ret; + sps = (t & ADIS16400_SMPL_PRD_TIME_BASE) ? 53 : 1638; + sps /= (t & ADIS16400_SMPL_PRD_DIV_MASK) + 1; + len = sprintf(buf, "%d SPS\n", sps); + return len; +} + +static ssize_t adis16400_write_frequency(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t len) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct adis16400_state *st = iio_dev_get_devdata(indio_dev); + long val; + int ret; + u8 t; + + ret = strict_strtol(buf, 10, &val); + if (ret) + return ret; + + mutex_lock(&indio_dev->mlock); + + t = (1638 / val); + if (t > 0) + t--; + t &= ADIS16400_SMPL_PRD_DIV_MASK; + if ((t & ADIS16400_SMPL_PRD_DIV_MASK) >= 0x0A) + st->us->max_speed_hz = ADIS16400_SPI_SLOW; + else + st->us->max_speed_hz = ADIS16400_SPI_FAST; + + ret = adis16400_spi_write_reg_8(dev, + ADIS16400_SMPL_PRD, + t); + + mutex_unlock(&indio_dev->mlock); + + return ret ? ret : len; +} + +static ssize_t adis16400_write_reset(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + if (len < 1) + return -1; + switch (buf[0]) { + case '1': + case 'y': + case 'Y': + return adis16400_reset(dev); + } + return -1; +} + + + +int adis16400_set_irq(struct device *dev, bool enable) +{ + int ret; + u16 msc; + ret = adis16400_spi_read_reg_16(dev, ADIS16400_MSC_CTRL, &msc); + if (ret) + goto error_ret; + + msc |= ADIS16400_MSC_CTRL_DATA_RDY_POL_HIGH; + if (enable) + msc |= ADIS16400_MSC_CTRL_DATA_RDY_EN; + else + msc &= ~ADIS16400_MSC_CTRL_DATA_RDY_EN; + + ret = adis16400_spi_write_reg_16(dev, ADIS16400_MSC_CTRL, msc); + if (ret) + goto error_ret; + +error_ret: + return ret; +} + +int adis16400_reset(struct device *dev) +{ + int ret; + ret = adis16400_spi_write_reg_8(dev, + ADIS16400_GLOB_CMD, + ADIS16400_GLOB_CMD_SW_RESET); + if (ret) + dev_err(dev, "problem resetting device"); + + return ret; +} + +/* Power down the device */ +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; +} + +int adis16400_self_test(struct device *dev) +{ + int ret; + ret = adis16400_spi_write_reg_16(dev, + ADIS16400_MSC_CTRL, + ADIS16400_MSC_CTRL_MEM_TEST); + if (ret) { + dev_err(dev, "problem starting self test"); + goto err_ret; + } + + adis16400_check_status(dev); + +err_ret: + return ret; +} + +int adis16400_check_status(struct device *dev) +{ + u16 status; + int ret; + + ret = adis16400_spi_read_reg_16(dev, ADIS16400_DIAG_STAT, &status); + + if (ret < 0) { + dev_err(dev, "Reading status failed\n"); + goto error_ret; + } + ret = status; + if (status & ADIS16400_DIAG_STAT_ZACCL_FAIL) + dev_err(dev, "Z-axis accelerometer self-test failure\n"); + if (status & ADIS16400_DIAG_STAT_YACCL_FAIL) + dev_err(dev, "Y-axis accelerometer self-test failure\n"); + if (status & ADIS16400_DIAG_STAT_XACCL_FAIL) + dev_err(dev, "X-axis accelerometer self-test failure\n"); + if (status & ADIS16400_DIAG_STAT_XGYRO_FAIL) + dev_err(dev, "X-axis gyroscope self-test failure\n"); + if (status & ADIS16400_DIAG_STAT_YGYRO_FAIL) + dev_err(dev, "Y-axis gyroscope self-test failure\n"); + if (status & ADIS16400_DIAG_STAT_ZGYRO_FAIL) + dev_err(dev, "Z-axis gyroscope self-test failure\n"); + if (status & ADIS16400_DIAG_STAT_ALARM2) + dev_err(dev, "Alarm 2 active\n"); + if (status & ADIS16400_DIAG_STAT_ALARM1) + dev_err(dev, "Alarm 1 active\n"); + if (status & ADIS16400_DIAG_STAT_FLASH_CHK) + dev_err(dev, "Flash checksum error\n"); + if (status & ADIS16400_DIAG_STAT_SELF_TEST) + dev_err(dev, "Self test error\n"); + if (status & ADIS16400_DIAG_STAT_OVERFLOW) + dev_err(dev, "Sensor overrange\n"); + if (status & ADIS16400_DIAG_STAT_SPI_FAIL) + dev_err(dev, "SPI failure\n"); + if (status & ADIS16400_DIAG_STAT_FLASH_UPT) + dev_err(dev, "Flash update failed\n"); + if (status & ADIS16400_DIAG_STAT_POWER_HIGH) + dev_err(dev, "Power supply above 5.25V\n"); + if (status & ADIS16400_DIAG_STAT_POWER_LOW) + dev_err(dev, "Power supply below 4.75V\n"); + +error_ret: + return ret; +} + +static int adis16400_initial_setup(struct adis16400_state *st) +{ + int ret; + u16 prod_id, smp_prd; + struct device *dev = &st->indio_dev->dev; + + /* use low spi speed for init */ + st->us->max_speed_hz = ADIS16400_SPI_SLOW; + st->us->mode = SPI_MODE_3; + spi_setup(st->us); + + /* Disable IRQ */ + ret = adis16400_set_irq(dev, false); + if (ret) { + dev_err(dev, "disable irq failed"); + goto err_ret; + } + + /* Do self test */ + + /* Read status register to check the result */ + ret = adis16400_check_status(dev); + if (ret) { + adis16400_reset(dev); + dev_err(dev, "device not playing ball -> reset"); + msleep(ADIS16400_STARTUP_DELAY); + ret = adis16400_check_status(dev); + if (ret) { + dev_err(dev, "giving up"); + goto err_ret; + } + } + + ret = adis16400_spi_read_reg_16(dev, ADIS16400_PRODUCT_ID, &prod_id); + if (ret) + goto err_ret; + + if (prod_id != ADIS16400_PRODUCT_ID_DEFAULT) + dev_warn(dev, "unknown product id"); + + printk(KERN_INFO DRIVER_NAME ": prod_id 0x%04x at CS%d (irq %d)\n", + prod_id, st->us->chip_select, st->us->irq); + + /* use high spi speed if possible */ + ret = adis16400_spi_read_reg_16(dev, ADIS16400_SMPL_PRD, &smp_prd); + if (!ret && (smp_prd & ADIS16400_SMPL_PRD_DIV_MASK) < 0x0A) { + st->us->max_speed_hz = ADIS16400_SPI_SLOW; + spi_setup(st->us); + } + + +err_ret: + + return ret; +} + +static IIO_DEV_ATTR_ACCEL_X_OFFSET(S_IWUSR | S_IRUGO, + adis16400_read_12bit_signed, + adis16400_write_16bit, + ADIS16400_XACCL_OFF); + +static IIO_DEV_ATTR_ACCEL_Y_OFFSET(S_IWUSR | S_IRUGO, + adis16400_read_12bit_signed, + adis16400_write_16bit, + ADIS16400_YACCL_OFF); + +static IIO_DEV_ATTR_ACCEL_Z_OFFSET(S_IWUSR | S_IRUGO, + adis16400_read_12bit_signed, + adis16400_write_16bit, + ADIS16400_ZACCL_OFF); + +static IIO_DEV_ATTR_IN_NAMED_RAW(supply, adis16400_read_14bit_signed, + ADIS16400_SUPPLY_OUT); +static IIO_CONST_ATTR(in_supply_scale, "0.002418"); + +static IIO_DEV_ATTR_GYRO_X(adis16400_read_14bit_signed, + ADIS16400_XGYRO_OUT); +static IIO_DEV_ATTR_GYRO_Y(adis16400_read_14bit_signed, + ADIS16400_YGYRO_OUT); +static IIO_DEV_ATTR_GYRO_Z(adis16400_read_14bit_signed, + ADIS16400_ZGYRO_OUT); +static IIO_CONST_ATTR(gyro_scale, "0.05 deg/s"); + +static IIO_DEV_ATTR_ACCEL_X(adis16400_read_14bit_signed, + ADIS16400_XACCL_OUT); +static IIO_DEV_ATTR_ACCEL_Y(adis16400_read_14bit_signed, + ADIS16400_YACCL_OUT); +static IIO_DEV_ATTR_ACCEL_Z(adis16400_read_14bit_signed, + ADIS16400_ZACCL_OUT); +static IIO_CONST_ATTR(accel_scale, "0.00333 g"); + +static IIO_DEV_ATTR_MAGN_X(adis16400_read_14bit_signed, + ADIS16400_XMAGN_OUT); +static IIO_DEV_ATTR_MAGN_Y(adis16400_read_14bit_signed, + ADIS16400_YMAGN_OUT); +static IIO_DEV_ATTR_MAGN_Z(adis16400_read_14bit_signed, + ADIS16400_ZMAGN_OUT); +static IIO_CONST_ATTR(magn_scale, "0.0005 Gs"); + + +static IIO_DEV_ATTR_TEMP(adis16400_read_12bit_signed); +static IIO_CONST_ATTR(temp_offset, "198.16 K"); +static IIO_CONST_ATTR(temp_scale, "0.14 K"); + +static IIO_DEV_ATTR_IN_RAW(0, adis16400_read_12bit_unsigned, + ADIS16400_AUX_ADC); +static IIO_CONST_ATTR(in0_scale, "0.000806"); + +static IIO_DEV_ATTR_SAMP_FREQ(S_IWUSR | S_IRUGO, + adis16400_read_frequency, + adis16400_write_frequency); + +static IIO_DEVICE_ATTR(reset, S_IWUSR, NULL, adis16400_write_reset, 0); + +static IIO_CONST_ATTR_AVAIL_SAMP_FREQ("409 546 819 1638"); + +static IIO_CONST_ATTR(name, "adis16400"); + +static struct attribute *adis16400_event_attributes[] = { + NULL +}; + +static struct attribute_group adis16400_event_attribute_group = { + .attrs = adis16400_event_attributes, +}; + +static struct attribute *adis16400_attributes[] = { + &iio_dev_attr_accel_x_offset.dev_attr.attr, + &iio_dev_attr_accel_y_offset.dev_attr.attr, + &iio_dev_attr_accel_z_offset.dev_attr.attr, + &iio_dev_attr_in_supply_raw.dev_attr.attr, + &iio_const_attr_in_supply_scale.dev_attr.attr, + &iio_dev_attr_gyro_x.dev_attr.attr, + &iio_dev_attr_gyro_y.dev_attr.attr, + &iio_dev_attr_gyro_z.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.dev_attr.attr, + &iio_dev_attr_magn_y.dev_attr.attr, + &iio_dev_attr_magn_z.dev_attr.attr, + &iio_const_attr_magn_scale.dev_attr.attr, + &iio_dev_attr_temp.dev_attr.attr, + &iio_const_attr_temp_offset.dev_attr.attr, + &iio_const_attr_temp_scale.dev_attr.attr, + &iio_dev_attr_in0_raw.dev_attr.attr, + &iio_const_attr_in0_scale.dev_attr.attr, + &iio_dev_attr_sampling_frequency.dev_attr.attr, + &iio_const_attr_available_sampling_frequency.dev_attr.attr, + &iio_dev_attr_reset.dev_attr.attr, + &iio_const_attr_name.dev_attr.attr, + NULL +}; + +static const struct attribute_group adis16400_attribute_group = { + .attrs = adis16400_attributes, +}; + +static int __devinit adis16400_probe(struct spi_device *spi) +{ + int ret, regdone = 0; + struct adis16400_state *st = kzalloc(sizeof *st, GFP_KERNEL); + if (!st) { + ret = -ENOMEM; + goto error_ret; + } + /* this is only used for removal purposes */ + spi_set_drvdata(spi, st); + + /* Allocate the comms buffers */ + st->rx = kzalloc(sizeof(*st->rx)*ADIS16400_MAX_RX, GFP_KERNEL); + if (st->rx == NULL) { + ret = -ENOMEM; + goto error_free_st; + } + st->tx = kzalloc(sizeof(*st->tx)*ADIS16400_MAX_TX, GFP_KERNEL); + if (st->tx == NULL) { + ret = -ENOMEM; + goto error_free_rx; + } + st->us = spi; + mutex_init(&st->buf_lock); + /* setup the industrialio driver allocated elements */ + st->indio_dev = iio_allocate_device(); + if (st->indio_dev == NULL) { + ret = -ENOMEM; + goto error_free_tx; + } + + st->indio_dev->dev.parent = &spi->dev; + st->indio_dev->num_interrupt_lines = 1; + st->indio_dev->event_attrs = &adis16400_event_attribute_group; + st->indio_dev->attrs = &adis16400_attribute_group; + st->indio_dev->dev_data = (void *)(st); + st->indio_dev->driver_module = THIS_MODULE; + st->indio_dev->modes = INDIO_DIRECT_MODE; + + ret = adis16400_configure_ring(st->indio_dev); + if (ret) + goto error_free_dev; + + ret = iio_device_register(st->indio_dev); + if (ret) + goto error_unreg_ring_funcs; + regdone = 1; + + ret = adis16400_initialize_ring(st->indio_dev->ring); + if (ret) { + printk(KERN_ERR "failed to initialize the ring\n"); + goto error_unreg_ring_funcs; + } + + if (spi->irq && gpio_is_valid(irq_to_gpio(spi->irq)) > 0) { +#if 0 /* fixme: here we should support */ + iio_init_work_cont(&st->work_cont_thresh, + NULL, + adis16400_thresh_handler_bh_no_check, + 0, + 0, + st); +#endif + ret = iio_register_interrupt_line(spi->irq, + st->indio_dev, + 0, + IRQF_TRIGGER_RISING, + "adis16400"); + if (ret) + goto error_uninitialize_ring; + + ret = adis16400_probe_trigger(st->indio_dev); + if (ret) + goto error_unregister_line; + } + + /* Get the device into a sane initial state */ + ret = adis16400_initial_setup(st); + if (ret) + goto error_remove_trigger; + return 0; + +error_remove_trigger: + if (st->indio_dev->modes & INDIO_RING_TRIGGERED) + adis16400_remove_trigger(st->indio_dev); +error_unregister_line: + if (st->indio_dev->modes & INDIO_RING_TRIGGERED) + iio_unregister_interrupt_line(st->indio_dev, 0); +error_uninitialize_ring: + adis16400_uninitialize_ring(st->indio_dev->ring); +error_unreg_ring_funcs: + adis16400_unconfigure_ring(st->indio_dev); +error_free_dev: + if (regdone) + iio_device_unregister(st->indio_dev); + else + iio_free_device(st->indio_dev); +error_free_tx: + kfree(st->tx); +error_free_rx: + kfree(st->rx); +error_free_st: + kfree(st); +error_ret: + return ret; +} + +/* fixme, confirm ordering in this function */ +static int adis16400_remove(struct spi_device *spi) +{ + int ret; + struct adis16400_state *st = spi_get_drvdata(spi); + struct iio_dev *indio_dev = st->indio_dev; + + ret = adis16400_stop_device(&(indio_dev->dev)); + if (ret) + goto err_ret; + + flush_scheduled_work(); + + adis16400_remove_trigger(indio_dev); + if (spi->irq && gpio_is_valid(irq_to_gpio(spi->irq)) > 0) + iio_unregister_interrupt_line(indio_dev, 0); + + adis16400_uninitialize_ring(indio_dev->ring); + adis16400_unconfigure_ring(indio_dev); + iio_device_unregister(indio_dev); + kfree(st->tx); + kfree(st->rx); + kfree(st); + + return 0; + +err_ret: + return ret; +} + +static struct spi_driver adis16400_driver = { + .driver = { + .name = "adis16400", + .owner = THIS_MODULE, + }, + .probe = adis16400_probe, + .remove = __devexit_p(adis16400_remove), +}; + +static __init int adis16400_init(void) +{ + return spi_register_driver(&adis16400_driver); +} +module_init(adis16400_init); + +static __exit void adis16400_exit(void) +{ + spi_unregister_driver(&adis16400_driver); +} +module_exit(adis16400_exit); + +MODULE_AUTHOR("Manuel Stahl "); +MODULE_DESCRIPTION("Analog Devices ADIS16400/5 IMU SPI driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/iio/imu/adis16400_ring.c b/drivers/staging/iio/imu/adis16400_ring.c new file mode 100644 index 0000000..5529b32 --- /dev/null +++ b/drivers/staging/iio/imu/adis16400_ring.c @@ -0,0 +1,245 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../iio.h" +#include "../sysfs.h" +#include "../ring_sw.h" +#include "../accel/accel.h" +#include "../trigger.h" +#include "adis16400.h" + +/** + * combine_8_to_16() utility function to munge to u8s into u16 + **/ +static inline u16 combine_8_to_16(u8 lower, u8 upper) +{ + u16 _lower = lower; + u16 _upper = upper; + return _lower | (_upper << 8); +} + +static IIO_SCAN_EL_C(supply, ADIS16400_SCAN_SUPPLY, IIO_SIGNED(14), + ADIS16400_SUPPLY_OUT, NULL); + +static IIO_SCAN_EL_C(gyro_x, ADIS16400_SCAN_GYRO_X, IIO_SIGNED(14), + ADIS16400_XGYRO_OUT, NULL); +static IIO_SCAN_EL_C(gyro_y, ADIS16400_SCAN_GYRO_Y, IIO_SIGNED(14), + ADIS16400_YGYRO_OUT, NULL); +static IIO_SCAN_EL_C(gyro_z, ADIS16400_SCAN_GYRO_Z, IIO_SIGNED(14), + ADIS16400_ZGYRO_OUT, NULL); + +static IIO_SCAN_EL_C(accel_x, ADIS16400_SCAN_ACC_X, IIO_SIGNED(14), + ADIS16400_XACCL_OUT, NULL); +static IIO_SCAN_EL_C(accel_y, ADIS16400_SCAN_ACC_Y, IIO_SIGNED(14), + ADIS16400_YACCL_OUT, NULL); +static IIO_SCAN_EL_C(accel_z, ADIS16400_SCAN_ACC_Z, IIO_SIGNED(14), + ADIS16400_ZACCL_OUT, NULL); + +static IIO_SCAN_EL_C(magn_x, ADIS16400_SCAN_MAGN_X, IIO_SIGNED(14), + ADIS16400_XMAGN_OUT, NULL); +static IIO_SCAN_EL_C(magn_y, ADIS16400_SCAN_MAGN_Y, IIO_SIGNED(14), + ADIS16400_YMAGN_OUT, NULL); +static IIO_SCAN_EL_C(magn_z, ADIS16400_SCAN_MAGN_Z, IIO_SIGNED(14), + ADIS16400_ZMAGN_OUT, NULL); + +static IIO_SCAN_EL_C(temp, ADIS16400_SCAN_TEMP, IIO_SIGNED(12), + ADIS16400_TEMP_OUT, NULL); +static IIO_SCAN_EL_C(adc_0, ADIS16400_SCAN_ADC_0, IIO_SIGNED(12), + ADIS16400_AUX_ADC, NULL); + +static IIO_SCAN_EL_TIMESTAMP(12); + +static struct attribute *adis16400_scan_el_attrs[] = { + &iio_scan_el_supply.dev_attr.attr, + &iio_scan_el_gyro_x.dev_attr.attr, + &iio_scan_el_gyro_y.dev_attr.attr, + &iio_scan_el_gyro_z.dev_attr.attr, + &iio_scan_el_accel_x.dev_attr.attr, + &iio_scan_el_accel_y.dev_attr.attr, + &iio_scan_el_accel_z.dev_attr.attr, + &iio_scan_el_magn_x.dev_attr.attr, + &iio_scan_el_magn_y.dev_attr.attr, + &iio_scan_el_magn_z.dev_attr.attr, + &iio_scan_el_temp.dev_attr.attr, + &iio_scan_el_adc_0.dev_attr.attr, + &iio_scan_el_timestamp.dev_attr.attr, + NULL, +}; + +static struct attribute_group adis16400_scan_el_group = { + .attrs = adis16400_scan_el_attrs, + .name = "scan_elements", +}; + +/** + * adis16400_poll_func_th() top half interrupt handler called by trigger + * @private_data: iio_dev + **/ +static void adis16400_poll_func_th(struct iio_dev *indio_dev) +{ + struct adis16400_state *st = iio_dev_get_devdata(indio_dev); + st->last_timestamp = indio_dev->trig->timestamp; + schedule_work(&st->work_trigger_to_ring); + /* Indicate that this interrupt is being handled */ + + /* Technically this is trigger related, but without this + * handler running there is currently no way for the interrupt + * to clear. + */ +} + +/* Whilst this makes a lot of calls to iio_sw_ring functions - it is to device + * specific to be rolled into the core. + */ +static void adis16400_trigger_bh_to_ring(struct work_struct *work_s) +{ + struct adis16400_state *st + = container_of(work_s, struct adis16400_state, + work_trigger_to_ring); + + int i = 0; + s16 *data; + size_t datasize = st->indio_dev + ->ring->access.get_bpd(st->indio_dev->ring); + + data = kmalloc(datasize , GFP_KERNEL); + if (data == NULL) { + dev_err(&st->us->dev, "memory alloc failed in ring bh"); + return; + } + + if (st->indio_dev->scan_count) + if (adis16400_spi_read_burst(&st->indio_dev->dev, st->rx) >= 0) + for (; i < st->indio_dev->scan_count; i++) { + data[i] = combine_8_to_16(st->rx[i*2+1], + st->rx[i*2]); + } + + /* Guaranteed to be aligned with 8 byte boundary */ + if (st->indio_dev->scan_timestamp) + *((s64 *)(data + ((i + 3)/4)*4)) = st->last_timestamp; + + st->indio_dev->ring->access.store_to(st->indio_dev->ring, + (u8 *)data, + st->last_timestamp); + + iio_trigger_notify_done(st->indio_dev->trig); + kfree(data); + + return; +} +/* in these circumstances is it better to go with unaligned packing and + * deal with the cost?*/ +static int adis16400_data_rdy_ring_preenable(struct iio_dev *indio_dev) +{ + size_t size; + dev_dbg(&indio_dev->dev, "%s\n", __func__); + /* Check if there are any scan elements enabled, if not fail*/ + if (!(indio_dev->scan_count || indio_dev->scan_timestamp)) + return -EINVAL; + + if (indio_dev->ring->access.set_bpd) { + if (indio_dev->scan_timestamp) + if (indio_dev->scan_count) /* Timestamp and data */ + size = 6*sizeof(s64); + else /* Timestamp only */ + size = sizeof(s64); + else /* Data only */ + size = indio_dev->scan_count*sizeof(s16); + indio_dev->ring->access.set_bpd(indio_dev->ring, size); + } + + return 0; +} + +static int adis16400_data_rdy_ring_postenable(struct iio_dev *indio_dev) +{ + return indio_dev->trig + ? iio_trigger_attach_poll_func(indio_dev->trig, + indio_dev->pollfunc) + : 0; +} + +static int adis16400_data_rdy_ring_predisable(struct iio_dev *indio_dev) +{ + return indio_dev->trig + ? iio_trigger_dettach_poll_func(indio_dev->trig, + indio_dev->pollfunc) + : 0; +} + +void adis16400_unconfigure_ring(struct iio_dev *indio_dev) +{ + kfree(indio_dev->pollfunc); + iio_sw_rb_free(indio_dev->ring); +} + +int adis16400_configure_ring(struct iio_dev *indio_dev) +{ + int ret = 0; + struct adis16400_state *st = indio_dev->dev_data; + struct iio_ring_buffer *ring; + INIT_WORK(&st->work_trigger_to_ring, adis16400_trigger_bh_to_ring); + /* Set default scan mode */ + + iio_scan_mask_set(indio_dev, iio_scan_el_supply.number); + iio_scan_mask_set(indio_dev, iio_scan_el_gyro_x.number); + iio_scan_mask_set(indio_dev, iio_scan_el_gyro_y.number); + iio_scan_mask_set(indio_dev, iio_scan_el_gyro_z.number); + iio_scan_mask_set(indio_dev, iio_scan_el_accel_x.number); + iio_scan_mask_set(indio_dev, iio_scan_el_accel_y.number); + iio_scan_mask_set(indio_dev, iio_scan_el_accel_z.number); + iio_scan_mask_set(indio_dev, iio_scan_el_magn_x.number); + iio_scan_mask_set(indio_dev, iio_scan_el_magn_y.number); + iio_scan_mask_set(indio_dev, iio_scan_el_magn_z.number); + iio_scan_mask_set(indio_dev, iio_scan_el_temp.number); + iio_scan_mask_set(indio_dev, iio_scan_el_adc_0.number); + indio_dev->scan_timestamp = true; + + indio_dev->scan_el_attrs = &adis16400_scan_el_group; + + ring = iio_sw_rb_allocate(indio_dev); + if (!ring) { + ret = -ENOMEM; + return ret; + } + indio_dev->ring = ring; + /* Effectively select the ring buffer implementation */ + iio_ring_sw_register_funcs(&ring->access); + ring->preenable = &adis16400_data_rdy_ring_preenable; + ring->postenable = &adis16400_data_rdy_ring_postenable; + ring->predisable = &adis16400_data_rdy_ring_predisable; + ring->owner = THIS_MODULE; + + indio_dev->pollfunc = kzalloc(sizeof(*indio_dev->pollfunc), GFP_KERNEL); + if (indio_dev->pollfunc == NULL) { + ret = -ENOMEM; + goto error_iio_sw_rb_free;; + } + indio_dev->pollfunc->poll_func_main = &adis16400_poll_func_th; + indio_dev->pollfunc->private_data = indio_dev; + indio_dev->modes |= INDIO_RING_TRIGGERED; + return 0; + +error_iio_sw_rb_free: + iio_sw_rb_free(indio_dev->ring); + return ret; +} + +int adis16400_initialize_ring(struct iio_ring_buffer *ring) +{ + return iio_ring_buffer_register(ring, 0); +} + +void adis16400_uninitialize_ring(struct iio_ring_buffer *ring) +{ + iio_ring_buffer_unregister(ring); +} diff --git a/drivers/staging/iio/imu/adis16400_trigger.c b/drivers/staging/iio/imu/adis16400_trigger.c new file mode 100644 index 0000000..3b3250a --- /dev/null +++ b/drivers/staging/iio/imu/adis16400_trigger.c @@ -0,0 +1,127 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../iio.h" +#include "../sysfs.h" +#include "../trigger.h" +#include "adis16400.h" + +/** + * adis16400_data_rdy_trig_poll() the event handler for the data rdy trig + **/ +static int adis16400_data_rdy_trig_poll(struct iio_dev *dev_info, + int index, + s64 timestamp, + int no_test) +{ + struct adis16400_state *st = iio_dev_get_devdata(dev_info); + struct iio_trigger *trig = st->trig; + + trig->timestamp = timestamp; + iio_trigger_poll(trig); + + return IRQ_HANDLED; +} + +IIO_EVENT_SH(data_rdy_trig, &adis16400_data_rdy_trig_poll); + +static DEVICE_ATTR(name, S_IRUGO, iio_trigger_read_name, NULL); + +static struct attribute *adis16400_trigger_attrs[] = { + &dev_attr_name.attr, + NULL, +}; + +static const struct attribute_group adis16400_trigger_attr_group = { + .attrs = adis16400_trigger_attrs, +}; + +/** + * adis16400_data_rdy_trigger_set_state() set datardy interrupt state + **/ +static int adis16400_data_rdy_trigger_set_state(struct iio_trigger *trig, + bool state) +{ + struct adis16400_state *st = trig->private_data; + struct iio_dev *indio_dev = st->indio_dev; + int ret = 0; + + dev_dbg(&indio_dev->dev, "%s (%d)\n", __func__, state); + ret = adis16400_set_irq(&st->indio_dev->dev, state); + if (state == false) { + iio_remove_event_from_list(&iio_event_data_rdy_trig, + &indio_dev->interrupts[0] + ->ev_list); + /* possible quirk with handler currently worked around + by ensuring the work queue is empty */ + flush_scheduled_work(); + } else { + iio_add_event_to_list(&iio_event_data_rdy_trig, + &indio_dev->interrupts[0]->ev_list); + } + return ret; +} + +/** + * adis16400_trig_try_reen() try renabling irq for data rdy trigger + * @trig: the datardy trigger + **/ +static int adis16400_trig_try_reen(struct iio_trigger *trig) +{ + struct adis16400_state *st = trig->private_data; + enable_irq(st->us->irq); + /* irq reenabled so success! */ + return 0; +} + +int adis16400_probe_trigger(struct iio_dev *indio_dev) +{ + int ret; + struct adis16400_state *st = indio_dev->dev_data; + + st->trig = iio_allocate_trigger(); + st->trig->name = kmalloc(IIO_TRIGGER_NAME_LENGTH, GFP_KERNEL); + if (!st->trig->name) { + ret = -ENOMEM; + goto error_free_trig; + } + snprintf((char *)st->trig->name, + IIO_TRIGGER_NAME_LENGTH, + "adis16400-dev%d", indio_dev->id); + st->trig->dev.parent = &st->us->dev; + st->trig->owner = THIS_MODULE; + st->trig->private_data = st; + st->trig->set_trigger_state = &adis16400_data_rdy_trigger_set_state; + st->trig->try_reenable = &adis16400_trig_try_reen; + st->trig->control_attrs = &adis16400_trigger_attr_group; + ret = iio_trigger_register(st->trig); + + /* select default trigger */ + indio_dev->trig = st->trig; + if (ret) + goto error_free_trig_name; + + return 0; + +error_free_trig_name: + kfree(st->trig->name); +error_free_trig: + iio_free_trigger(st->trig); + + return ret; +} + +void adis16400_remove_trigger(struct iio_dev *indio_dev) +{ + struct adis16400_state *state = indio_dev->dev_data; + + iio_trigger_unregister(state->trig); + kfree(state->trig->name); + iio_free_trigger(state->trig); +} diff --git a/drivers/staging/iio/magnetometer/magnet.h b/drivers/staging/iio/magnetometer/magnet.h new file mode 100644 index 0000000..6f6c6ed --- /dev/null +++ b/drivers/staging/iio/magnetometer/magnet.h @@ -0,0 +1,31 @@ + +#include "../sysfs.h" + +/* Magnetometer types of attribute */ + +#define IIO_DEV_ATTR_MAGN_X_OFFSET(_mode, _show, _store, _addr) \ + IIO_DEVICE_ATTR(magn_x_offset, _mode, _show, _store, _addr) + +#define IIO_DEV_ATTR_MAGN_Y_OFFSET(_mode, _show, _store, _addr) \ + IIO_DEVICE_ATTR(magn_y_offset, _mode, _show, _store, _addr) + +#define IIO_DEV_ATTR_MAGN_Z_OFFSET(_mode, _show, _store, _addr) \ + IIO_DEVICE_ATTR(magn_z_offset, _mode, _show, _store, _addr) + +#define IIO_DEV_ATTR_MAGN_X_GAIN(_mode, _show, _store, _addr) \ + IIO_DEVICE_ATTR(magn_x_gain, _mode, _show, _store, _addr) + +#define IIO_DEV_ATTR_MAGN_Y_GAIN(_mode, _show, _store, _addr) \ + IIO_DEVICE_ATTR(magn_y_gain, _mode, _show, _store, _addr) + +#define IIO_DEV_ATTR_MAGN_Z_GAIN(_mode, _show, _store, _addr) \ + IIO_DEVICE_ATTR(magn_z_gain, _mode, _show, _store, _addr) + +#define IIO_DEV_ATTR_MAGN_X(_show, _addr) \ + IIO_DEVICE_ATTR(magn_x, S_IRUGO, _show, NULL, _addr) + +#define IIO_DEV_ATTR_MAGN_Y(_show, _addr) \ + IIO_DEVICE_ATTR(magn_y, S_IRUGO, _show, NULL, _addr) + +#define IIO_DEV_ATTR_MAGN_Z(_show, _addr) \ + IIO_DEVICE_ATTR(magn_z, S_IRUGO, _show, NULL, _addr) -- cgit v1.1 From 5f87404dfce14b2a194d085b7bac4b8c3672ab00 Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Tue, 4 May 2010 22:20:54 +0100 Subject: Staging: iio: Trivial - remove pointless semi colon (checkpatch found) Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/staging/iio/industrialio-trigger.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/staging/iio') diff --git a/drivers/staging/iio/industrialio-trigger.c b/drivers/staging/iio/industrialio-trigger.c index 918b0fd..46bd94a 100644 --- a/drivers/staging/iio/industrialio-trigger.c +++ b/drivers/staging/iio/industrialio-trigger.c @@ -169,7 +169,7 @@ struct iio_trigger *iio_trigger_find_by_name(const char *name, size_t len) mutex_unlock(&iio_trigger_list_lock); return found ? trig : NULL; -}; +} EXPORT_SYMBOL(iio_trigger_find_by_name); void iio_trigger_poll(struct iio_trigger *trig) -- cgit v1.1 From 26de7208281fd21606201ab3314f60e517d6c56f Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Tue, 4 May 2010 22:34:36 +0100 Subject: Staging: iio: accelerometers trivial checkpatch related fixes Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/staging/iio/accel/lis3l02dq_ring.c | 11 +++++------ drivers/staging/iio/accel/sca3000_core.c | 4 ++-- 2 files changed, 7 insertions(+), 8 deletions(-) (limited to 'drivers/staging/iio') diff --git a/drivers/staging/iio/accel/lis3l02dq_ring.c b/drivers/staging/iio/accel/lis3l02dq_ring.c index 7617da8..9214d84 100644 --- a/drivers/staging/iio/accel/lis3l02dq_ring.c +++ b/drivers/staging/iio/accel/lis3l02dq_ring.c @@ -192,8 +192,7 @@ error_ret: } -static const u8 read_all_tx_array[] = -{ +static const u8 read_all_tx_array[] = { LIS3L02DQ_READ_REG(LIS3L02DQ_REG_OUT_X_L_ADDR), 0, LIS3L02DQ_READ_REG(LIS3L02DQ_REG_OUT_X_H_ADDR), 0, LIS3L02DQ_READ_REG(LIS3L02DQ_REG_OUT_Y_L_ADDR), 0, @@ -358,10 +357,10 @@ static int lis3l02dq_data_rdy_ring_predisable(struct iio_dev *indio_dev) /* Caller responsible for locking as necessary. */ -static int __lis3l02dq_write_data_ready_config(struct device *dev, - struct - iio_event_handler_list *list, - bool state) +static int +__lis3l02dq_write_data_ready_config(struct device *dev, + struct iio_event_handler_list *list, + bool state) { int ret; u8 valold; diff --git a/drivers/staging/iio/accel/sca3000_core.c b/drivers/staging/iio/accel/sca3000_core.c index 9485b13..d4f82c39 100644 --- a/drivers/staging/iio/accel/sca3000_core.c +++ b/drivers/staging/iio/accel/sca3000_core.c @@ -277,7 +277,7 @@ static int sca3000_check_status(struct device *dev) if (ret < 0) goto error_ret; if (rx[1] & SCA3000_EEPROM_CS_ERROR) - dev_err(dev, "eeprom error \n"); + dev_err(dev, "eeprom error\n"); if (rx[1] & SCA3000_SPI_FRAME_ERROR) dev_err(dev, "Previous SPI Frame was corrupt\n"); kfree(rx); @@ -394,7 +394,7 @@ sca3000_show_available_measurement_modes(struct device *dev, break; } /* always supported */ - len += sprintf(buf + len, " 3 - motion detection \n"); + len += sprintf(buf + len, " 3 - motion detection\n"); return len; } -- cgit v1.1 From cb6405b15f7b2814f8f5170482b2e13ccfcb6376 Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Tue, 4 May 2010 22:34:37 +0100 Subject: Staging: iio: light trivial whitespace fix Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/staging/iio/light/tsl2563.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/staging/iio') diff --git a/drivers/staging/iio/light/tsl2563.c b/drivers/staging/iio/light/tsl2563.c index da3d51c..f8a0836 100644 --- a/drivers/staging/iio/light/tsl2563.c +++ b/drivers/staging/iio/light/tsl2563.c @@ -646,7 +646,7 @@ static int __devinit tsl2563_probe(struct i2c_client *client, err = tsl2563_detect(chip); if (err) { - dev_err(&client->dev, "device not found, error %d \n", -err); + dev_err(&client->dev, "device not found, error %d\n", -err); goto fail1; } -- cgit v1.1 From 1b183e4b93c4e05160d2c8f08b9546dd4752084b Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 4 May 2010 22:24:05 -0700 Subject: Staging: iio: adc: max1363_core: fix up some sparse warnings Also fix a minor coding style issue. Cc: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/staging/iio/adc/max1363_core.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) (limited to 'drivers/staging/iio') diff --git a/drivers/staging/iio/adc/max1363_core.c b/drivers/staging/iio/adc/max1363_core.c index fa91cc1..4a77657 100644 --- a/drivers/staging/iio/adc/max1363_core.c +++ b/drivers/staging/iio/adc/max1363_core.c @@ -139,16 +139,15 @@ static const struct max1363_mode max1363_mode_table[] = { }; const struct max1363_mode -*max1363_match_mode(u32 mask, const struct max1363_chip_info *ci) { +*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) & + if (!((~max1363_mode_table[ci->mode_list[i]].modemask) & mask)) - return &max1363_mode_table[ci - ->mode_list[i]]; - return 0; + return &max1363_mode_table[ci->mode_list[i]]; + return NULL; }; static ssize_t max1363_show_precision(struct device *dev, @@ -298,7 +297,7 @@ static ssize_t max1363_show_scale(struct device *dev, st->chip_info->int_vref_mv >> st->chip_info->bits); } -IIO_DEVICE_ATTR(in_scale, S_IRUGO, max1363_show_scale, NULL, 0); +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, @@ -309,7 +308,7 @@ static ssize_t max1363_show_name(struct device *dev, return sprintf(buf, "%s\n", st->chip_info->name); } -IIO_DEVICE_ATTR(name, S_IRUGO, max1363_show_name, NULL, 0); +static IIO_DEVICE_ATTR(name, S_IRUGO, max1363_show_name, NULL, 0); /* Applies to max1363 */ static const enum max1363_modes max1363_mode_list[] = { -- cgit v1.1 From f03de82b3f10b2e8b7f75f3d4323ec38d131a99c Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 4 May 2010 22:26:19 -0700 Subject: Staging: iio: max1363_core: fix bug in kzalloc call The operands were switched around :( Cc: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/staging/iio/adc/max1363_core.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'drivers/staging/iio') diff --git a/drivers/staging/iio/adc/max1363_core.c b/drivers/staging/iio/adc/max1363_core.c index 4a77657..905f856 100644 --- a/drivers/staging/iio/adc/max1363_core.c +++ b/drivers/staging/iio/adc/max1363_core.c @@ -958,9 +958,8 @@ static int __devinit max1363_probe(struct i2c_client *client, } st->indio_dev->available_scan_masks - = kzalloc(GFP_KERNEL, - sizeof(*st->indio_dev->available_scan_masks)* - (st->chip_info->num_modes + 1)); + = 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; -- cgit v1.1 From acbbfe236eb6fa25fdfb2a8762d0ad260f6b667c Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 4 May 2010 22:28:37 -0700 Subject: Staging: iio: adc: max1363_ring.c: fix up sparse warnings Cc: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/staging/iio/adc/max1363_ring.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/staging/iio') diff --git a/drivers/staging/iio/adc/max1363_ring.c b/drivers/staging/iio/adc/max1363_ring.c index 85fde75..c8aa011 100644 --- a/drivers/staging/iio/adc/max1363_ring.c +++ b/drivers/staging/iio/adc/max1363_ring.c @@ -142,7 +142,7 @@ static int max1363_ring_predisable(struct iio_dev *indio_dev) * then. Some triggers will generate their own time stamp. Currently * there is no way of notifying them when no one cares. **/ -void max1363_poll_func_th(struct iio_dev *indio_dev) +static void max1363_poll_func_th(struct iio_dev *indio_dev) { struct max1363_state *st = indio_dev->dev_data; -- cgit v1.1 From 341713f265950884d2edbbf55088127a915927ad Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 4 May 2010 22:29:41 -0700 Subject: Staging: iio: light: tsl2563: fix static sparse warning Cc: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/staging/iio/light/tsl2563.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/staging/iio') diff --git a/drivers/staging/iio/light/tsl2563.c b/drivers/staging/iio/light/tsl2563.c index f8a0836..e4b0a5e 100644 --- a/drivers/staging/iio/light/tsl2563.c +++ b/drivers/staging/iio/light/tsl2563.c @@ -607,7 +607,7 @@ static ssize_t tsl2563_show_name(struct device *dev, return sprintf(buf, "%s\n", chip->client->name); } -DEVICE_ATTR(name, S_IRUGO, tsl2563_show_name, NULL); +static DEVICE_ATTR(name, S_IRUGO, tsl2563_show_name, NULL); static struct attribute *tsl2563_attributes[] = { &dev_attr_adc0.attr, -- cgit v1.1 From d1d8abdb05bddce645649c17949b0bc62e6f096b Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 4 May 2010 22:31:22 -0700 Subject: Staging: iio: trigger: fix up some global variables These should be static. Cc: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/staging/iio/trigger/iio-trig-gpio.c | 4 ++-- drivers/staging/iio/trigger/iio-trig-periodic-rtc.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers/staging/iio') diff --git a/drivers/staging/iio/trigger/iio-trig-gpio.c b/drivers/staging/iio/trigger/iio-trig-gpio.c index e40099f..1da285d 100644 --- a/drivers/staging/iio/trigger/iio-trig-gpio.c +++ b/drivers/staging/iio/trigger/iio-trig-gpio.c @@ -25,8 +25,8 @@ #include "../iio.h" #include "../trigger.h" -LIST_HEAD(iio_gpio_trigger_list); -DEFINE_MUTEX(iio_gpio_trigger_list_lock); +static LIST_HEAD(iio_gpio_trigger_list); +static DEFINE_MUTEX(iio_gpio_trigger_list_lock); struct iio_gpio_trigger_info { struct mutex in_use; diff --git a/drivers/staging/iio/trigger/iio-trig-periodic-rtc.c b/drivers/staging/iio/trigger/iio-trig-periodic-rtc.c index 4295bbc..4ee3ae1 100644 --- a/drivers/staging/iio/trigger/iio-trig-periodic-rtc.c +++ b/drivers/staging/iio/trigger/iio-trig-periodic-rtc.c @@ -19,8 +19,8 @@ #include "../iio.h" #include "../trigger.h" -LIST_HEAD(iio_prtc_trigger_list); -DEFINE_MUTEX(iio_prtc_trigger_list_lock); +static LIST_HEAD(iio_prtc_trigger_list); +static DEFINE_MUTEX(iio_prtc_trigger_list_lock); struct iio_prtc_trigger_info { struct rtc_device *rtc; -- cgit v1.1 From 7cfce527a2cec1a2757136eb44d8dfa473a82d37 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 4 May 2010 22:32:01 -0700 Subject: Staging: iio: accel: fix up some sparse warnings. Minor stuff (static, NULL, etc.) Cc: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/staging/iio/accel/lis3l02dq_ring.c | 3 +-- drivers/staging/iio/accel/sca3000_ring.c | 8 ++++---- 2 files changed, 5 insertions(+), 6 deletions(-) (limited to 'drivers/staging/iio') diff --git a/drivers/staging/iio/accel/lis3l02dq_ring.c b/drivers/staging/iio/accel/lis3l02dq_ring.c index 9214d84..78d8572 100644 --- a/drivers/staging/iio/accel/lis3l02dq_ring.c +++ b/drivers/staging/iio/accel/lis3l02dq_ring.c @@ -207,7 +207,7 @@ static const u8 read_all_tx_array[] = { * @rx_array: (dma capable) recieve array, must be at least * 4*number of channels **/ -int lis3l02dq_read_all(struct lis3l02dq_state *st, u8 *rx_array) +static int lis3l02dq_read_all(struct lis3l02dq_state *st, u8 *rx_array) { struct spi_transfer *xfers; struct spi_message msg; @@ -588,7 +588,6 @@ void lis3l02dq_uninitialize_ring(struct iio_ring_buffer *ring) iio_ring_buffer_unregister(ring); } - int lis3l02dq_set_ring_length(struct iio_dev *indio_dev, int length) { /* Set sensible defaults for the ring buffer */ diff --git a/drivers/staging/iio/accel/sca3000_ring.c b/drivers/staging/iio/accel/sca3000_ring.c index 2b39e6f..8e8c068 100644 --- a/drivers/staging/iio/accel/sca3000_ring.c +++ b/drivers/staging/iio/accel/sca3000_ring.c @@ -186,9 +186,9 @@ static ssize_t sca3000_store_ring_bpse(struct device *dev, return ret ? ret : len; } -static IIO_SCAN_EL_C(accel_x, 0, 0, 0, 0); -static IIO_SCAN_EL_C(accel_y, 1, 0, 0, 0); -static IIO_SCAN_EL_C(accel_z, 2, 0, 0, 0); +static IIO_SCAN_EL_C(accel_x, 0, 0, 0, NULL); +static IIO_SCAN_EL_C(accel_y, 1, 0, 0, NULL); +static IIO_SCAN_EL_C(accel_z, 2, 0, 0, NULL); static IIO_CONST_ATTR(accel_precision_available, "8 11"); static IIO_DEVICE_ATTR(accel_precision, S_IRUGO | S_IWUSR, @@ -244,7 +244,7 @@ static struct iio_ring_buffer *sca3000_rb_allocate(struct iio_dev *indio_dev) ring = kzalloc(sizeof *ring, GFP_KERNEL); if (!ring) - return 0; + return NULL; ring->private = indio_dev; buf = &ring->buf; iio_ring_buffer_init(buf, indio_dev); -- cgit v1.1 From 0deebb4fc4784699afec04231f981aaad3126ff9 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 4 May 2010 22:32:43 -0700 Subject: Staging: iio: industrialio-ring.c: fix up sparse warnings Cc: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/staging/iio/industrialio-ring.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) (limited to 'drivers/staging/iio') diff --git a/drivers/staging/iio/industrialio-ring.c b/drivers/staging/iio/industrialio-ring.c index fc27d22..ada159b 100644 --- a/drivers/staging/iio/industrialio-ring.c +++ b/drivers/staging/iio/industrialio-ring.c @@ -58,7 +58,7 @@ EXPORT_SYMBOL(iio_push_or_escallate_ring_event); * This function relies on all ring buffer implementations having an * iio_ring_buffer as their first element. **/ -int iio_ring_open(struct inode *inode, struct file *filp) +static int iio_ring_open(struct inode *inode, struct file *filp) { struct iio_handler *hand = container_of(inode->i_cdev, struct iio_handler, chrdev); @@ -77,7 +77,7 @@ int iio_ring_open(struct inode *inode, struct file *filp) * This function relies on all ring buffer implementations having an * iio_ring_buffer as their first element. **/ -int iio_ring_release(struct inode *inode, struct file *filp) +static int iio_ring_release(struct inode *inode, struct file *filp) { struct cdev *cd = inode->i_cdev; struct iio_handler *hand = iio_cdev_to_handler(cd); @@ -96,10 +96,8 @@ int iio_ring_release(struct inode *inode, struct file *filp) * This function relies on all ring buffer implementations having an * iio_ring _bufer as their first element. **/ -ssize_t iio_ring_rip_outer(struct file *filp, - char *buf, - size_t count, - loff_t *f_ps) +static ssize_t iio_ring_rip_outer(struct file *filp, char __user *buf, + size_t count, loff_t *f_ps) { struct iio_ring_buffer *rb = filp->private_data; int ret, dead_offset, copied; @@ -251,7 +249,7 @@ void iio_ring_buffer_init(struct iio_ring_buffer *ring, ring->indio_dev = dev_info; ring->ev_int.private = ring; ring->access_handler.private = ring; - ring->shared_ev_pointer.ev_p = 0; + ring->shared_ev_pointer.ev_p = NULL; spin_lock_init(&ring->shared_ev_pointer.lock); } EXPORT_SYMBOL(iio_ring_buffer_init); -- cgit v1.1 From 19ca92e0bca3499a12eb9b612b90bbe90d2cc895 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 4 May 2010 22:33:27 -0700 Subject: Staging: iio: ring_sw.c: fix up sparse warnings NULL usage, static stuff, etc. Cc: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/staging/iio/ring_sw.c | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) (limited to 'drivers/staging/iio') diff --git a/drivers/staging/iio/ring_sw.c b/drivers/staging/iio/ring_sw.c index 5ee3e45..1f14cd4 100644 --- a/drivers/staging/iio/ring_sw.c +++ b/drivers/staging/iio/ring_sw.c @@ -21,10 +21,10 @@ static inline int __iio_allocate_sw_ring_buffer(struct iio_sw_ring_buffer *ring, return -EINVAL; __iio_update_ring_buffer(&ring->buf, bytes_per_datum, length); ring->data = kmalloc(length*ring->buf.bpd, GFP_KERNEL); - ring->read_p = 0; - ring->write_p = 0; - ring->last_written_p = 0; - ring->half_p = 0; + ring->read_p = NULL; + ring->write_p = NULL; + ring->last_written_p = NULL; + ring->half_p = NULL; return ring->data ? 0 : -ENOMEM; } @@ -62,16 +62,15 @@ EXPORT_SYMBOL(iio_unmark_sw_rb_in_use); * in the device driver */ /* Lock always held if their is a chance this may be called */ /* Only one of these per ring may run concurrently - enforced by drivers */ -int iio_store_to_sw_ring(struct iio_sw_ring_buffer *ring, - unsigned char *data, - s64 timestamp) +static int iio_store_to_sw_ring(struct iio_sw_ring_buffer *ring, + unsigned char *data, s64 timestamp) { int ret = 0; int code; unsigned char *temp_ptr, *change_test_ptr; /* initial store */ - if (unlikely(ring->write_p == 0)) { + if (unlikely(ring->write_p == NULL)) { ring->write_p = ring->data; /* Doesn't actually matter if this is out of the set * as long as the read pointer is valid before this @@ -102,7 +101,7 @@ int iio_store_to_sw_ring(struct iio_sw_ring_buffer *ring, */ ring->write_p = temp_ptr; - if (ring->read_p == 0) + if (ring->read_p == NULL) ring->read_p = ring->data; /* Buffer full - move the read pointer and create / escalate * ring event */ @@ -182,7 +181,7 @@ int iio_rip_sw_rb(struct iio_ring_buffer *r, /* build local copy */ initial_read_p = ring->read_p; - if (unlikely(initial_read_p == 0)) { /* No data here as yet */ + if (unlikely(initial_read_p == NULL)) { /* No data here as yet */ ret = 0; goto error_free_data_cpy; } @@ -280,8 +279,8 @@ int iio_store_to_sw_rb(struct iio_ring_buffer *r, u8 *data, s64 timestamp) } EXPORT_SYMBOL(iio_store_to_sw_rb); -int iio_read_last_from_sw_ring(struct iio_sw_ring_buffer *ring, - unsigned char *data) +static int iio_read_last_from_sw_ring(struct iio_sw_ring_buffer *ring, + unsigned char *data) { unsigned char *last_written_p_copy; @@ -291,7 +290,7 @@ again: last_written_p_copy = ring->last_written_p; barrier(); /*unnessecary? */ /* Check there is anything here */ - if (last_written_p_copy == 0) + if (last_written_p_copy == NULL) return -EAGAIN; memcpy(data, last_written_p_copy, ring->buf.bpd); @@ -412,7 +411,7 @@ struct iio_ring_buffer *iio_sw_rb_allocate(struct iio_dev *indio_dev) ring = kzalloc(sizeof *ring, GFP_KERNEL); if (!ring) - return 0; + return NULL; buf = &ring->buf; iio_ring_buffer_init(buf, indio_dev); __iio_init_sw_ring_buffer(ring); -- cgit v1.1 From 74112d3f3a64afb98179169cf8af691ccc63434d Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 4 May 2010 22:34:00 -0700 Subject: Staging: iio: industrialio-trigger.c: minor fixups We needed to include a header file that declared the functions that are being exported in this file. Also fix up an indentation problem, and some sparse warnings. Cc: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/staging/iio/industrialio-trigger.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) (limited to 'drivers/staging/iio') diff --git a/drivers/staging/iio/industrialio-trigger.c b/drivers/staging/iio/industrialio-trigger.c index 46bd94a..5682e61 100644 --- a/drivers/staging/iio/industrialio-trigger.c +++ b/drivers/staging/iio/industrialio-trigger.c @@ -18,6 +18,7 @@ #include "iio.h" #include "trigger.h" +#include "trigger_consumer.h" /* RFC - Question of approach * Make the common case (single sensor single trigger) @@ -92,9 +93,9 @@ idr_again: **/ static void iio_trigger_unregister_id(struct iio_trigger *trig_info) { - spin_lock(&iio_trigger_idr_lock); - idr_remove(&iio_trigger_idr, trig_info->id); - spin_unlock(&iio_trigger_idr_lock); + spin_lock(&iio_trigger_idr_lock); + idr_remove(&iio_trigger_idr, trig_info->id); + spin_unlock(&iio_trigger_idr_lock); } int iio_trigger_register(struct iio_trigger *trig_info) @@ -334,9 +335,9 @@ static ssize_t iio_trigger_write_current(struct device *dev, return len; } -DEVICE_ATTR(current_trigger, S_IRUGO | S_IWUSR, - iio_trigger_read_current, - iio_trigger_write_current); +static DEVICE_ATTR(current_trigger, S_IRUGO | S_IWUSR, + iio_trigger_read_current, + iio_trigger_write_current); static struct attribute *iio_trigger_consumer_attrs[] = { &dev_attr_current_trigger.attr, -- cgit v1.1 From 85798ec85ec6146bff3af886c06d72cc66940a05 Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Fri, 7 May 2010 15:38:54 +0100 Subject: staging: iio: Documentation fixes Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/staging/iio/Documentation/sysfs-class-iio | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/staging/iio') diff --git a/drivers/staging/iio/Documentation/sysfs-class-iio b/drivers/staging/iio/Documentation/sysfs-class-iio index 7238582..ff19f6e 100644 --- a/drivers/staging/iio/Documentation/sysfs-class-iio +++ b/drivers/staging/iio/Documentation/sysfs-class-iio @@ -66,7 +66,7 @@ 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 volt[m]_scale in order to obtain voltage in + 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 @@ -111,8 +111,8 @@ 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 volt[m]_offset in order to obtain the measured voltage - in millivolts. If shared across all in channels then m is not present. + 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 -- cgit v1.1 From a1169c5a0bfec75730a080a5f5d668e65144d1d1 Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Fri, 7 May 2010 15:38:55 +0100 Subject: staging: iio: Break up gyro.h and move to new abi Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/staging/iio/accel/inclinometer.h | 23 +++++++++++++++++ drivers/staging/iio/gyro/gyro.h | 43 +++---------------------------- drivers/staging/iio/imu/adis16300_core.c | 11 ++++---- drivers/staging/iio/imu/adis16400_core.c | 16 ++++++------ drivers/staging/iio/magnetometer/magnet.h | 12 ++++----- 5 files changed, 47 insertions(+), 58 deletions(-) create mode 100644 drivers/staging/iio/accel/inclinometer.h (limited to 'drivers/staging/iio') diff --git a/drivers/staging/iio/accel/inclinometer.h b/drivers/staging/iio/accel/inclinometer.h new file mode 100644 index 0000000..5b49f83 --- /dev/null +++ b/drivers/staging/iio/accel/inclinometer.h @@ -0,0 +1,23 @@ +/* + * Inclinometer related attributes + */ +#include "../sysfs.h" + +#define IIO_DEV_ATTR_INCLI_X(_show, _addr) \ + IIO_DEVICE_ATTR(incli_x_raw, S_IRUGO, _show, NULL, _addr) + +#define IIO_DEV_ATTR_INCLI_Y(_show, _addr) \ + IIO_DEVICE_ATTR(incli_y_raw, S_IRUGO, _show, NULL, _addr) + +#define IIO_DEV_ATTR_INCLI_Z(_show, _addr) \ + IIO_DEVICE_ATTR(incli_z_raw, S_IRUGO, _show, NULL, _addr) + +#define IIO_DEV_ATTR_INCLI_X_OFFSET(_mode, _show, _store, _addr) \ + IIO_DEVICE_ATTR(incli_x_offset, _mode, _show, _store, _addr) + +#define IIO_DEV_ATTR_INCLI_Y_OFFSET(_mode, _show, _store, _addr) \ + IIO_DEVICE_ATTR(incli_y_offset, _mode, _show, _store, _addr) + +#define IIO_DEV_ATTR_INCLI_Z_OFFSET(_mode, _show, _store, _addr) \ + IIO_DEVICE_ATTR(incli_z_offset, _mode, _show, _store, _addr) + diff --git a/drivers/staging/iio/gyro/gyro.h b/drivers/staging/iio/gyro/gyro.h index 7c4dcf2..16f6ffa 100644 --- a/drivers/staging/iio/gyro/gyro.h +++ b/drivers/staging/iio/gyro/gyro.h @@ -28,49 +28,14 @@ IIO_DEVICE_ATTR(gyro_scale, S_IRUGO, _show, _store, _addr) #define IIO_DEV_ATTR_GYRO(_show, _addr) \ - IIO_DEVICE_ATTR(gyro, S_IRUGO, _show, NULL, _addr) + IIO_DEVICE_ATTR(gyro_raw, S_IRUGO, _show, NULL, _addr) #define IIO_DEV_ATTR_GYRO_X(_show, _addr) \ - IIO_DEVICE_ATTR(gyro_x, S_IRUGO, _show, NULL, _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, S_IRUGO, _show, NULL, _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, S_IRUGO, _show, NULL, _addr) + IIO_DEVICE_ATTR(gyro_z_raw, S_IRUGO, _show, NULL, _addr) -#define IIO_DEV_ATTR_TEMP_X(_show, _addr) \ - IIO_DEVICE_ATTR(temp_x, S_IRUGO, _show, NULL, _addr) - -#define IIO_DEV_ATTR_TEMP_Y(_show, _addr) \ - IIO_DEVICE_ATTR(temp_y, S_IRUGO, _show, NULL, _addr) - -#define IIO_DEV_ATTR_TEMP_Z(_show, _addr) \ - IIO_DEVICE_ATTR(temp_z, S_IRUGO, _show, NULL, _addr) - -#define IIO_DEV_ATTR_INCLI_X(_show, _addr) \ - IIO_DEVICE_ATTR(incli_x, S_IRUGO, _show, NULL, _addr) - -#define IIO_DEV_ATTR_INCLI_Y(_show, _addr) \ - IIO_DEVICE_ATTR(incli_y, S_IRUGO, _show, NULL, _addr) - -#define IIO_DEV_ATTR_INCLI_Z(_show, _addr) \ - IIO_DEVICE_ATTR(incli_z, 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_DEV_ATTR_ROT(_show, _addr) \ - IIO_DEVICE_ATTR(rot, S_IRUGO, _show, NULL, _addr) - -#define IIO_DEV_ATTR_ROT_OFFSET(_mode, _show, _store, _addr) \ - IIO_DEVICE_ATTR(rot_offset, _mode, _show, _store, _addr) - -#define IIO_DEV_ATTR_ANGL(_show, _addr) \ - IIO_DEVICE_ATTR(angl, S_IRUGO, _show, NULL, _addr) diff --git a/drivers/staging/iio/imu/adis16300_core.c b/drivers/staging/iio/imu/adis16300_core.c index 9f5f8cb..b923ecc 100644 --- a/drivers/staging/iio/imu/adis16300_core.c +++ b/drivers/staging/iio/imu/adis16300_core.c @@ -21,6 +21,7 @@ #include "../iio.h" #include "../sysfs.h" #include "../accel/accel.h" +#include "../accel/inclinometer.h" #include "../gyro/gyro.h" #include "../adc/adc.h" @@ -613,7 +614,7 @@ static IIO_DEV_ATTR_INCLI_Y(adis16300_read_13bit_signed, ADIS16300_YINCLI_OUT); static IIO_CONST_ATTR(incli_scale, "0.044 d"); -static IIO_DEV_ATTR_TEMP(adis16300_read_12bit_signed); +static IIO_DEV_ATTR_TEMP_RAW(adis16300_read_12bit_signed); static IIO_CONST_ATTR(temp_offset, "198.16 K"); static IIO_CONST_ATTR(temp_scale, "0.14 K"); @@ -645,16 +646,16 @@ static struct attribute *adis16300_attributes[] = { &iio_dev_attr_accel_z_offset.dev_attr.attr, &iio_dev_attr_in_supply_raw.dev_attr.attr, &iio_const_attr_in_supply_scale.dev_attr.attr, - &iio_dev_attr_gyro_x.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.dev_attr.attr, - &iio_dev_attr_incli_y.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.dev_attr.attr, + &iio_dev_attr_temp_raw.dev_attr.attr, &iio_const_attr_temp_offset.dev_attr.attr, &iio_const_attr_temp_scale.dev_attr.attr, &iio_dev_attr_in0_raw.dev_attr.attr, diff --git a/drivers/staging/iio/imu/adis16400_core.c b/drivers/staging/iio/imu/adis16400_core.c index 27e4a73..868c526 100644 --- a/drivers/staging/iio/imu/adis16400_core.c +++ b/drivers/staging/iio/imu/adis16400_core.c @@ -626,7 +626,7 @@ static IIO_DEV_ATTR_MAGN_Z(adis16400_read_14bit_signed, static IIO_CONST_ATTR(magn_scale, "0.0005 Gs"); -static IIO_DEV_ATTR_TEMP(adis16400_read_12bit_signed); +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"); @@ -658,19 +658,19 @@ static struct attribute *adis16400_attributes[] = { &iio_dev_attr_accel_z_offset.dev_attr.attr, &iio_dev_attr_in_supply_raw.dev_attr.attr, &iio_const_attr_in_supply_scale.dev_attr.attr, - &iio_dev_attr_gyro_x.dev_attr.attr, - &iio_dev_attr_gyro_y.dev_attr.attr, - &iio_dev_attr_gyro_z.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.dev_attr.attr, - &iio_dev_attr_magn_y.dev_attr.attr, - &iio_dev_attr_magn_z.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.dev_attr.attr, + &iio_dev_attr_temp_raw.dev_attr.attr, &iio_const_attr_temp_offset.dev_attr.attr, &iio_const_attr_temp_scale.dev_attr.attr, &iio_dev_attr_in0_raw.dev_attr.attr, diff --git a/drivers/staging/iio/magnetometer/magnet.h b/drivers/staging/iio/magnetometer/magnet.h index 6f6c6ed..6433830 100644 --- a/drivers/staging/iio/magnetometer/magnet.h +++ b/drivers/staging/iio/magnetometer/magnet.h @@ -21,11 +21,11 @@ #define IIO_DEV_ATTR_MAGN_Z_GAIN(_mode, _show, _store, _addr) \ IIO_DEVICE_ATTR(magn_z_gain, _mode, _show, _store, _addr) -#define IIO_DEV_ATTR_MAGN_X(_show, _addr) \ - IIO_DEVICE_ATTR(magn_x, S_IRUGO, _show, NULL, _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, 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, S_IRUGO, _show, NULL, _addr) +#define IIO_DEV_ATTR_MAGN_Z(_show, _addr) \ + IIO_DEVICE_ATTR(magn_z_raw, S_IRUGO, _show, NULL, _addr) -- cgit v1.1 From 4da54d93abe7bdad6880e27270ad50b6303898b6 Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Fri, 7 May 2010 15:38:56 +0100 Subject: staging: iio: adis16300 clean out some unused code Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/staging/iio/imu/adis16300.h | 9 ---- drivers/staging/iio/imu/adis16300_core.c | 70 +------------------------------- 2 files changed, 2 insertions(+), 77 deletions(-) (limited to 'drivers/staging/iio') diff --git a/drivers/staging/iio/imu/adis16300.h b/drivers/staging/iio/imu/adis16300.h index 77d890d..1c7ea5c 100644 --- a/drivers/staging/iio/imu/adis16300.h +++ b/drivers/staging/iio/imu/adis16300.h @@ -115,21 +115,12 @@ struct adis16300_state { struct mutex buf_lock; }; -int adis16300_spi_write_reg_8(struct device *dev, - u8 reg_address, - u8 val); - int adis16300_spi_read_burst(struct device *dev, u8 *rx); -int adis16300_spi_read_sequence(struct device *dev, - u8 *tx, u8 *rx, int num); - int adis16300_set_irq(struct device *dev, bool enable); int adis16300_reset(struct device *dev); -int adis16300_stop_device(struct device *dev); - int adis16300_check_status(struct device *dev); #ifdef CONFIG_IIO_RING_BUFFER diff --git a/drivers/staging/iio/imu/adis16300_core.c b/drivers/staging/iio/imu/adis16300_core.c index b923ecc..5a7e5ef 100644 --- a/drivers/staging/iio/imu/adis16300_core.c +++ b/drivers/staging/iio/imu/adis16300_core.c @@ -40,7 +40,7 @@ * @reg_address: the address of the register to be written * @val: the value to write **/ -int adis16300_spi_write_reg_8(struct device *dev, +static int adis16300_spi_write_reg_8(struct device *dev, u8 reg_address, u8 val) { @@ -202,55 +202,6 @@ int adis16300_spi_read_burst(struct device *dev, u8 *rx) return ret; } -/** - * adis16300_spi_read_sequence() - read a sequence of 16-bit registers - * @dev: device associated with child of actual device (iio_dev or iio_trig) - * @tx: register addresses in bytes 0,2,4,6... (min size is 2*num bytes) - * @rx: somewhere to pass back the value read (min size is 2*num bytes) - **/ -int adis16300_spi_read_sequence(struct device *dev, - u8 *tx, u8 *rx, int num) -{ - struct spi_message msg; - struct spi_transfer *xfers; - struct iio_dev *indio_dev = dev_get_drvdata(dev); - struct adis16300_state *st = iio_dev_get_devdata(indio_dev); - int ret, i; - - xfers = kzalloc(num + 1, GFP_KERNEL); - if (xfers == NULL) { - dev_err(&st->us->dev, "memory alloc failed"); - ret = -ENOMEM; - goto error_ret; - } - - /* tx: |add1|addr2|addr3|...|addrN |zero| - * rx: |zero|res1 |res2 |...|resN-1|resN| */ - spi_message_init(&msg); - for (i = 0; i < num + 1; i++) { - if (i > 0) - xfers[i].rx_buf = st->rx + 2*(i - 1); - if (i < num) - xfers[i].tx_buf = st->tx + 2*i; - xfers[i].bits_per_word = 8; - xfers[i].len = 2; - xfers[i].cs_change = 1; - spi_message_add_tail(&xfers[i], &msg); - } - - mutex_lock(&st->buf_lock); - - ret = spi_sync(st->us, &msg); - if (ret) - dev_err(&st->us->dev, "problem when reading sequence"); - - mutex_unlock(&st->buf_lock); - kfree(xfers); - -error_ret: - return ret; -} - static ssize_t adis16300_spi_read_signed(struct device *dev, struct device_attribute *attr, char *buf, @@ -458,7 +409,7 @@ int adis16300_reset(struct device *dev) } /* Power down the device */ -int adis16300_stop_device(struct device *dev) +static int adis16300_stop_device(struct device *dev) { int ret; u16 val = ADIS16300_SLP_CNT_POWER_OFF; @@ -470,23 +421,6 @@ int adis16300_stop_device(struct device *dev) return ret; } -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; -} - int adis16300_check_status(struct device *dev) { u16 status; -- cgit v1.1 From e5107fb87018c8fc6b7376c4669dc877ab65db47 Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Fri, 7 May 2010 15:38:57 +0100 Subject: staging: iio: Documentation update to add incli and switch to magn Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/staging/iio/Documentation/sysfs-class-iio | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) (limited to 'drivers/staging/iio') diff --git a/drivers/staging/iio/Documentation/sysfs-class-iio b/drivers/staging/iio/Documentation/sysfs-class-iio index ff19f6e..714b4c5 100644 --- a/drivers/staging/iio/Documentation/sysfs-class-iio +++ b/drivers/staging/iio/Documentation/sysfs-class-iio @@ -143,7 +143,16 @@ Description: 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]/mag[_x|_y|_z][m]_raw +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: -- cgit v1.1 From b155498b1842090f2cf5164908deaa0762a9a8b1 Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Fri, 7 May 2010 15:38:58 +0100 Subject: staging: iio: adis16400 clean out some unused code Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/staging/iio/imu/adis16400.h | 9 ------ drivers/staging/iio/imu/adis16400_core.c | 55 ++------------------------------ 2 files changed, 3 insertions(+), 61 deletions(-) (limited to 'drivers/staging/iio') diff --git a/drivers/staging/iio/imu/adis16400.h b/drivers/staging/iio/imu/adis16400.h index a8062f6..5a69a7a 100644 --- a/drivers/staging/iio/imu/adis16400.h +++ b/drivers/staging/iio/imu/adis16400.h @@ -147,21 +147,12 @@ struct adis16400_state { struct mutex buf_lock; }; -int adis16400_spi_write_reg_8(struct device *dev, - u8 reg_address, - u8 val); - int adis16400_spi_read_burst(struct device *dev, u8 *rx); -int adis16400_spi_read_sequence(struct device *dev, - u8 *tx, u8 *rx, int num); - int adis16400_set_irq(struct device *dev, bool enable); int adis16400_reset(struct device *dev); -int adis16400_stop_device(struct device *dev); - int adis16400_check_status(struct device *dev); #ifdef CONFIG_IIO_RING_BUFFER diff --git a/drivers/staging/iio/imu/adis16400_core.c b/drivers/staging/iio/imu/adis16400_core.c index 868c526..e69e2ce 100644 --- a/drivers/staging/iio/imu/adis16400_core.c +++ b/drivers/staging/iio/imu/adis16400_core.c @@ -47,7 +47,7 @@ * @reg_address: the address of the register to be written * @val: the value to write **/ -int adis16400_spi_write_reg_8(struct device *dev, +static int adis16400_spi_write_reg_8(struct device *dev, u8 reg_address, u8 val) { @@ -209,55 +209,6 @@ int adis16400_spi_read_burst(struct device *dev, u8 *rx) return ret; } -/** - * adis16400_spi_read_sequence() - read a sequence of 16-bit registers - * @dev: device associated with child of actual device (iio_dev or iio_trig) - * @tx: register addresses in bytes 0,2,4,6... (min size is 2*num bytes) - * @rx: somewhere to pass back the value read (min size is 2*num bytes) - **/ -int adis16400_spi_read_sequence(struct device *dev, - u8 *tx, u8 *rx, int num) -{ - struct spi_message msg; - struct spi_transfer *xfers; - struct iio_dev *indio_dev = dev_get_drvdata(dev); - struct adis16400_state *st = iio_dev_get_devdata(indio_dev); - int ret, i; - - xfers = kzalloc(num + 1, GFP_KERNEL); - if (xfers == NULL) { - dev_err(&st->us->dev, "memory alloc failed"); - ret = -ENOMEM; - goto error_ret; - } - - /* tx: |add1|addr2|addr3|...|addrN |zero| - * rx: |zero|res1 |res2 |...|resN-1|resN| */ - spi_message_init(&msg); - for (i = 0; i < num + 1; i++) { - if (i > 0) - xfers[i].rx_buf = st->rx + 2*(i - 1); - if (i < num) - xfers[i].tx_buf = st->tx + 2*i; - xfers[i].bits_per_word = 8; - xfers[i].len = 2; - xfers[i].cs_change = 1; - spi_message_add_tail(&xfers[i], &msg); - } - - mutex_lock(&st->buf_lock); - - ret = spi_sync(st->us, &msg); - if (ret) - dev_err(&st->us->dev, "problem when reading sequence"); - - mutex_unlock(&st->buf_lock); - kfree(xfers); - -error_ret: - return ret; -} - static ssize_t adis16400_spi_read_signed(struct device *dev, struct device_attribute *attr, char *buf, @@ -450,7 +401,7 @@ int adis16400_reset(struct device *dev) } /* Power down the device */ -int adis16400_stop_device(struct device *dev) +static int adis16400_stop_device(struct device *dev) { int ret; u16 val = ADIS16400_SLP_CNT_POWER_OFF; @@ -462,7 +413,7 @@ int adis16400_stop_device(struct device *dev) return ret; } -int adis16400_self_test(struct device *dev) +static int adis16400_self_test(struct device *dev) { int ret; ret = adis16400_spi_write_reg_16(dev, -- cgit v1.1 From 671ece14bc58a88dea76b11c5745a09c33934663 Mon Sep 17 00:00:00 2001 From: Barry Song Date: Fri, 7 May 2010 15:38:59 +0100 Subject: staging: iio: adis16209 driver Signed-off-by: Barry Song Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/staging/iio/accel/Kconfig | 9 + drivers/staging/iio/accel/Makefile | 4 + drivers/staging/iio/accel/adis16209.h | 193 ++++++++ drivers/staging/iio/accel/adis16209_core.c | 615 ++++++++++++++++++++++++++ drivers/staging/iio/accel/adis16209_ring.c | 266 +++++++++++ drivers/staging/iio/accel/adis16209_trigger.c | 124 ++++++ 6 files changed, 1211 insertions(+) create mode 100644 drivers/staging/iio/accel/adis16209.h create mode 100644 drivers/staging/iio/accel/adis16209_core.c create mode 100644 drivers/staging/iio/accel/adis16209_ring.c create mode 100644 drivers/staging/iio/accel/adis16209_trigger.c (limited to 'drivers/staging/iio') diff --git a/drivers/staging/iio/accel/Kconfig b/drivers/staging/iio/accel/Kconfig index 3d3c333..1d89e21 100644 --- a/drivers/staging/iio/accel/Kconfig +++ b/drivers/staging/iio/accel/Kconfig @@ -3,6 +3,15 @@ # comment "Accelerometers" +config ADIS16209 + tristate "Analog Devices ADIS16209 Dual-Axis Digital Inclinometer and Accelerometer" + depends on SPI + select IIO_TRIGGER if IIO_RING_BUFFER + select IIO_SW_RING if IIO_RING_BUFFER + help + Say yes here to build support for Analog Devices adis16209 dual-axis digital inclinometer + and accelerometer. + config KXSD9 tristate "Kionix KXSD9 Accelerometer Driver" depends on SPI diff --git a/drivers/staging/iio/accel/Makefile b/drivers/staging/iio/accel/Makefile index d5335f9..f8f2124 100644 --- a/drivers/staging/iio/accel/Makefile +++ b/drivers/staging/iio/accel/Makefile @@ -1,6 +1,10 @@ # # Makefile for industrial I/O accelerometer drivers # +adis16209-y := adis16209_core.o +adis16209-$(CONFIG_IIO_RING_BUFFER) += adis16209_ring.o adis16209_trigger.o +obj-$(CONFIG_ADIS16209) += adis16209.o + obj-$(CONFIG_KXSD9) += kxsd9.o lis3l02dq-y := lis3l02dq_core.o diff --git a/drivers/staging/iio/accel/adis16209.h b/drivers/staging/iio/accel/adis16209.h new file mode 100644 index 0000000..877fd2a --- /dev/null +++ b/drivers/staging/iio/accel/adis16209.h @@ -0,0 +1,193 @@ +#ifndef SPI_ADIS16209_H_ +#define SPI_ADIS16209_H_ + +#define ADIS16209_STARTUP_DELAY 220 /* ms */ + +#define ADIS16209_READ_REG(a) a +#define ADIS16209_WRITE_REG(a) ((a) | 0x80) + +/* Flash memory write count */ +#define ADIS16209_FLASH_CNT 0x00 +/* Output, power supply */ +#define ADIS16209_SUPPLY_OUT 0x02 +/* Output, x-axis accelerometer */ +#define ADIS16209_XACCL_OUT 0x04 +/* Output, y-axis accelerometer */ +#define ADIS16209_YACCL_OUT 0x06 +/* Output, auxiliary ADC input */ +#define ADIS16209_AUX_ADC 0x08 +/* Output, temperature */ +#define ADIS16209_TEMP_OUT 0x0A +/* Output, x-axis inclination */ +#define ADIS16209_XINCL_OUT 0x0C +/* Output, y-axis inclination */ +#define ADIS16209_YINCL_OUT 0x0E +/* Output, +/-180 vertical rotational position */ +#define ADIS16209_ROT_OUT 0x10 +/* Calibration, x-axis acceleration offset null */ +#define ADIS16209_XACCL_NULL 0x12 +/* Calibration, y-axis acceleration offset null */ +#define ADIS16209_YACCL_NULL 0x14 +/* Calibration, x-axis inclination offset null */ +#define ADIS16209_XINCL_NULL 0x16 +/* Calibration, y-axis inclination offset null */ +#define ADIS16209_YINCL_NULL 0x18 +/* Calibration, vertical rotation offset null */ +#define ADIS16209_ROT_NULL 0x1A +/* Alarm 1 amplitude threshold */ +#define ADIS16209_ALM_MAG1 0x20 +/* Alarm 2 amplitude threshold */ +#define ADIS16209_ALM_MAG2 0x22 +/* Alarm 1, sample period */ +#define ADIS16209_ALM_SMPL1 0x24 +/* Alarm 2, sample period */ +#define ADIS16209_ALM_SMPL2 0x26 +/* Alarm control */ +#define ADIS16209_ALM_CTRL 0x28 +/* Auxiliary DAC data */ +#define ADIS16209_AUX_DAC 0x30 +/* General-purpose digital input/output control */ +#define ADIS16209_GPIO_CTRL 0x32 +/* Miscellaneous control */ +#define ADIS16209_MSC_CTRL 0x34 +/* Internal sample period (rate) control */ +#define ADIS16209_SMPL_PRD 0x36 +/* Operation, filter configuration */ +#define ADIS16209_AVG_CNT 0x38 +/* Operation, sleep mode control */ +#define ADIS16209_SLP_CNT 0x3A +/* Diagnostics, system status register */ +#define ADIS16209_DIAG_STAT 0x3C +/* Operation, system command register */ +#define ADIS16209_GLOB_CMD 0x3E + +#define ADIS16209_OUTPUTS 8 + +/* MSC_CTRL */ +/* Self-test at power-on: 1 = disabled, 0 = enabled */ +#define ADIS16209_MSC_CTRL_PWRUP_SELF_TEST (1 << 10) +/* Self-test enable */ +#define ADIS16209_MSC_CTRL_SELF_TEST_EN (1 << 8) +/* Data-ready enable: 1 = enabled, 0 = disabled */ +#define ADIS16209_MSC_CTRL_DATA_RDY_EN (1 << 2) +/* Data-ready polarity: 1 = active high, 0 = active low */ +#define ADIS16209_MSC_CTRL_ACTIVE_HIGH (1 << 1) +/* Data-ready line selection: 1 = DIO2, 0 = DIO1 */ +#define ADIS16209_MSC_CTRL_DATA_RDY_DIO2 (1 << 0) + +/* DIAG_STAT */ +/* Alarm 2 status: 1 = alarm active, 0 = alarm inactive */ +#define ADIS16209_DIAG_STAT_ALARM2 (1<<9) +/* Alarm 1 status: 1 = alarm active, 0 = alarm inactive */ +#define ADIS16209_DIAG_STAT_ALARM1 (1<<8) +/* Self-test diagnostic error flag: 1 = error condition, 0 = normal operation */ +#define ADIS16209_DIAG_STAT_SELFTEST_FAIL (1<<5) +/* SPI communications failure */ +#define ADIS16209_DIAG_STAT_SPI_FAIL (1<<3) +/* Flash update failure */ +#define ADIS16209_DIAG_STAT_FLASH_UPT (1<<2) +/* Power supply above 3.625 V */ +#define ADIS16209_DIAG_STAT_POWER_HIGH (1<<1) +/* Power supply below 3.15 V */ +#define ADIS16209_DIAG_STAT_POWER_LOW (1<<0) + +/* GLOB_CMD */ +#define ADIS16209_GLOB_CMD_SW_RESET (1<<7) +#define ADIS16209_GLOB_CMD_CLEAR_STAT (1<<4) +#define ADIS16209_GLOB_CMD_FACTORY_CAL (1<<1) + +#define ADIS16209_MAX_TX 24 +#define ADIS16209_MAX_RX 24 + +#define ADIS16209_ERROR_ACTIVE (1<<14) + +/** + * struct adis16209_state - device instance specific data + * @us: actual spi_device + * @work_trigger_to_ring: bh for triggered event handling + * @work_cont_thresh: CLEAN + * @inter: used to check if new interrupt has been triggered + * @last_timestamp: passing timestamp from th to bh of interrupt handler + * @indio_dev: industrial I/O device structure + * @trig: data ready trigger registered with iio + * @tx: transmit buffer + * @rx: recieve buffer + * @buf_lock: mutex to protect tx and rx + **/ +struct adis16209_state { + struct spi_device *us; + struct work_struct work_trigger_to_ring; + struct iio_work_cont work_cont_thresh; + s64 last_timestamp; + struct iio_dev *indio_dev; + struct iio_trigger *trig; + u8 *tx; + u8 *rx; + struct mutex buf_lock; +}; + +int adis16209_set_irq(struct device *dev, bool enable); + +#ifdef CONFIG_IIO_RING_BUFFER +enum adis16209_scan { + ADIS16209_SCAN_SUPPLY, + ADIS16209_SCAN_ACC_X, + ADIS16209_SCAN_ACC_Y, + ADIS16209_SCAN_AUX_ADC, + ADIS16209_SCAN_TEMP, + ADIS16209_SCAN_INCLI_X, + ADIS16209_SCAN_INCLI_Y, + ADIS16209_SCAN_ROT, +}; + +void adis16209_remove_trigger(struct iio_dev *indio_dev); +int adis16209_probe_trigger(struct iio_dev *indio_dev); + +ssize_t adis16209_read_data_from_ring(struct device *dev, + struct device_attribute *attr, + char *buf); + +int adis16209_configure_ring(struct iio_dev *indio_dev); +void adis16209_unconfigure_ring(struct iio_dev *indio_dev); + +int adis16209_initialize_ring(struct iio_ring_buffer *ring); +void adis16209_uninitialize_ring(struct iio_ring_buffer *ring); +#else /* CONFIG_IIO_RING_BUFFER */ + +static inline void adis16209_remove_trigger(struct iio_dev *indio_dev) +{ +} + +static inline int adis16209_probe_trigger(struct iio_dev *indio_dev) +{ + return 0; +} + +static inline ssize_t +adis16209_read_data_from_ring(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return 0; +} + +static int adis16209_configure_ring(struct iio_dev *indio_dev) +{ + return 0; +} + +static inline void adis16209_unconfigure_ring(struct iio_dev *indio_dev) +{ +} + +static inline int adis16209_initialize_ring(struct iio_ring_buffer *ring) +{ + return 0; +} + +static inline void adis16209_uninitialize_ring(struct iio_ring_buffer *ring) +{ +} + +#endif /* CONFIG_IIO_RING_BUFFER */ +#endif /* SPI_ADIS16209_H_ */ diff --git a/drivers/staging/iio/accel/adis16209_core.c b/drivers/staging/iio/accel/adis16209_core.c new file mode 100644 index 0000000..ac375c5 --- /dev/null +++ b/drivers/staging/iio/accel/adis16209_core.c @@ -0,0 +1,615 @@ +/* + * ADIS16209 Programmable Digital Vibration Sensor driver + * + * Copyright 2010 Analog Devices Inc. + * + * Licensed under the GPL-2 or later. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "../iio.h" +#include "../sysfs.h" +#include "accel.h" +#include "inclinometer.h" +#include "../gyro/gyro.h" +#include "../adc/adc.h" + +#include "adis16209.h" + +#define DRIVER_NAME "adis16209" + +static int adis16209_check_status(struct device *dev); + +/** + * adis16209_spi_write_reg_8() - write single byte to a register + * @dev: device associated with child of actual device (iio_dev or iio_trig) + * @reg_address: the address of the register to be written + * @val: the value to write + **/ +static int adis16209_spi_write_reg_8(struct device *dev, + u8 reg_address, + u8 val) +{ + int ret; + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct adis16209_state *st = iio_dev_get_devdata(indio_dev); + + mutex_lock(&st->buf_lock); + st->tx[0] = ADIS16209_WRITE_REG(reg_address); + st->tx[1] = val; + + ret = spi_write(st->us, st->tx, 2); + mutex_unlock(&st->buf_lock); + + return ret; +} + +/** + * adis16209_spi_write_reg_16() - write 2 bytes to a pair of registers + * @dev: device associated with child of actual device (iio_dev or iio_trig) + * @reg_address: the address of the lower of the two registers. Second register + * is assumed to have address one greater. + * @val: value to be written + **/ +static int adis16209_spi_write_reg_16(struct device *dev, + u8 lower_reg_address, + u16 value) +{ + int ret; + struct spi_message msg; + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct adis16209_state *st = iio_dev_get_devdata(indio_dev); + struct spi_transfer xfers[] = { + { + .tx_buf = st->tx, + .bits_per_word = 8, + .len = 2, + .cs_change = 1, + }, { + .tx_buf = st->tx + 2, + .bits_per_word = 8, + .len = 2, + .cs_change = 1, + }, + }; + + mutex_lock(&st->buf_lock); + st->tx[0] = ADIS16209_WRITE_REG(lower_reg_address); + st->tx[1] = value & 0xFF; + st->tx[2] = ADIS16209_WRITE_REG(lower_reg_address + 1); + st->tx[3] = (value >> 8) & 0xFF; + + spi_message_init(&msg); + spi_message_add_tail(&xfers[0], &msg); + spi_message_add_tail(&xfers[1], &msg); + ret = spi_sync(st->us, &msg); + mutex_unlock(&st->buf_lock); + + return ret; +} + +/** + * adis16209_spi_read_reg_16() - read 2 bytes from a 16-bit register + * @dev: device associated with child of actual device (iio_dev or iio_trig) + * @reg_address: the address of the lower of the two registers. Second register + * is assumed to have address one greater. + * @val: somewhere to pass back the value read + **/ +static int adis16209_spi_read_reg_16(struct device *dev, + u8 lower_reg_address, + u16 *val) +{ + struct spi_message msg; + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct adis16209_state *st = iio_dev_get_devdata(indio_dev); + int ret; + struct spi_transfer xfers[] = { + { + .tx_buf = st->tx, + .bits_per_word = 8, + .len = 2, + .cs_change = 1, + .delay_usecs = 20, + }, { + .rx_buf = st->rx, + .bits_per_word = 8, + .len = 2, + .cs_change = 1, + .delay_usecs = 20, + }, + }; + + mutex_lock(&st->buf_lock); + st->tx[0] = ADIS16209_READ_REG(lower_reg_address); + st->tx[1] = 0; + + spi_message_init(&msg); + spi_message_add_tail(&xfers[0], &msg); + spi_message_add_tail(&xfers[1], &msg); + ret = spi_sync(st->us, &msg); + if (ret) { + dev_err(&st->us->dev, + "problem when reading 16 bit register 0x%02X", + lower_reg_address); + goto error_ret; + } + *val = (st->rx[0] << 8) | st->rx[1]; + +error_ret: + mutex_unlock(&st->buf_lock); + return ret; +} + +static ssize_t adis16209_read_12bit_unsigned(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int ret; + u16 val = 0; + struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); + + ret = adis16209_spi_read_reg_16(dev, this_attr->address, &val); + if (ret) + return ret; + + if (val & ADIS16209_ERROR_ACTIVE) + adis16209_check_status(dev); + + return sprintf(buf, "%u\n", val & 0x0FFF); +} + +static ssize_t adis16209_read_14bit_unsigned(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int ret; + u16 val = 0; + struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); + + ret = adis16209_spi_read_reg_16(dev, this_attr->address, &val); + if (ret) + return ret; + + if (val & ADIS16209_ERROR_ACTIVE) + adis16209_check_status(dev); + + return sprintf(buf, "%u\n", val & 0x3FFF); +} + +static ssize_t adis16209_read_temp(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + ssize_t ret; + u16 val; + + /* Take the iio_dev status lock */ + mutex_lock(&indio_dev->mlock); + + ret = adis16209_spi_read_reg_16(dev, ADIS16209_TEMP_OUT, (u16 *)&val); + if (ret) + goto error_ret; + + if (val & ADIS16209_ERROR_ACTIVE) + adis16209_check_status(dev); + + val &= 0xFFF; + ret = sprintf(buf, "%d\n", val); + +error_ret: + mutex_unlock(&indio_dev->mlock); + return ret; +} + +static ssize_t adis16209_read_14bit_signed(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); + s16 val = 0; + ssize_t ret; + + mutex_lock(&indio_dev->mlock); + + ret = adis16209_spi_read_reg_16(dev, this_attr->address, (u16 *)&val); + if (!ret) { + if (val & ADIS16209_ERROR_ACTIVE) + adis16209_check_status(dev); + + val = ((s16)(val << 2) >> 2); + ret = sprintf(buf, "%d\n", val); + } + + mutex_unlock(&indio_dev->mlock); + + return ret; +} + +static ssize_t adis16209_write_16bit(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t len) +{ + struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); + int ret; + long val; + + ret = strict_strtol(buf, 10, &val); + if (ret) + goto error_ret; + ret = adis16209_spi_write_reg_16(dev, this_attr->address, val); + +error_ret: + return ret ? ret : len; +} + +static int adis16209_reset(struct device *dev) +{ + int ret; + ret = adis16209_spi_write_reg_8(dev, + ADIS16209_GLOB_CMD, + ADIS16209_GLOB_CMD_SW_RESET); + if (ret) + dev_err(dev, "problem resetting device"); + + return ret; +} + +static ssize_t adis16209_write_reset(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + if (len < 1) + return -EINVAL; + switch (buf[0]) { + case '1': + case 'y': + case 'Y': + return adis16209_reset(dev); + } + return -EINVAL; +} + +int adis16209_set_irq(struct device *dev, bool enable) +{ + int ret = 0; + u16 msc; + + ret = adis16209_spi_read_reg_16(dev, ADIS16209_MSC_CTRL, &msc); + if (ret) + goto error_ret; + + msc |= ADIS16209_MSC_CTRL_ACTIVE_HIGH; + msc &= ~ADIS16209_MSC_CTRL_DATA_RDY_DIO2; + if (enable) + msc |= ADIS16209_MSC_CTRL_DATA_RDY_EN; + else + msc &= ~ADIS16209_MSC_CTRL_DATA_RDY_EN; + + ret = adis16209_spi_write_reg_16(dev, ADIS16209_MSC_CTRL, msc); + +error_ret: + return ret; +} + +static int adis16209_check_status(struct device *dev) +{ + u16 status; + int ret; + + ret = adis16209_spi_read_reg_16(dev, ADIS16209_DIAG_STAT, &status); + if (ret < 0) { + dev_err(dev, "Reading status failed\n"); + goto error_ret; + } + ret = status & 0x1F; + + if (status & ADIS16209_DIAG_STAT_SELFTEST_FAIL) + dev_err(dev, "Self test failure\n"); + if (status & ADIS16209_DIAG_STAT_SPI_FAIL) + dev_err(dev, "SPI failure\n"); + if (status & ADIS16209_DIAG_STAT_FLASH_UPT) + dev_err(dev, "Flash update failed\n"); + if (status & ADIS16209_DIAG_STAT_POWER_HIGH) + dev_err(dev, "Power supply above 3.625V\n"); + if (status & ADIS16209_DIAG_STAT_POWER_LOW) + dev_err(dev, "Power supply below 3.15V\n"); + +error_ret: + return ret; +} + +static int adis16209_self_test(struct device *dev) +{ + int ret; + ret = adis16209_spi_write_reg_16(dev, + ADIS16209_MSC_CTRL, + ADIS16209_MSC_CTRL_SELF_TEST_EN); + if (ret) { + dev_err(dev, "problem starting self test"); + goto err_ret; + } + + adis16209_check_status(dev); + +err_ret: + return ret; +} + +static int adis16209_initial_setup(struct adis16209_state *st) +{ + int ret; + struct device *dev = &st->indio_dev->dev; + + /* Disable IRQ */ + ret = adis16209_set_irq(dev, false); + if (ret) { + dev_err(dev, "disable irq failed"); + goto err_ret; + } + + /* Do self test */ + ret = adis16209_self_test(dev); + if (ret) { + dev_err(dev, "self test failure"); + goto err_ret; + } + + /* Read status register to check the result */ + ret = adis16209_check_status(dev); + if (ret) { + adis16209_reset(dev); + dev_err(dev, "device not playing ball -> reset"); + msleep(ADIS16209_STARTUP_DELAY); + ret = adis16209_check_status(dev); + if (ret) { + dev_err(dev, "giving up"); + goto err_ret; + } + } + + printk(KERN_INFO DRIVER_NAME ": at CS%d (irq %d)\n", + st->us->chip_select, st->us->irq); + +err_ret: + return ret; +} + +static IIO_DEV_ATTR_IN_NAMED_RAW(supply, adis16209_read_14bit_unsigned, + ADIS16209_SUPPLY_OUT); +static IIO_CONST_ATTR(in_supply_scale, "0.30518"); +static IIO_DEV_ATTR_IN_RAW(0, adis16209_read_12bit_unsigned, + ADIS16209_AUX_ADC); +static IIO_CONST_ATTR(in0_scale, "0.6105"); + +static IIO_DEV_ATTR_ACCEL_X(adis16209_read_14bit_signed, + ADIS16209_XACCL_OUT); +static IIO_DEV_ATTR_ACCEL_Y(adis16209_read_14bit_signed, + ADIS16209_YACCL_OUT); +static IIO_DEV_ATTR_ACCEL_X_OFFSET(S_IWUSR | S_IRUGO, + adis16209_read_14bit_signed, + adis16209_write_16bit, + ADIS16209_XACCL_NULL); +static IIO_DEV_ATTR_ACCEL_Y_OFFSET(S_IWUSR | S_IRUGO, + adis16209_read_14bit_signed, + adis16209_write_16bit, + ADIS16209_YACCL_NULL); +static IIO_CONST_ATTR(accel_scale, "0.24414"); + +static IIO_DEV_ATTR_INCLI_X(adis16209_read_14bit_signed, + ADIS16209_XINCL_OUT); +static IIO_DEV_ATTR_INCLI_Y(adis16209_read_14bit_signed, + ADIS16209_YINCL_OUT); +static IIO_DEV_ATTR_INCLI_X_OFFSET(S_IWUSR | S_IRUGO, + adis16209_read_14bit_signed, + adis16209_write_16bit, + ADIS16209_XACCL_NULL); +static IIO_DEV_ATTR_INCLI_Y_OFFSET(S_IWUSR | S_IRUGO, + adis16209_read_14bit_signed, + adis16209_write_16bit, + ADIS16209_YACCL_NULL); +static IIO_CONST_ATTR(incli_scale, "0.025"); + +static IIO_DEVICE_ATTR(rot_raw, S_IRUGO, adis16209_read_14bit_signed, + NULL, ADIS16209_ROT_OUT); + +static IIO_DEV_ATTR_TEMP(adis16209_read_temp); +static IIO_CONST_ATTR(temp_offset, "25"); +static IIO_CONST_ATTR(temp_scale, "-0.47"); + +static IIO_DEVICE_ATTR(reset, S_IWUSR, NULL, adis16209_write_reset, 0); + +static IIO_CONST_ATTR(name, "adis16209"); + +static struct attribute *adis16209_event_attributes[] = { + NULL +}; + +static struct attribute_group adis16209_event_attribute_group = { + .attrs = adis16209_event_attributes, +}; + +static struct attribute *adis16209_attributes[] = { + &iio_dev_attr_in_supply_raw.dev_attr.attr, + &iio_const_attr_in_supply_scale.dev_attr.attr, + &iio_dev_attr_temp.dev_attr.attr, + &iio_const_attr_temp_offset.dev_attr.attr, + &iio_const_attr_temp_scale.dev_attr.attr, + &iio_dev_attr_reset.dev_attr.attr, + &iio_const_attr_name.dev_attr.attr, + &iio_dev_attr_in0_raw.dev_attr.attr, + &iio_const_attr_in0_scale.dev_attr.attr, + &iio_dev_attr_accel_x_raw.dev_attr.attr, + &iio_dev_attr_accel_y_raw.dev_attr.attr, + &iio_dev_attr_accel_x_offset.dev_attr.attr, + &iio_dev_attr_accel_y_offset.dev_attr.attr, + &iio_const_attr_accel_scale.dev_attr.attr, + &iio_dev_attr_incli_x_raw.dev_attr.attr, + &iio_dev_attr_incli_y_raw.dev_attr.attr, + &iio_dev_attr_incli_x_offset.dev_attr.attr, + &iio_dev_attr_incli_y_offset.dev_attr.attr, + &iio_const_attr_incli_scale.dev_attr.attr, + &iio_dev_attr_rot_raw.dev_attr.attr, + NULL +}; + +static const struct attribute_group adis16209_attribute_group = { + .attrs = adis16209_attributes, +}; + +static int __devinit adis16209_probe(struct spi_device *spi) +{ + int ret, regdone = 0; + struct adis16209_state *st = kzalloc(sizeof *st, GFP_KERNEL); + if (!st) { + ret = -ENOMEM; + goto error_ret; + } + /* this is only used for removal purposes */ + spi_set_drvdata(spi, st); + + /* Allocate the comms buffers */ + st->rx = kzalloc(sizeof(*st->rx)*ADIS16209_MAX_RX, GFP_KERNEL); + if (st->rx == NULL) { + ret = -ENOMEM; + goto error_free_st; + } + st->tx = kzalloc(sizeof(*st->tx)*ADIS16209_MAX_TX, GFP_KERNEL); + if (st->tx == NULL) { + ret = -ENOMEM; + goto error_free_rx; + } + st->us = spi; + mutex_init(&st->buf_lock); + /* setup the industrialio driver allocated elements */ + st->indio_dev = iio_allocate_device(); + if (st->indio_dev == NULL) { + ret = -ENOMEM; + goto error_free_tx; + } + + st->indio_dev->dev.parent = &spi->dev; + st->indio_dev->num_interrupt_lines = 1; + st->indio_dev->event_attrs = &adis16209_event_attribute_group; + st->indio_dev->attrs = &adis16209_attribute_group; + st->indio_dev->dev_data = (void *)(st); + st->indio_dev->driver_module = THIS_MODULE; + st->indio_dev->modes = INDIO_DIRECT_MODE; + + ret = adis16209_configure_ring(st->indio_dev); + if (ret) + goto error_free_dev; + + ret = iio_device_register(st->indio_dev); + if (ret) + goto error_unreg_ring_funcs; + regdone = 1; + + ret = adis16209_initialize_ring(st->indio_dev->ring); + if (ret) { + printk(KERN_ERR "failed to initialize the ring\n"); + goto error_unreg_ring_funcs; + } + + if (spi->irq) { + ret = iio_register_interrupt_line(spi->irq, + st->indio_dev, + 0, + IRQF_TRIGGER_RISING, + "adis16209"); + if (ret) + goto error_uninitialize_ring; + + ret = adis16209_probe_trigger(st->indio_dev); + if (ret) + goto error_unregister_line; + } + + /* Get the device into a sane initial state */ + ret = adis16209_initial_setup(st); + if (ret) + goto error_remove_trigger; + return 0; + +error_remove_trigger: + adis16209_remove_trigger(st->indio_dev); +error_unregister_line: + if (spi->irq) + iio_unregister_interrupt_line(st->indio_dev, 0); +error_uninitialize_ring: + adis16209_uninitialize_ring(st->indio_dev->ring); +error_unreg_ring_funcs: + adis16209_unconfigure_ring(st->indio_dev); +error_free_dev: + if (regdone) + iio_device_unregister(st->indio_dev); + else + iio_free_device(st->indio_dev); +error_free_tx: + kfree(st->tx); +error_free_rx: + kfree(st->rx); +error_free_st: + kfree(st); +error_ret: + return ret; +} + +static int adis16209_remove(struct spi_device *spi) +{ + struct adis16209_state *st = spi_get_drvdata(spi); + struct iio_dev *indio_dev = st->indio_dev; + + flush_scheduled_work(); + + adis16209_remove_trigger(indio_dev); + if (spi->irq) + iio_unregister_interrupt_line(indio_dev, 0); + + adis16209_uninitialize_ring(indio_dev->ring); + iio_device_unregister(indio_dev); + adis16209_unconfigure_ring(indio_dev); + kfree(st->tx); + kfree(st->rx); + kfree(st); + + return 0; +} + +static struct spi_driver adis16209_driver = { + .driver = { + .name = "adis16209", + .owner = THIS_MODULE, + }, + .probe = adis16209_probe, + .remove = __devexit_p(adis16209_remove), +}; + +static __init int adis16209_init(void) +{ + return spi_register_driver(&adis16209_driver); +} +module_init(adis16209_init); + +static __exit void adis16209_exit(void) +{ + spi_unregister_driver(&adis16209_driver); +} +module_exit(adis16209_exit); + +MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>"); +MODULE_DESCRIPTION("Analog Devices ADIS16209 Digital Vibration Sensor driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/iio/accel/adis16209_ring.c b/drivers/staging/iio/accel/adis16209_ring.c new file mode 100644 index 0000000..533e285 --- /dev/null +++ b/drivers/staging/iio/accel/adis16209_ring.c @@ -0,0 +1,266 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../iio.h" +#include "../sysfs.h" +#include "../ring_sw.h" +#include "accel.h" +#include "../trigger.h" +#include "adis16209.h" + +/** + * combine_8_to_16() utility function to munge to u8s into u16 + **/ +static inline u16 combine_8_to_16(u8 lower, u8 upper) +{ + u16 _lower = lower; + u16 _upper = upper; + return _lower | (_upper << 8); +} + +static IIO_SCAN_EL_C(supply, ADIS16209_SCAN_SUPPLY, IIO_UNSIGNED(14), + ADIS16209_SUPPLY_OUT, NULL); +static IIO_SCAN_EL_C(accel_x, ADIS16209_SCAN_ACC_X, IIO_SIGNED(14), + ADIS16209_XACCL_OUT, NULL); +static IIO_SCAN_EL_C(accel_y, ADIS16209_SCAN_ACC_Y, IIO_SIGNED(14), + ADIS16209_YACCL_OUT, NULL); +static IIO_SCAN_EL_C(aux_adc, ADIS16209_SCAN_AUX_ADC, IIO_UNSIGNED(12), + ADIS16209_AUX_ADC, NULL); +static IIO_SCAN_EL_C(temp, ADIS16209_SCAN_TEMP, IIO_UNSIGNED(12), + ADIS16209_TEMP_OUT, NULL); +static IIO_SCAN_EL_C(incli_x, ADIS16209_SCAN_INCLI_X, IIO_SIGNED(14), + ADIS16209_XINCL_OUT, NULL); +static IIO_SCAN_EL_C(incli_y, ADIS16209_SCAN_INCLI_Y, IIO_SIGNED(14), + ADIS16209_YINCL_OUT, NULL); +static IIO_SCAN_EL_C(rot, ADIS16209_SCAN_ROT, IIO_SIGNED(14), + ADIS16209_ROT_OUT, NULL); + +static IIO_SCAN_EL_TIMESTAMP(8); + +static struct attribute *adis16209_scan_el_attrs[] = { + &iio_scan_el_supply.dev_attr.attr, + &iio_scan_el_accel_x.dev_attr.attr, + &iio_scan_el_accel_y.dev_attr.attr, + &iio_scan_el_aux_adc.dev_attr.attr, + &iio_scan_el_temp.dev_attr.attr, + &iio_scan_el_incli_x.dev_attr.attr, + &iio_scan_el_incli_y.dev_attr.attr, + &iio_scan_el_rot.dev_attr.attr, + &iio_scan_el_timestamp.dev_attr.attr, + NULL, +}; + +static struct attribute_group adis16209_scan_el_group = { + .attrs = adis16209_scan_el_attrs, + .name = "scan_elements", +}; + +/** + * adis16209_poll_func_th() top half interrupt handler called by trigger + * @private_data: iio_dev + **/ +static void adis16209_poll_func_th(struct iio_dev *indio_dev) +{ + struct adis16209_state *st = iio_dev_get_devdata(indio_dev); + st->last_timestamp = indio_dev->trig->timestamp; + schedule_work(&st->work_trigger_to_ring); +} + +/** + * adis16209_read_ring_data() read data registers which will be placed into ring + * @dev: device associated with child of actual device (iio_dev or iio_trig) + * @rx: somewhere to pass back the value read + **/ +static int adis16209_read_ring_data(struct device *dev, u8 *rx) +{ + struct spi_message msg; + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct adis16209_state *st = iio_dev_get_devdata(indio_dev); + struct spi_transfer xfers[ADIS16209_OUTPUTS + 1]; + int ret; + int i; + + mutex_lock(&st->buf_lock); + + spi_message_init(&msg); + + memset(xfers, 0, sizeof(xfers)); + for (i = 0; i <= ADIS16209_OUTPUTS; i++) { + xfers[i].bits_per_word = 8; + xfers[i].cs_change = 1; + xfers[i].len = 2; + xfers[i].delay_usecs = 20; + xfers[i].tx_buf = st->tx + 2 * i; + st->tx[2 * i] + = ADIS16209_READ_REG(ADIS16209_SUPPLY_OUT + 2 * i); + st->tx[2 * i + 1] = 0; + if (i >= 1) + xfers[i].rx_buf = rx + 2 * (i - 1); + spi_message_add_tail(&xfers[i], &msg); + } + + ret = spi_sync(st->us, &msg); + if (ret) + dev_err(&st->us->dev, "problem when burst reading"); + + mutex_unlock(&st->buf_lock); + + return ret; +} + +/* Whilst this makes a lot of calls to iio_sw_ring functions - it is to device + * specific to be rolled into the core. + */ +static void adis16209_trigger_bh_to_ring(struct work_struct *work_s) +{ + struct adis16209_state *st + = container_of(work_s, struct adis16209_state, + work_trigger_to_ring); + + int i = 0; + s16 *data; + size_t datasize = st->indio_dev + ->ring->access.get_bpd(st->indio_dev->ring); + + data = kmalloc(datasize , GFP_KERNEL); + if (data == NULL) { + dev_err(&st->us->dev, "memory alloc failed in ring bh"); + return; + } + + if (st->indio_dev->scan_count) + if (adis16209_read_ring_data(&st->indio_dev->dev, st->rx) >= 0) + for (; i < st->indio_dev->scan_count; i++) { + data[i] = combine_8_to_16(st->rx[i*2+1], + st->rx[i*2]); + } + + /* Guaranteed to be aligned with 8 byte boundary */ + if (st->indio_dev->scan_timestamp) + *((s64 *)(data + ((i + 3)/4)*4)) = st->last_timestamp; + + st->indio_dev->ring->access.store_to(st->indio_dev->ring, + (u8 *)data, + st->last_timestamp); + + iio_trigger_notify_done(st->indio_dev->trig); + kfree(data); + + return; +} + +/* in these circumstances is it better to go with unaligned packing and + * deal with the cost?*/ +static int adis16209_data_rdy_ring_preenable(struct iio_dev *indio_dev) +{ + size_t size; + dev_dbg(&indio_dev->dev, "%s\n", __func__); + /* Check if there are any scan elements enabled, if not fail*/ + if (!(indio_dev->scan_count || indio_dev->scan_timestamp)) + return -EINVAL; + + if (indio_dev->ring->access.set_bpd) { + if (indio_dev->scan_timestamp) + if (indio_dev->scan_count) + /* Timestamp (aligned to s64) and data */ + size = (((indio_dev->scan_count * sizeof(s16)) + + sizeof(s64) - 1) + & ~(sizeof(s64) - 1)) + + sizeof(s64); + else /* Timestamp only */ + size = sizeof(s64); + else /* Data only */ + size = indio_dev->scan_count*sizeof(s16); + indio_dev->ring->access.set_bpd(indio_dev->ring, size); + } + + return 0; +} + +static int adis16209_data_rdy_ring_postenable(struct iio_dev *indio_dev) +{ + return indio_dev->trig + ? iio_trigger_attach_poll_func(indio_dev->trig, + indio_dev->pollfunc) + : 0; +} + +static int adis16209_data_rdy_ring_predisable(struct iio_dev *indio_dev) +{ + return indio_dev->trig + ? iio_trigger_dettach_poll_func(indio_dev->trig, + indio_dev->pollfunc) + : 0; +} + +void adis16209_unconfigure_ring(struct iio_dev *indio_dev) +{ + kfree(indio_dev->pollfunc); + iio_sw_rb_free(indio_dev->ring); +} + +int adis16209_configure_ring(struct iio_dev *indio_dev) +{ + int ret = 0; + struct adis16209_state *st = indio_dev->dev_data; + struct iio_ring_buffer *ring; + INIT_WORK(&st->work_trigger_to_ring, adis16209_trigger_bh_to_ring); + /* Set default scan mode */ + + iio_scan_mask_set(indio_dev, iio_scan_el_supply.number); + iio_scan_mask_set(indio_dev, iio_scan_el_rot.number); + iio_scan_mask_set(indio_dev, iio_scan_el_accel_x.number); + iio_scan_mask_set(indio_dev, iio_scan_el_accel_y.number); + iio_scan_mask_set(indio_dev, iio_scan_el_temp.number); + iio_scan_mask_set(indio_dev, iio_scan_el_aux_adc.number); + iio_scan_mask_set(indio_dev, iio_scan_el_incli_x.number); + iio_scan_mask_set(indio_dev, iio_scan_el_incli_y.number); + indio_dev->scan_timestamp = true; + + indio_dev->scan_el_attrs = &adis16209_scan_el_group; + + ring = iio_sw_rb_allocate(indio_dev); + if (!ring) { + ret = -ENOMEM; + return ret; + } + indio_dev->ring = ring; + /* Effectively select the ring buffer implementation */ + iio_ring_sw_register_funcs(&ring->access); + ring->preenable = &adis16209_data_rdy_ring_preenable; + ring->postenable = &adis16209_data_rdy_ring_postenable; + ring->predisable = &adis16209_data_rdy_ring_predisable; + ring->owner = THIS_MODULE; + + indio_dev->pollfunc = kzalloc(sizeof(*indio_dev->pollfunc), GFP_KERNEL); + if (indio_dev->pollfunc == NULL) { + ret = -ENOMEM; + goto error_iio_sw_rb_free;; + } + indio_dev->pollfunc->poll_func_main = &adis16209_poll_func_th; + indio_dev->pollfunc->private_data = indio_dev; + indio_dev->modes |= INDIO_RING_TRIGGERED; + return 0; + +error_iio_sw_rb_free: + iio_sw_rb_free(indio_dev->ring); + return ret; +} + +int adis16209_initialize_ring(struct iio_ring_buffer *ring) +{ + return iio_ring_buffer_register(ring, 0); +} + +void adis16209_uninitialize_ring(struct iio_ring_buffer *ring) +{ + iio_ring_buffer_unregister(ring); +} diff --git a/drivers/staging/iio/accel/adis16209_trigger.c b/drivers/staging/iio/accel/adis16209_trigger.c new file mode 100644 index 0000000..4a0507c --- /dev/null +++ b/drivers/staging/iio/accel/adis16209_trigger.c @@ -0,0 +1,124 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../iio.h" +#include "../sysfs.h" +#include "../trigger.h" +#include "adis16209.h" + +/** + * adis16209_data_rdy_trig_poll() the event handler for the data rdy trig + **/ +static int adis16209_data_rdy_trig_poll(struct iio_dev *dev_info, + int index, + s64 timestamp, + int no_test) +{ + struct adis16209_state *st = iio_dev_get_devdata(dev_info); + struct iio_trigger *trig = st->trig; + + trig->timestamp = timestamp; + iio_trigger_poll(trig); + + return IRQ_HANDLED; +} + +IIO_EVENT_SH(data_rdy_trig, &adis16209_data_rdy_trig_poll); + +static DEVICE_ATTR(name, S_IRUGO, iio_trigger_read_name, NULL); + +static struct attribute *adis16209_trigger_attrs[] = { + &dev_attr_name.attr, + NULL, +}; + +static const struct attribute_group adis16209_trigger_attr_group = { + .attrs = adis16209_trigger_attrs, +}; + +/** + * adis16209_data_rdy_trigger_set_state() set datardy interrupt state + **/ +static int adis16209_data_rdy_trigger_set_state(struct iio_trigger *trig, + bool state) +{ + struct adis16209_state *st = trig->private_data; + struct iio_dev *indio_dev = st->indio_dev; + int ret = 0; + + dev_dbg(&indio_dev->dev, "%s (%d)\n", __func__, state); + ret = adis16209_set_irq(&st->indio_dev->dev, state); + if (state == false) { + iio_remove_event_from_list(&iio_event_data_rdy_trig, + &indio_dev->interrupts[0] + ->ev_list); + flush_scheduled_work(); + } else { + iio_add_event_to_list(&iio_event_data_rdy_trig, + &indio_dev->interrupts[0]->ev_list); + } + return ret; +} + +/** + * adis16209_trig_try_reen() try renabling irq for data rdy trigger + * @trig: the datardy trigger + **/ +static int adis16209_trig_try_reen(struct iio_trigger *trig) +{ + struct adis16209_state *st = trig->private_data; + enable_irq(st->us->irq); + return 0; +} + +int adis16209_probe_trigger(struct iio_dev *indio_dev) +{ + int ret; + struct adis16209_state *st = indio_dev->dev_data; + + st->trig = iio_allocate_trigger(); + st->trig->name = kmalloc(IIO_TRIGGER_NAME_LENGTH, GFP_KERNEL); + if (!st->trig->name) { + ret = -ENOMEM; + goto error_free_trig; + } + snprintf((char *)st->trig->name, + IIO_TRIGGER_NAME_LENGTH, + "adis16209-dev%d", indio_dev->id); + st->trig->dev.parent = &st->us->dev; + st->trig->owner = THIS_MODULE; + st->trig->private_data = st; + st->trig->set_trigger_state = &adis16209_data_rdy_trigger_set_state; + st->trig->try_reenable = &adis16209_trig_try_reen; + st->trig->control_attrs = &adis16209_trigger_attr_group; + ret = iio_trigger_register(st->trig); + + /* select default trigger */ + indio_dev->trig = st->trig; + if (ret) + goto error_free_trig_name; + + return 0; + +error_free_trig_name: + kfree(st->trig->name); +error_free_trig: + iio_free_trigger(st->trig); + + return ret; +} + +void adis16209_remove_trigger(struct iio_dev *indio_dev) +{ + struct adis16209_state *state = indio_dev->dev_data; + + iio_trigger_unregister(state->trig); + kfree(state->trig->name); + iio_free_trigger(state->trig); +} -- cgit v1.1 From 2c834b4d2d30c8f8ae5365a66c50da95aed7b7ea Mon Sep 17 00:00:00 2001 From: Barry Song Date: Fri, 7 May 2010 15:39:00 +0100 Subject: staging: iio: adis16240 driver Signed-off-by: Barry Song Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/staging/iio/accel/Kconfig | 9 + drivers/staging/iio/accel/Makefile | 4 + drivers/staging/iio/accel/adis16240.h | 218 ++++++++++ drivers/staging/iio/accel/adis16240_core.c | 599 ++++++++++++++++++++++++++ drivers/staging/iio/accel/adis16240_ring.c | 254 +++++++++++ drivers/staging/iio/accel/adis16240_trigger.c | 124 ++++++ 6 files changed, 1208 insertions(+) create mode 100644 drivers/staging/iio/accel/adis16240.h create mode 100644 drivers/staging/iio/accel/adis16240_core.c create mode 100644 drivers/staging/iio/accel/adis16240_ring.c create mode 100644 drivers/staging/iio/accel/adis16240_trigger.c (limited to 'drivers/staging/iio') diff --git a/drivers/staging/iio/accel/Kconfig b/drivers/staging/iio/accel/Kconfig index 1d89e21..8f3f70f 100644 --- a/drivers/staging/iio/accel/Kconfig +++ b/drivers/staging/iio/accel/Kconfig @@ -12,6 +12,15 @@ config ADIS16209 Say yes here to build support for Analog Devices adis16209 dual-axis digital inclinometer and accelerometer. +config ADIS16240 + tristate "Analog Devices ADIS16240 Programmable Impact Sensor and Recorder" + depends on SPI + select IIO_TRIGGER if IIO_RING_BUFFER + select IIO_SW_RING if IIO_RING_BUFFER + help + Say yes here to build support for Analog Devices adis16240 programmable + impact Sensor and recorder. + config KXSD9 tristate "Kionix KXSD9 Accelerometer Driver" depends on SPI diff --git a/drivers/staging/iio/accel/Makefile b/drivers/staging/iio/accel/Makefile index f8f2124..0e6762c 100644 --- a/drivers/staging/iio/accel/Makefile +++ b/drivers/staging/iio/accel/Makefile @@ -5,6 +5,10 @@ adis16209-y := adis16209_core.o adis16209-$(CONFIG_IIO_RING_BUFFER) += adis16209_ring.o adis16209_trigger.o obj-$(CONFIG_ADIS16209) += adis16209.o +adis16240-y := adis16240_core.o +adis16240-$(CONFIG_IIO_RING_BUFFER) += adis16240_ring.o adis16240_trigger.o +obj-$(CONFIG_ADIS16240) += adis16240.o + obj-$(CONFIG_KXSD9) += kxsd9.o lis3l02dq-y := lis3l02dq_core.o diff --git a/drivers/staging/iio/accel/adis16240.h b/drivers/staging/iio/accel/adis16240.h new file mode 100644 index 0000000..dcff43c --- /dev/null +++ b/drivers/staging/iio/accel/adis16240.h @@ -0,0 +1,218 @@ +#ifndef SPI_ADIS16240_H_ +#define SPI_ADIS16240_H_ + +#define ADIS16240_STARTUP_DELAY 220 /* ms */ + +#define ADIS16240_READ_REG(a) a +#define ADIS16240_WRITE_REG(a) ((a) | 0x80) + +/* Flash memory write count */ +#define ADIS16240_FLASH_CNT 0x00 +/* Output, power supply */ +#define ADIS16240_SUPPLY_OUT 0x02 +/* Output, x-axis accelerometer */ +#define ADIS16240_XACCL_OUT 0x04 +/* Output, y-axis accelerometer */ +#define ADIS16240_YACCL_OUT 0x06 +/* Output, z-axis accelerometer */ +#define ADIS16240_ZACCL_OUT 0x08 +/* Output, auxiliary ADC input */ +#define ADIS16240_AUX_ADC 0x0A +/* Output, temperature */ +#define ADIS16240_TEMP_OUT 0x0C +/* Output, x-axis acceleration peak */ +#define ADIS16240_XPEAK_OUT 0x0E +/* Output, y-axis acceleration peak */ +#define ADIS16240_YPEAK_OUT 0x10 +/* Output, z-axis acceleration peak */ +#define ADIS16240_ZPEAK_OUT 0x12 +/* Output, sum-of-squares acceleration peak */ +#define ADIS16240_XYZPEAK_OUT 0x14 +/* Output, Capture Buffer 1, X and Y acceleration */ +#define ADIS16240_CAPT_BUF1 0x16 +/* Output, Capture Buffer 2, Z acceleration */ +#define ADIS16240_CAPT_BUF2 0x18 +/* Diagnostic, error flags */ +#define ADIS16240_DIAG_STAT 0x1A +/* Diagnostic, event counter */ +#define ADIS16240_EVNT_CNTR 0x1C +/* Diagnostic, check sum value from firmware test */ +#define ADIS16240_CHK_SUM 0x1E +/* Calibration, x-axis acceleration offset adjustment */ +#define ADIS16240_XACCL_OFF 0x20 +/* Calibration, y-axis acceleration offset adjustment */ +#define ADIS16240_YACCL_OFF 0x22 +/* Calibration, z-axis acceleration offset adjustment */ +#define ADIS16240_ZACCL_OFF 0x24 +/* Clock, hour and minute */ +#define ADIS16240_CLK_TIME 0x2E +/* Clock, month and day */ +#define ADIS16240_CLK_DATE 0x30 +/* Clock, year */ +#define ADIS16240_CLK_YEAR 0x32 +/* Wake-up setting, hour and minute */ +#define ADIS16240_WAKE_TIME 0x34 +/* Wake-up setting, month and day */ +#define ADIS16240_WAKE_DATE 0x36 +/* Alarm 1 amplitude threshold */ +#define ADIS16240_ALM_MAG1 0x38 +/* Alarm 2 amplitude threshold */ +#define ADIS16240_ALM_MAG2 0x3A +/* Alarm control */ +#define ADIS16240_ALM_CTRL 0x3C +/* Capture, external trigger control */ +#define ADIS16240_XTRIG_CTRL 0x3E +/* Capture, address pointer */ +#define ADIS16240_CAPT_PNTR 0x40 +/* Capture, configuration and control */ +#define ADIS16240_CAPT_CTRL 0x42 +/* General-purpose digital input/output control */ +#define ADIS16240_GPIO_CTRL 0x44 +/* Miscellaneous control */ +#define ADIS16240_MSC_CTRL 0x46 +/* Internal sample period (rate) control */ +#define ADIS16240_SMPL_PRD 0x48 +/* System command */ +#define ADIS16240_GLOB_CMD 0x4A + +#define ADIS16240_OUTPUTS 6 + +/* MSC_CTRL */ +/* Enables sum-of-squares output (XYZPEAK_OUT) */ +#define ADIS16240_MSC_CTRL_XYZPEAK_OUT_EN (1 << 15) +/* Enables peak tracking output (XPEAK_OUT, YPEAK_OUT, and ZPEAK_OUT) */ +#define ADIS16240_MSC_CTRL_X_Y_ZPEAK_OUT_EN (1 << 14) +/* Self-test enable: 1 = apply electrostatic force, 0 = disabled */ +#define ADIS16240_MSC_CTRL_SELF_TEST_EN (1 << 8) +/* Data-ready enable: 1 = enabled, 0 = disabled */ +#define ADIS16240_MSC_CTRL_DATA_RDY_EN (1 << 2) +/* Data-ready polarity: 1 = active high, 0 = active low */ +#define ADIS16240_MSC_CTRL_ACTIVE_HIGH (1 << 1) +/* Data-ready line selection: 1 = DIO2, 0 = DIO1 */ +#define ADIS16240_MSC_CTRL_DATA_RDY_DIO2 (1 << 0) + +/* DIAG_STAT */ +/* Alarm 2 status: 1 = alarm active, 0 = alarm inactive */ +#define ADIS16240_DIAG_STAT_ALARM2 (1<<9) +/* Alarm 1 status: 1 = alarm active, 0 = alarm inactive */ +#define ADIS16240_DIAG_STAT_ALARM1 (1<<8) +/* Capture buffer full: 1 = capture buffer is full */ +#define ADIS16240_DIAG_STAT_CPT_BUF_FUL (1<<7) +/* Flash test, checksum flag: 1 = mismatch, 0 = match */ +#define ADIS16240_DIAG_STAT_CHKSUM (1<<6) +/* Power-on, self-test flag: 1 = failure, 0 = pass */ +#define ADIS16240_DIAG_STAT_PWRON_FAIL (1<<5) +/* Power-on self-test: 1 = in-progress, 0 = complete */ +#define ADIS16240_DIAG_STAT_PWRON_BUSY (1<<4) +/* SPI communications failure */ +#define ADIS16240_DIAG_STAT_SPI_FAIL (1<<3) +/* Flash update failure */ +#define ADIS16240_DIAG_STAT_FLASH_UPT (1<<2) +/* Power supply above 3.625 V */ +#define ADIS16240_DIAG_STAT_POWER_HIGH (1<<1) + /* Power supply below 3.15 V */ +#define ADIS16240_DIAG_STAT_POWER_LOW (1<<0) + +/* GLOB_CMD */ +#define ADIS16240_GLOB_CMD_RESUME (1<<8) +#define ADIS16240_GLOB_CMD_SW_RESET (1<<7) +#define ADIS16240_GLOB_CMD_STANDBY (1<<2) + +#define ADIS16240_ERROR_ACTIVE (1<<14) + +#define ADIS16240_MAX_TX 24 +#define ADIS16240_MAX_RX 24 + +/** + * struct adis16240_state - device instance specific data + * @us: actual spi_device + * @work_trigger_to_ring: bh for triggered event handling + * @work_cont_thresh: CLEAN + * @inter: used to check if new interrupt has been triggered + * @last_timestamp: passing timestamp from th to bh of interrupt handler + * @indio_dev: industrial I/O device structure + * @trig: data ready trigger registered with iio + * @tx: transmit buffer + * @rx: recieve buffer + * @buf_lock: mutex to protect tx and rx + **/ +struct adis16240_state { + struct spi_device *us; + struct work_struct work_trigger_to_ring; + struct iio_work_cont work_cont_thresh; + s64 last_timestamp; + struct iio_dev *indio_dev; + struct iio_trigger *trig; + u8 *tx; + u8 *rx; + struct mutex buf_lock; +}; + +int adis16240_set_irq(struct device *dev, bool enable); + +#ifdef CONFIG_IIO_RING_BUFFER +/* At the moment triggers are only used for ring buffer + * filling. This may change! + */ + +enum adis16240_scan { + ADIS16240_SCAN_SUPPLY, + ADIS16240_SCAN_ACC_X, + ADIS16240_SCAN_ACC_Y, + ADIS16240_SCAN_ACC_Z, + ADIS16240_SCAN_AUX_ADC, + ADIS16240_SCAN_TEMP, +}; + +void adis16240_remove_trigger(struct iio_dev *indio_dev); +int adis16240_probe_trigger(struct iio_dev *indio_dev); + +ssize_t adis16240_read_data_from_ring(struct device *dev, + struct device_attribute *attr, + char *buf); + + +int adis16240_configure_ring(struct iio_dev *indio_dev); +void adis16240_unconfigure_ring(struct iio_dev *indio_dev); + +int adis16240_initialize_ring(struct iio_ring_buffer *ring); +void adis16240_uninitialize_ring(struct iio_ring_buffer *ring); +#else /* CONFIG_IIO_RING_BUFFER */ + +static inline void adis16240_remove_trigger(struct iio_dev *indio_dev) +{ +} + +static inline int adis16240_probe_trigger(struct iio_dev *indio_dev) +{ + return 0; +} + +static inline ssize_t +adis16240_read_data_from_ring(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return 0; +} + +static int adis16240_configure_ring(struct iio_dev *indio_dev) +{ + return 0; +} + +static inline void adis16240_unconfigure_ring(struct iio_dev *indio_dev) +{ +} + +static inline int adis16240_initialize_ring(struct iio_ring_buffer *ring) +{ + return 0; +} + +static inline void adis16240_uninitialize_ring(struct iio_ring_buffer *ring) +{ +} + +#endif /* CONFIG_IIO_RING_BUFFER */ +#endif /* SPI_ADIS16240_H_ */ diff --git a/drivers/staging/iio/accel/adis16240_core.c b/drivers/staging/iio/accel/adis16240_core.c new file mode 100644 index 0000000..54fd6d7 --- /dev/null +++ b/drivers/staging/iio/accel/adis16240_core.c @@ -0,0 +1,599 @@ +/* + * ADIS16240 Programmable Impact Sensor and Recorder driver + * + * Copyright 2010 Analog Devices Inc. + * + * Licensed under the GPL-2 or later. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "../iio.h" +#include "../sysfs.h" +#include "accel.h" +#include "../adc/adc.h" + +#include "adis16240.h" + +#define DRIVER_NAME "adis16240" + +static int adis16240_check_status(struct device *dev); + +/** + * adis16240_spi_write_reg_8() - write single byte to a register + * @dev: device associated with child of actual device (iio_dev or iio_trig) + * @reg_address: the address of the register to be written + * @val: the value to write + **/ +static int adis16240_spi_write_reg_8(struct device *dev, + u8 reg_address, + u8 val) +{ + int ret; + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct adis16240_state *st = iio_dev_get_devdata(indio_dev); + + mutex_lock(&st->buf_lock); + st->tx[0] = ADIS16240_WRITE_REG(reg_address); + st->tx[1] = val; + + ret = spi_write(st->us, st->tx, 2); + mutex_unlock(&st->buf_lock); + + return ret; +} + +/** + * adis16240_spi_write_reg_16() - write 2 bytes to a pair of registers + * @dev: device associated with child of actual device (iio_dev or iio_trig) + * @reg_address: the address of the lower of the two registers. Second register + * is assumed to have address one greater. + * @val: value to be written + **/ +static int adis16240_spi_write_reg_16(struct device *dev, + u8 lower_reg_address, + u16 value) +{ + int ret; + struct spi_message msg; + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct adis16240_state *st = iio_dev_get_devdata(indio_dev); + struct spi_transfer xfers[] = { + { + .tx_buf = st->tx, + .bits_per_word = 8, + .len = 2, + .cs_change = 1, + .delay_usecs = 25, + }, { + .tx_buf = st->tx + 2, + .bits_per_word = 8, + .len = 2, + .cs_change = 1, + .delay_usecs = 25, + }, + }; + + mutex_lock(&st->buf_lock); + st->tx[0] = ADIS16240_WRITE_REG(lower_reg_address); + st->tx[1] = value & 0xFF; + st->tx[2] = ADIS16240_WRITE_REG(lower_reg_address + 1); + st->tx[3] = (value >> 8) & 0xFF; + + spi_message_init(&msg); + spi_message_add_tail(&xfers[0], &msg); + spi_message_add_tail(&xfers[1], &msg); + ret = spi_sync(st->us, &msg); + mutex_unlock(&st->buf_lock); + + return ret; +} + +/** + * adis16240_spi_read_reg_16() - read 2 bytes from a 16-bit register + * @dev: device associated with child of actual device (iio_dev or iio_trig) + * @reg_address: the address of the lower of the two registers. Second register + * is assumed to have address one greater. + * @val: somewhere to pass back the value read + **/ +static int adis16240_spi_read_reg_16(struct device *dev, + u8 lower_reg_address, + u16 *val) +{ + struct spi_message msg; + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct adis16240_state *st = iio_dev_get_devdata(indio_dev); + int ret; + struct spi_transfer xfers[] = { + { + .tx_buf = st->tx, + .bits_per_word = 8, + .len = 2, + .cs_change = 1, + .delay_usecs = 25, + }, { + .rx_buf = st->rx, + .bits_per_word = 8, + .len = 2, + .cs_change = 1, + .delay_usecs = 25, + }, + }; + + mutex_lock(&st->buf_lock); + st->tx[0] = ADIS16240_READ_REG(lower_reg_address); + st->tx[1] = 0; + st->tx[2] = 0; + st->tx[3] = 0; + + spi_message_init(&msg); + spi_message_add_tail(&xfers[0], &msg); + spi_message_add_tail(&xfers[1], &msg); + ret = spi_sync(st->us, &msg); + if (ret) { + dev_err(&st->us->dev, + "problem when reading 16 bit register 0x%02X", + lower_reg_address); + goto error_ret; + } + *val = (st->rx[0] << 8) | st->rx[1]; + +error_ret: + mutex_unlock(&st->buf_lock); + return ret; +} + +static ssize_t adis16240_spi_read_signed(struct device *dev, + struct device_attribute *attr, + char *buf, + unsigned bits) +{ + int ret; + s16 val = 0; + unsigned shift = 16 - bits; + struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); + + ret = adis16240_spi_read_reg_16(dev, this_attr->address, (u16 *)&val); + if (ret) + return ret; + + if (val & ADIS16240_ERROR_ACTIVE) + adis16240_check_status(dev); + + val = ((s16)(val << shift) >> shift); + return sprintf(buf, "%d\n", val); +} + +static ssize_t adis16240_read_10bit_unsigned(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int ret; + u16 val = 0; + struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); + + ret = adis16240_spi_read_reg_16(dev, this_attr->address, &val); + if (ret) + return ret; + + if (val & ADIS16240_ERROR_ACTIVE) + adis16240_check_status(dev); + + return sprintf(buf, "%u\n", val & 0x03FF); +} + +static ssize_t adis16240_read_10bit_signed(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + ssize_t ret; + + /* Take the iio_dev status lock */ + mutex_lock(&indio_dev->mlock); + ret = adis16240_spi_read_signed(dev, attr, buf, 10); + mutex_unlock(&indio_dev->mlock); + + return ret; +} + +static ssize_t adis16240_read_12bit_signed(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + ssize_t ret; + + /* Take the iio_dev status lock */ + mutex_lock(&indio_dev->mlock); + ret = adis16240_spi_read_signed(dev, attr, buf, 12); + mutex_unlock(&indio_dev->mlock); + + return ret; +} + +static ssize_t adis16240_write_16bit(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t len) +{ + struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); + int ret; + long val; + + ret = strict_strtol(buf, 10, &val); + if (ret) + goto error_ret; + ret = adis16240_spi_write_reg_16(dev, this_attr->address, val); + +error_ret: + return ret ? ret : len; +} + +static int adis16240_reset(struct device *dev) +{ + int ret; + ret = adis16240_spi_write_reg_8(dev, + ADIS16240_GLOB_CMD, + ADIS16240_GLOB_CMD_SW_RESET); + if (ret) + dev_err(dev, "problem resetting device"); + + return ret; +} + +static ssize_t adis16240_write_reset(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + if (len < 1) + return -EINVAL; + switch (buf[0]) { + case '1': + case 'y': + case 'Y': + return adis16240_reset(dev); + } + return -EINVAL; +} + +int adis16240_set_irq(struct device *dev, bool enable) +{ + int ret = 0; + u16 msc; + + ret = adis16240_spi_read_reg_16(dev, ADIS16240_MSC_CTRL, &msc); + if (ret) + goto error_ret; + + msc |= ADIS16240_MSC_CTRL_ACTIVE_HIGH; + msc &= ~ADIS16240_MSC_CTRL_DATA_RDY_DIO2; + if (enable) + msc |= ADIS16240_MSC_CTRL_DATA_RDY_EN; + else + msc &= ~ADIS16240_MSC_CTRL_DATA_RDY_EN; + + ret = adis16240_spi_write_reg_16(dev, ADIS16240_MSC_CTRL, msc); + +error_ret: + return ret; +} + +static int adis16240_self_test(struct device *dev) +{ + int ret; + ret = adis16240_spi_write_reg_16(dev, + ADIS16240_MSC_CTRL, + ADIS16240_MSC_CTRL_SELF_TEST_EN); + if (ret) { + dev_err(dev, "problem starting self test"); + goto err_ret; + } + + msleep(ADIS16240_STARTUP_DELAY); + + adis16240_check_status(dev); + +err_ret: + return ret; +} + +static int adis16240_check_status(struct device *dev) +{ + u16 status; + int ret; + + ret = adis16240_spi_read_reg_16(dev, ADIS16240_DIAG_STAT, &status); + + if (ret < 0) { + dev_err(dev, "Reading status failed\n"); + goto error_ret; + } + + ret = status & 0x2F; + if (status & ADIS16240_DIAG_STAT_PWRON_FAIL) + dev_err(dev, "Power-on, self-test fail\n"); + if (status & ADIS16240_DIAG_STAT_SPI_FAIL) + dev_err(dev, "SPI failure\n"); + if (status & ADIS16240_DIAG_STAT_FLASH_UPT) + dev_err(dev, "Flash update failed\n"); + if (status & ADIS16240_DIAG_STAT_POWER_HIGH) + dev_err(dev, "Power supply above 3.625V\n"); + if (status & ADIS16240_DIAG_STAT_POWER_LOW) + dev_err(dev, "Power supply below 2.225V\n"); + +error_ret: + return ret; +} + +static int adis16240_initial_setup(struct adis16240_state *st) +{ + int ret; + struct device *dev = &st->indio_dev->dev; + + /* Disable IRQ */ + ret = adis16240_set_irq(dev, false); + if (ret) { + dev_err(dev, "disable irq failed"); + goto err_ret; + } + + /* Do self test */ + ret = adis16240_self_test(dev); + if (ret) { + dev_err(dev, "self test failure"); + goto err_ret; + } + + /* Read status register to check the result */ + ret = adis16240_check_status(dev); + if (ret) { + adis16240_reset(dev); + dev_err(dev, "device not playing ball -> reset"); + msleep(ADIS16240_STARTUP_DELAY); + ret = adis16240_check_status(dev); + if (ret) { + dev_err(dev, "giving up"); + goto err_ret; + } + } + + printk(KERN_INFO DRIVER_NAME ": at CS%d (irq %d)\n", + st->us->chip_select, st->us->irq); + +err_ret: + return ret; +} + +static IIO_DEV_ATTR_IN_NAMED_RAW(supply, adis16240_read_10bit_unsigned, + ADIS16240_SUPPLY_OUT); +static IIO_DEV_ATTR_IN_RAW(0, adis16240_read_10bit_signed, + ADIS16240_AUX_ADC); +static IIO_CONST_ATTR(in_supply_scale, "0.00488"); +static IIO_DEV_ATTR_ACCEL_X(adis16240_read_10bit_signed, + ADIS16240_XACCL_OUT); +static IIO_DEVICE_ATTR(accel_x_peak_raw, S_IRUGO, + adis16240_read_10bit_signed, NULL, + ADIS16240_XPEAK_OUT); +static IIO_DEV_ATTR_ACCEL_Y(adis16240_read_10bit_signed, + ADIS16240_YACCL_OUT); +static IIO_DEVICE_ATTR(accel_y_peak_raw, S_IRUGO, + adis16240_read_10bit_signed, NULL, + ADIS16240_YPEAK_OUT); +static IIO_DEV_ATTR_ACCEL_Z(adis16240_read_10bit_signed, + ADIS16240_ZACCL_OUT); +static IIO_DEVICE_ATTR(accel_z_peak_raw, S_IRUGO, + adis16240_read_10bit_signed, NULL, + ADIS16240_ZPEAK_OUT); + +static IIO_DEVICE_ATTR(accel_xyz_squared_peak_raw, S_IRUGO, + adis16240_read_12bit_signed, NULL, + ADIS16240_XYZPEAK_OUT); +static IIO_DEV_ATTR_ACCEL_X_OFFSET(S_IWUSR | S_IRUGO, + adis16240_read_10bit_signed, + adis16240_write_16bit, + ADIS16240_XACCL_OFF); +static IIO_DEV_ATTR_ACCEL_Y_OFFSET(S_IWUSR | S_IRUGO, + adis16240_read_10bit_signed, + adis16240_write_16bit, + ADIS16240_YACCL_OFF); +static IIO_DEV_ATTR_ACCEL_Z_OFFSET(S_IWUSR | S_IRUGO, + adis16240_read_10bit_signed, + adis16240_write_16bit, + ADIS16240_ZACCL_OFF); +static IIO_DEV_ATTR_TEMP_RAW(adis16240_read_10bit_unsigned); +static IIO_CONST_ATTR(temp_scale, "0.244"); + +static IIO_DEVICE_ATTR(reset, S_IWUSR, NULL, adis16240_write_reset, 0); + +static IIO_CONST_ATTR_AVAIL_SAMP_FREQ("4096"); + +static IIO_CONST_ATTR(name, "adis16240"); + +static struct attribute *adis16240_event_attributes[] = { + NULL +}; + +static struct attribute_group adis16240_event_attribute_group = { + .attrs = adis16240_event_attributes, +}; + +static struct attribute *adis16240_attributes[] = { + &iio_dev_attr_in_supply_raw.dev_attr.attr, + &iio_const_attr_in_supply_scale.dev_attr.attr, + &iio_dev_attr_in0_raw.dev_attr.attr, + &iio_dev_attr_accel_x_raw.dev_attr.attr, + &iio_dev_attr_accel_x_offset.dev_attr.attr, + &iio_dev_attr_accel_x_peak_raw.dev_attr.attr, + &iio_dev_attr_accel_y_raw.dev_attr.attr, + &iio_dev_attr_accel_y_offset.dev_attr.attr, + &iio_dev_attr_accel_y_peak_raw.dev_attr.attr, + &iio_dev_attr_accel_z_raw.dev_attr.attr, + &iio_dev_attr_accel_z_offset.dev_attr.attr, + &iio_dev_attr_accel_z_peak_raw.dev_attr.attr, + &iio_dev_attr_accel_xyz_squared_peak_raw.dev_attr.attr, + &iio_dev_attr_temp_raw.dev_attr.attr, + &iio_const_attr_temp_scale.dev_attr.attr, + &iio_const_attr_available_sampling_frequency.dev_attr.attr, + &iio_dev_attr_reset.dev_attr.attr, + &iio_const_attr_name.dev_attr.attr, + NULL +}; + +static const struct attribute_group adis16240_attribute_group = { + .attrs = adis16240_attributes, +}; + +static int __devinit adis16240_probe(struct spi_device *spi) +{ + int ret, regdone = 0; + struct adis16240_state *st = kzalloc(sizeof *st, GFP_KERNEL); + if (!st) { + ret = -ENOMEM; + goto error_ret; + } + /* this is only used for removal purposes */ + spi_set_drvdata(spi, st); + + /* Allocate the comms buffers */ + st->rx = kzalloc(sizeof(*st->rx)*ADIS16240_MAX_RX, GFP_KERNEL); + if (st->rx == NULL) { + ret = -ENOMEM; + goto error_free_st; + } + st->tx = kzalloc(sizeof(*st->tx)*ADIS16240_MAX_TX, GFP_KERNEL); + if (st->tx == NULL) { + ret = -ENOMEM; + goto error_free_rx; + } + st->us = spi; + mutex_init(&st->buf_lock); + /* setup the industrialio driver allocated elements */ + st->indio_dev = iio_allocate_device(); + if (st->indio_dev == NULL) { + ret = -ENOMEM; + goto error_free_tx; + } + + st->indio_dev->dev.parent = &spi->dev; + st->indio_dev->num_interrupt_lines = 1; + st->indio_dev->event_attrs = &adis16240_event_attribute_group; + st->indio_dev->attrs = &adis16240_attribute_group; + st->indio_dev->dev_data = (void *)(st); + st->indio_dev->driver_module = THIS_MODULE; + st->indio_dev->modes = INDIO_DIRECT_MODE; + + ret = adis16240_configure_ring(st->indio_dev); + if (ret) + goto error_free_dev; + + ret = iio_device_register(st->indio_dev); + if (ret) + goto error_unreg_ring_funcs; + regdone = 1; + + ret = adis16240_initialize_ring(st->indio_dev->ring); + if (ret) { + printk(KERN_ERR "failed to initialize the ring\n"); + goto error_unreg_ring_funcs; + } + + if (spi->irq) { + ret = iio_register_interrupt_line(spi->irq, + st->indio_dev, + 0, + IRQF_TRIGGER_RISING, + "adis16240"); + if (ret) + goto error_uninitialize_ring; + + ret = adis16240_probe_trigger(st->indio_dev); + if (ret) + goto error_unregister_line; + } + + /* Get the device into a sane initial state */ + ret = adis16240_initial_setup(st); + if (ret) + goto error_remove_trigger; + return 0; + +error_remove_trigger: + adis16240_remove_trigger(st->indio_dev); +error_unregister_line: + if (spi->irq) + iio_unregister_interrupt_line(st->indio_dev, 0); +error_uninitialize_ring: + adis16240_uninitialize_ring(st->indio_dev->ring); +error_unreg_ring_funcs: + adis16240_unconfigure_ring(st->indio_dev); +error_free_dev: + if (regdone) + iio_device_unregister(st->indio_dev); + else + iio_free_device(st->indio_dev); +error_free_tx: + kfree(st->tx); +error_free_rx: + kfree(st->rx); +error_free_st: + kfree(st); +error_ret: + return ret; +} + +static int adis16240_remove(struct spi_device *spi) +{ + struct adis16240_state *st = spi_get_drvdata(spi); + struct iio_dev *indio_dev = st->indio_dev; + + flush_scheduled_work(); + + adis16240_remove_trigger(indio_dev); + if (spi->irq) + iio_unregister_interrupt_line(indio_dev, 0); + + adis16240_uninitialize_ring(indio_dev->ring); + iio_device_unregister(indio_dev); + adis16240_unconfigure_ring(indio_dev); + kfree(st->tx); + kfree(st->rx); + kfree(st); + + return 0; +} + +static struct spi_driver adis16240_driver = { + .driver = { + .name = "adis16240", + .owner = THIS_MODULE, + }, + .probe = adis16240_probe, + .remove = __devexit_p(adis16240_remove), +}; + +static __init int adis16240_init(void) +{ + return spi_register_driver(&adis16240_driver); +} +module_init(adis16240_init); + +static __exit void adis16240_exit(void) +{ + spi_unregister_driver(&adis16240_driver); +} +module_exit(adis16240_exit); + +MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>"); +MODULE_DESCRIPTION("Analog Devices Programmable Impact Sensor and Recorder"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/iio/accel/adis16240_ring.c b/drivers/staging/iio/accel/adis16240_ring.c new file mode 100644 index 0000000..26b677b --- /dev/null +++ b/drivers/staging/iio/accel/adis16240_ring.c @@ -0,0 +1,254 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../iio.h" +#include "../sysfs.h" +#include "../ring_sw.h" +#include "accel.h" +#include "../trigger.h" +#include "adis16240.h" + +/** + * combine_8_to_16() utility function to munge to u8s into u16 + **/ +static inline u16 combine_8_to_16(u8 lower, u8 upper) +{ + u16 _lower = lower; + u16 _upper = upper; + return _lower | (_upper << 8); +} + +static IIO_SCAN_EL_C(supply, ADIS16240_SCAN_SUPPLY, IIO_UNSIGNED(10), + ADIS16240_SUPPLY_OUT, NULL); +static IIO_SCAN_EL_C(accel_x, ADIS16240_SCAN_ACC_X, IIO_SIGNED(10), + ADIS16240_XACCL_OUT, NULL); +static IIO_SCAN_EL_C(accel_y, ADIS16240_SCAN_ACC_Y, IIO_SIGNED(10), + ADIS16240_YACCL_OUT, NULL); +static IIO_SCAN_EL_C(accel_z, ADIS16240_SCAN_ACC_Z, IIO_SIGNED(10), + ADIS16240_ZACCL_OUT, NULL); +static IIO_SCAN_EL_C(aux_adc, ADIS16240_SCAN_AUX_ADC, IIO_UNSIGNED(10), + ADIS16240_AUX_ADC, NULL); +static IIO_SCAN_EL_C(temp, ADIS16240_SCAN_TEMP, IIO_UNSIGNED(10), + ADIS16240_TEMP_OUT, NULL); + +static IIO_SCAN_EL_TIMESTAMP(6); + +static struct attribute *adis16240_scan_el_attrs[] = { + &iio_scan_el_supply.dev_attr.attr, + &iio_scan_el_accel_x.dev_attr.attr, + &iio_scan_el_accel_y.dev_attr.attr, + &iio_scan_el_accel_z.dev_attr.attr, + &iio_scan_el_aux_adc.dev_attr.attr, + &iio_scan_el_temp.dev_attr.attr, + &iio_scan_el_timestamp.dev_attr.attr, + NULL, +}; + +static struct attribute_group adis16240_scan_el_group = { + .attrs = adis16240_scan_el_attrs, + .name = "scan_elements", +}; + +/** + * adis16240_poll_func_th() top half interrupt handler called by trigger + * @private_data: iio_dev + **/ +static void adis16240_poll_func_th(struct iio_dev *indio_dev) +{ + struct adis16240_state *st = iio_dev_get_devdata(indio_dev); + st->last_timestamp = indio_dev->trig->timestamp; + schedule_work(&st->work_trigger_to_ring); +} + +/** + * adis16240_read_ring_data() read data registers which will be placed into ring + * @dev: device associated with child of actual device (iio_dev or iio_trig) + * @rx: somewhere to pass back the value read + **/ +static int adis16240_read_ring_data(struct device *dev, u8 *rx) +{ + struct spi_message msg; + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct adis16240_state *st = iio_dev_get_devdata(indio_dev); + struct spi_transfer xfers[ADIS16240_OUTPUTS + 1]; + int ret; + int i; + + mutex_lock(&st->buf_lock); + + spi_message_init(&msg); + + memset(xfers, 0, sizeof(xfers)); + for (i = 0; i <= ADIS16240_OUTPUTS; i++) { + xfers[i].bits_per_word = 8; + xfers[i].cs_change = 1; + xfers[i].len = 2; + xfers[i].delay_usecs = 30; + xfers[i].tx_buf = st->tx + 2 * i; + st->tx[2 * i] + = ADIS16240_READ_REG(ADIS16240_SUPPLY_OUT + 2 * i); + st->tx[2 * i + 1] = 0; + if (i >= 1) + xfers[i].rx_buf = rx + 2 * (i - 1); + spi_message_add_tail(&xfers[i], &msg); + } + + ret = spi_sync(st->us, &msg); + if (ret) + dev_err(&st->us->dev, "problem when burst reading"); + + mutex_unlock(&st->buf_lock); + + return ret; +} + + +static void adis16240_trigger_bh_to_ring(struct work_struct *work_s) +{ + struct adis16240_state *st + = container_of(work_s, struct adis16240_state, + work_trigger_to_ring); + + int i = 0; + s16 *data; + size_t datasize = st->indio_dev + ->ring->access.get_bpd(st->indio_dev->ring); + + data = kmalloc(datasize , GFP_KERNEL); + if (data == NULL) { + dev_err(&st->us->dev, "memory alloc failed in ring bh"); + return; + } + + if (st->indio_dev->scan_count) + if (adis16240_read_ring_data(&st->indio_dev->dev, st->rx) >= 0) + for (; i < st->indio_dev->scan_count; i++) { + data[i] = combine_8_to_16(st->rx[i*2+1], + st->rx[i*2]); + } + + /* Guaranteed to be aligned with 8 byte boundary */ + if (st->indio_dev->scan_timestamp) + *((s64 *)(data + ((i + 3)/4)*4)) = st->last_timestamp; + + st->indio_dev->ring->access.store_to(st->indio_dev->ring, + (u8 *)data, + st->last_timestamp); + + iio_trigger_notify_done(st->indio_dev->trig); + kfree(data); + + return; +} + +static int adis16240_data_rdy_ring_preenable(struct iio_dev *indio_dev) +{ + size_t size; + dev_dbg(&indio_dev->dev, "%s\n", __func__); + /* Check if there are any scan elements enabled, if not fail*/ + if (!(indio_dev->scan_count || indio_dev->scan_timestamp)) + return -EINVAL; + + if (indio_dev->ring->access.set_bpd) { + if (indio_dev->scan_timestamp) + if (indio_dev->scan_count) + /* Timestamp (aligned sizeof(s64) and data */ + size = (((indio_dev->scan_count * sizeof(s16)) + + sizeof(s64) - 1) + & ~(sizeof(s64) - 1)) + + sizeof(s64); + else /* Timestamp only */ + size = sizeof(s64); + else /* Data only */ + size = indio_dev->scan_count*sizeof(s16); + indio_dev->ring->access.set_bpd(indio_dev->ring, size); + } + + return 0; +} + +static int adis16240_data_rdy_ring_postenable(struct iio_dev *indio_dev) +{ + return indio_dev->trig + ? iio_trigger_attach_poll_func(indio_dev->trig, + indio_dev->pollfunc) + : 0; +} + +static int adis16240_data_rdy_ring_predisable(struct iio_dev *indio_dev) +{ + return indio_dev->trig + ? iio_trigger_dettach_poll_func(indio_dev->trig, + indio_dev->pollfunc) + : 0; +} + +void adis16240_unconfigure_ring(struct iio_dev *indio_dev) +{ + kfree(indio_dev->pollfunc); + iio_sw_rb_free(indio_dev->ring); +} + +int adis16240_configure_ring(struct iio_dev *indio_dev) +{ + int ret = 0; + struct adis16240_state *st = indio_dev->dev_data; + struct iio_ring_buffer *ring; + INIT_WORK(&st->work_trigger_to_ring, adis16240_trigger_bh_to_ring); + /* Set default scan mode */ + + iio_scan_mask_set(indio_dev, iio_scan_el_supply.number); + iio_scan_mask_set(indio_dev, iio_scan_el_accel_x.number); + iio_scan_mask_set(indio_dev, iio_scan_el_accel_y.number); + iio_scan_mask_set(indio_dev, iio_scan_el_accel_z.number); + iio_scan_mask_set(indio_dev, iio_scan_el_temp.number); + iio_scan_mask_set(indio_dev, iio_scan_el_aux_adc.number); + indio_dev->scan_timestamp = true; + + indio_dev->scan_el_attrs = &adis16240_scan_el_group; + + ring = iio_sw_rb_allocate(indio_dev); + if (!ring) { + ret = -ENOMEM; + return ret; + } + indio_dev->ring = ring; + /* Effectively select the ring buffer implementation */ + iio_ring_sw_register_funcs(&ring->access); + ring->preenable = &adis16240_data_rdy_ring_preenable; + ring->postenable = &adis16240_data_rdy_ring_postenable; + ring->predisable = &adis16240_data_rdy_ring_predisable; + ring->owner = THIS_MODULE; + + indio_dev->pollfunc = kzalloc(sizeof(*indio_dev->pollfunc), GFP_KERNEL); + if (indio_dev->pollfunc == NULL) { + ret = -ENOMEM; + goto error_iio_sw_rb_free;; + } + indio_dev->pollfunc->poll_func_main = &adis16240_poll_func_th; + indio_dev->pollfunc->private_data = indio_dev; + indio_dev->modes |= INDIO_RING_TRIGGERED; + return 0; + +error_iio_sw_rb_free: + iio_sw_rb_free(indio_dev->ring); + return ret; +} + +int adis16240_initialize_ring(struct iio_ring_buffer *ring) +{ + return iio_ring_buffer_register(ring, 0); +} + +void adis16240_uninitialize_ring(struct iio_ring_buffer *ring) +{ + iio_ring_buffer_unregister(ring); +} diff --git a/drivers/staging/iio/accel/adis16240_trigger.c b/drivers/staging/iio/accel/adis16240_trigger.c new file mode 100644 index 0000000..df1312e --- /dev/null +++ b/drivers/staging/iio/accel/adis16240_trigger.c @@ -0,0 +1,124 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../iio.h" +#include "../sysfs.h" +#include "../trigger.h" +#include "adis16240.h" + +/** + * adis16240_data_rdy_trig_poll() the event handler for the data rdy trig + **/ +static int adis16240_data_rdy_trig_poll(struct iio_dev *dev_info, + int index, + s64 timestamp, + int no_test) +{ + struct adis16240_state *st = iio_dev_get_devdata(dev_info); + struct iio_trigger *trig = st->trig; + + trig->timestamp = timestamp; + iio_trigger_poll(trig); + + return IRQ_HANDLED; +} + +IIO_EVENT_SH(data_rdy_trig, &adis16240_data_rdy_trig_poll); + +static DEVICE_ATTR(name, S_IRUGO, iio_trigger_read_name, NULL); + +static struct attribute *adis16240_trigger_attrs[] = { + &dev_attr_name.attr, + NULL, +}; + +static const struct attribute_group adis16240_trigger_attr_group = { + .attrs = adis16240_trigger_attrs, +}; + +/** + * adis16240_data_rdy_trigger_set_state() set datardy interrupt state + **/ +static int adis16240_data_rdy_trigger_set_state(struct iio_trigger *trig, + bool state) +{ + struct adis16240_state *st = trig->private_data; + struct iio_dev *indio_dev = st->indio_dev; + int ret = 0; + + dev_dbg(&indio_dev->dev, "%s (%d)\n", __func__, state); + ret = adis16240_set_irq(&st->indio_dev->dev, state); + if (state == false) { + iio_remove_event_from_list(&iio_event_data_rdy_trig, + &indio_dev->interrupts[0] + ->ev_list); + flush_scheduled_work(); + } else { + iio_add_event_to_list(&iio_event_data_rdy_trig, + &indio_dev->interrupts[0]->ev_list); + } + return ret; +} + +/** + * adis16240_trig_try_reen() try renabling irq for data rdy trigger + * @trig: the datardy trigger + **/ +static int adis16240_trig_try_reen(struct iio_trigger *trig) +{ + struct adis16240_state *st = trig->private_data; + enable_irq(st->us->irq); + return 0; +} + +int adis16240_probe_trigger(struct iio_dev *indio_dev) +{ + int ret; + struct adis16240_state *st = indio_dev->dev_data; + + st->trig = iio_allocate_trigger(); + st->trig->name = kmalloc(IIO_TRIGGER_NAME_LENGTH, GFP_KERNEL); + if (!st->trig->name) { + ret = -ENOMEM; + goto error_free_trig; + } + snprintf((char *)st->trig->name, + IIO_TRIGGER_NAME_LENGTH, + "adis16240-dev%d", indio_dev->id); + st->trig->dev.parent = &st->us->dev; + st->trig->owner = THIS_MODULE; + st->trig->private_data = st; + st->trig->set_trigger_state = &adis16240_data_rdy_trigger_set_state; + st->trig->try_reenable = &adis16240_trig_try_reen; + st->trig->control_attrs = &adis16240_trigger_attr_group; + ret = iio_trigger_register(st->trig); + + /* select default trigger */ + indio_dev->trig = st->trig; + if (ret) + goto error_free_trig_name; + + return 0; + +error_free_trig_name: + kfree(st->trig->name); +error_free_trig: + iio_free_trigger(st->trig); + + return ret; +} + +void adis16240_remove_trigger(struct iio_dev *indio_dev) +{ + struct adis16240_state *state = indio_dev->dev_data; + + iio_trigger_unregister(state->trig); + kfree(state->trig->name); + iio_free_trigger(state->trig); +} -- cgit v1.1 From 089a41985c6c7e69c8fe043c0dd397da628254f5 Mon Sep 17 00:00:00 2001 From: Barry Song Date: Wed, 12 May 2010 19:34:06 +0100 Subject: staging: iio: adis16260 digital gyro driver Signed-off-by: Barry Song Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/staging/iio/Kconfig | 1 + drivers/staging/iio/Makefile | 1 + drivers/staging/iio/gyro/Kconfig | 13 + drivers/staging/iio/gyro/Makefile | 7 + drivers/staging/iio/gyro/adis16260.h | 175 +++++++ drivers/staging/iio/gyro/adis16260_core.c | 661 +++++++++++++++++++++++++++ drivers/staging/iio/gyro/adis16260_ring.c | 256 +++++++++++ drivers/staging/iio/gyro/adis16260_trigger.c | 124 +++++ drivers/staging/iio/gyro/gyro.h | 2 + 9 files changed, 1240 insertions(+) create mode 100644 drivers/staging/iio/gyro/Kconfig create mode 100644 drivers/staging/iio/gyro/Makefile create mode 100644 drivers/staging/iio/gyro/adis16260.h create mode 100644 drivers/staging/iio/gyro/adis16260_core.c create mode 100644 drivers/staging/iio/gyro/adis16260_ring.c create mode 100644 drivers/staging/iio/gyro/adis16260_trigger.c (limited to 'drivers/staging/iio') diff --git a/drivers/staging/iio/Kconfig b/drivers/staging/iio/Kconfig index ed38ef4..b0e6244 100644 --- a/drivers/staging/iio/Kconfig +++ b/drivers/staging/iio/Kconfig @@ -41,6 +41,7 @@ config IIO_TRIGGER source "drivers/staging/iio/accel/Kconfig" source "drivers/staging/iio/adc/Kconfig" +source "drivers/staging/iio/gyro/Kconfig" source "drivers/staging/iio/imu/Kconfig" source "drivers/staging/iio/light/Kconfig" diff --git a/drivers/staging/iio/Makefile b/drivers/staging/iio/Makefile index 92b81c2..3502b39 100644 --- a/drivers/staging/iio/Makefile +++ b/drivers/staging/iio/Makefile @@ -11,6 +11,7 @@ obj-$(CONFIG_IIO_SW_RING) += ring_sw.o obj-y += accel/ obj-y += adc/ +obj-y += gyro/ obj-y += imu/ obj-y += light/ diff --git a/drivers/staging/iio/gyro/Kconfig b/drivers/staging/iio/gyro/Kconfig new file mode 100644 index 0000000..c404361 --- /dev/null +++ b/drivers/staging/iio/gyro/Kconfig @@ -0,0 +1,13 @@ +# +# IIO Digital Gyroscope Sensor drivers configuration +# +comment "Digital gyroscope sensors" + +config ADIS16260 + tristate "Analog Devices ADIS16260/5 Digital Gyroscope Sensor SPI driver" + depends on SPI + select IIO_TRIGGER if IIO_RING_BUFFER + select IIO_SW_RING if IIO_RING_BUFFER + help + Say yes here to build support for Analog Devices adis16260/5 + programmable digital gyroscope sensor. diff --git a/drivers/staging/iio/gyro/Makefile b/drivers/staging/iio/gyro/Makefile new file mode 100644 index 0000000..6d2c547 --- /dev/null +++ b/drivers/staging/iio/gyro/Makefile @@ -0,0 +1,7 @@ + +# Makefile for digital gyroscope sensor drivers +# + +adis16260-y := adis16260_core.o +adis16260-$(CONFIG_IIO_RING_BUFFER) += adis16260_ring.o adis16260_trigger.o +obj-$(CONFIG_ADIS16260) += adis16260.o diff --git a/drivers/staging/iio/gyro/adis16260.h b/drivers/staging/iio/gyro/adis16260.h new file mode 100644 index 0000000..f19efb4 --- /dev/null +++ b/drivers/staging/iio/gyro/adis16260.h @@ -0,0 +1,175 @@ +#ifndef SPI_ADIS16260_H_ +#define SPI_ADIS16260_H_ + +#define ADIS16260_STARTUP_DELAY 220 /* ms */ + +#define ADIS16260_READ_REG(a) a +#define ADIS16260_WRITE_REG(a) ((a) | 0x80) + +#define ADIS16260_FLASH_CNT 0x00 /* Flash memory write count */ +#define ADIS16260_SUPPLY_OUT 0x02 /* Power supply measurement */ +#define ADIS16260_GYRO_OUT 0x04 /* X-axis gyroscope output */ +#define ADIS16260_AUX_ADC 0x0A /* analog input channel measurement */ +#define ADIS16260_TEMP_OUT 0x0C /* internal temperature measurement */ +#define ADIS16260_ANGL_OUT 0x0E /* angle displacement */ +#define ADIS16260_GYRO_OFF 0x14 /* Calibration, offset/bias adjustment */ +#define ADIS16260_GYRO_SCALE 0x16 /* Calibration, scale adjustment */ +#define ADIS16260_ALM_MAG1 0x20 /* Alarm 1 magnitude/polarity setting */ +#define ADIS16260_ALM_MAG2 0x22 /* Alarm 2 magnitude/polarity setting */ +#define ADIS16260_ALM_SMPL1 0x24 /* Alarm 1 dynamic rate of change setting */ +#define ADIS16260_ALM_SMPL2 0x26 /* Alarm 2 dynamic rate of change setting */ +#define ADIS16260_ALM_CTRL 0x28 /* Alarm control */ +#define ADIS16260_AUX_DAC 0x30 /* Auxiliary DAC data */ +#define ADIS16260_GPIO_CTRL 0x32 /* Control, digital I/O line */ +#define ADIS16260_MSC_CTRL 0x34 /* Control, data ready, self-test settings */ +#define ADIS16260_SMPL_PRD 0x36 /* Control, internal sample rate */ +#define ADIS16260_SENS_AVG 0x38 /* Control, dynamic range, filtering */ +#define ADIS16260_SLP_CNT 0x3A /* Control, sleep mode initiation */ +#define ADIS16260_DIAG_STAT 0x3C /* Diagnostic, error flags */ +#define ADIS16260_GLOB_CMD 0x3E /* Control, global commands */ +#define ADIS16260_LOT_ID1 0x52 /* Lot Identification Code 1 */ +#define ADIS16260_LOT_ID2 0x54 /* Lot Identification Code 2 */ +#define ADIS16260_PROD_ID 0x56 /* Product identifier; + * convert to decimal = 16,265/16,260 */ +#define ADIS16260_SERIAL_NUM 0x58 /* Serial number */ + +#define ADIS16260_OUTPUTS 5 + +#define ADIS16260_ERROR_ACTIVE (1<<14) +#define ADIS16260_NEW_DATA (1<<15) + +/* MSC_CTRL */ +#define ADIS16260_MSC_CTRL_MEM_TEST (1<<11) +/* Internal self-test enable */ +#define ADIS16260_MSC_CTRL_INT_SELF_TEST (1<<10) +#define ADIS16260_MSC_CTRL_NEG_SELF_TEST (1<<9) +#define ADIS16260_MSC_CTRL_POS_SELF_TEST (1<<8) +#define ADIS16260_MSC_CTRL_DATA_RDY_EN (1<<2) +#define ADIS16260_MSC_CTRL_DATA_RDY_POL_HIGH (1<<1) +#define ADIS16260_MSC_CTRL_DATA_RDY_DIO2 (1<<0) + +/* SMPL_PRD */ +/* Time base (tB): 0 = 1.953 ms, 1 = 60.54 ms */ +#define ADIS16260_SMPL_PRD_TIME_BASE (1<<7) +#define ADIS16260_SMPL_PRD_DIV_MASK 0x7F + +/* SLP_CNT */ +#define ADIS16260_SLP_CNT_POWER_OFF 0x80 + +/* DIAG_STAT */ +#define ADIS16260_DIAG_STAT_ALARM2 (1<<9) +#define ADIS16260_DIAG_STAT_ALARM1 (1<<8) +#define ADIS16260_DIAG_STAT_FLASH_CHK (1<<6) +#define ADIS16260_DIAG_STAT_SELF_TEST (1<<5) +#define ADIS16260_DIAG_STAT_OVERFLOW (1<<4) +#define ADIS16260_DIAG_STAT_SPI_FAIL (1<<3) +#define ADIS16260_DIAG_STAT_FLASH_UPT (1<<2) +#define ADIS16260_DIAG_STAT_POWER_HIGH (1<<1) +#define ADIS16260_DIAG_STAT_POWER_LOW (1<<0) + +/* GLOB_CMD */ +#define ADIS16260_GLOB_CMD_SW_RESET (1<<7) +#define ADIS16260_GLOB_CMD_FLASH_UPD (1<<3) +#define ADIS16260_GLOB_CMD_DAC_LATCH (1<<2) +#define ADIS16260_GLOB_CMD_FAC_CALIB (1<<1) +#define ADIS16260_GLOB_CMD_AUTO_NULL (1<<0) + +#define ADIS16260_MAX_TX 24 +#define ADIS16260_MAX_RX 24 + +#define ADIS16260_SPI_SLOW (u32)(300 * 1000) +#define ADIS16260_SPI_BURST (u32)(1000 * 1000) +#define ADIS16260_SPI_FAST (u32)(2000 * 1000) + +/** + * struct adis16260_state - device instance specific data + * @us: actual spi_device + * @work_trigger_to_ring: bh for triggered event handling + * @work_cont_thresh: CLEAN + * @inter: used to check if new interrupt has been triggered + * @last_timestamp: passing timestamp from th to bh of interrupt handler + * @indio_dev: industrial I/O device structure + * @trig: data ready trigger registered with iio + * @tx: transmit buffer + * @rx: recieve buffer + * @buf_lock: mutex to protect tx and rx + **/ +struct adis16260_state { + struct spi_device *us; + struct work_struct work_trigger_to_ring; + struct iio_work_cont work_cont_thresh; + s64 last_timestamp; + struct iio_dev *indio_dev; + struct iio_trigger *trig; + u8 *tx; + u8 *rx; + struct mutex buf_lock; +}; + +int adis16260_set_irq(struct device *dev, bool enable); + +#ifdef CONFIG_IIO_RING_BUFFER +/* At the moment triggers are only used for ring buffer + * filling. This may change! + */ + +enum adis16260_scan { + ADIS16260_SCAN_SUPPLY, + ADIS16260_SCAN_GYRO, + ADIS16260_SCAN_AUX_ADC, + ADIS16260_SCAN_TEMP, + ADIS16260_SCAN_ANGL, +}; + +void adis16260_remove_trigger(struct iio_dev *indio_dev); +int adis16260_probe_trigger(struct iio_dev *indio_dev); + +ssize_t adis16260_read_data_from_ring(struct device *dev, + struct device_attribute *attr, + char *buf); + + +int adis16260_configure_ring(struct iio_dev *indio_dev); +void adis16260_unconfigure_ring(struct iio_dev *indio_dev); + +int adis16260_initialize_ring(struct iio_ring_buffer *ring); +void adis16260_uninitialize_ring(struct iio_ring_buffer *ring); +#else /* CONFIG_IIO_RING_BUFFER */ + +static inline void adis16260_remove_trigger(struct iio_dev *indio_dev) +{ +} + +static inline int adis16260_probe_trigger(struct iio_dev *indio_dev) +{ + return 0; +} + +static inline ssize_t +adis16260_read_data_from_ring(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return 0; +} + +static int adis16260_configure_ring(struct iio_dev *indio_dev) +{ + return 0; +} + +static inline void adis16260_unconfigure_ring(struct iio_dev *indio_dev) +{ +} + +static inline int adis16260_initialize_ring(struct iio_ring_buffer *ring) +{ + return 0; +} + +static inline void adis16260_uninitialize_ring(struct iio_ring_buffer *ring) +{ +} + +#endif /* CONFIG_IIO_RING_BUFFER */ +#endif /* SPI_ADIS16260_H_ */ diff --git a/drivers/staging/iio/gyro/adis16260_core.c b/drivers/staging/iio/gyro/adis16260_core.c new file mode 100644 index 0000000..c93f4d5 --- /dev/null +++ b/drivers/staging/iio/gyro/adis16260_core.c @@ -0,0 +1,661 @@ +/* + * ADIS16260 Programmable Digital Gyroscope Sensor Driver + * + * Copyright 2010 Analog Devices Inc. + * + * Licensed under the GPL-2 or later. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "../iio.h" +#include "../sysfs.h" +#include "../adc/adc.h" +#include "gyro.h" + +#include "adis16260.h" + +#define DRIVER_NAME "adis16260" + +static int adis16260_check_status(struct device *dev); + +/** + * adis16260_spi_write_reg_8() - write single byte to a register + * @dev: device associated with child of actual device (iio_dev or iio_trig) + * @reg_address: the address of the register to be written + * @val: the value to write + **/ +static int adis16260_spi_write_reg_8(struct device *dev, + u8 reg_address, + u8 val) +{ + int ret; + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct adis16260_state *st = iio_dev_get_devdata(indio_dev); + + mutex_lock(&st->buf_lock); + st->tx[0] = ADIS16260_WRITE_REG(reg_address); + st->tx[1] = val; + + ret = spi_write(st->us, st->tx, 2); + mutex_unlock(&st->buf_lock); + + return ret; +} + +/** + * adis16260_spi_write_reg_16() - write 2 bytes to a pair of registers + * @dev: device associated with child of actual device (iio_dev or iio_trig) + * @reg_address: the address of the lower of the two registers. Second register + * is assumed to have address one greater. + * @val: value to be written + **/ +static int adis16260_spi_write_reg_16(struct device *dev, + u8 lower_reg_address, + u16 value) +{ + int ret; + struct spi_message msg; + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct adis16260_state *st = iio_dev_get_devdata(indio_dev); + struct spi_transfer xfers[] = { + { + .tx_buf = st->tx, + .bits_per_word = 8, + .len = 2, + .cs_change = 1, + .delay_usecs = 20, + }, { + .tx_buf = st->tx + 2, + .bits_per_word = 8, + .len = 2, + .cs_change = 1, + .delay_usecs = 20, + }, + }; + + mutex_lock(&st->buf_lock); + st->tx[0] = ADIS16260_WRITE_REG(lower_reg_address); + st->tx[1] = value & 0xFF; + st->tx[2] = ADIS16260_WRITE_REG(lower_reg_address + 1); + st->tx[3] = (value >> 8) & 0xFF; + + spi_message_init(&msg); + spi_message_add_tail(&xfers[0], &msg); + spi_message_add_tail(&xfers[1], &msg); + ret = spi_sync(st->us, &msg); + mutex_unlock(&st->buf_lock); + + return ret; +} + +/** + * adis16260_spi_read_reg_16() - read 2 bytes from a 16-bit register + * @dev: device associated with child of actual device (iio_dev or iio_trig) + * @reg_address: the address of the lower of the two registers. Second register + * is assumed to have address one greater. + * @val: somewhere to pass back the value read + **/ +static int adis16260_spi_read_reg_16(struct device *dev, + u8 lower_reg_address, + u16 *val) +{ + struct spi_message msg; + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct adis16260_state *st = iio_dev_get_devdata(indio_dev); + int ret; + struct spi_transfer xfers[] = { + { + .tx_buf = st->tx, + .bits_per_word = 8, + .len = 2, + .cs_change = 1, + .delay_usecs = 30, + }, { + .rx_buf = st->rx, + .bits_per_word = 8, + .len = 2, + .cs_change = 1, + .delay_usecs = 30, + }, + }; + + mutex_lock(&st->buf_lock); + st->tx[0] = ADIS16260_READ_REG(lower_reg_address); + st->tx[1] = 0; + st->tx[2] = 0; + st->tx[3] = 0; + + spi_message_init(&msg); + spi_message_add_tail(&xfers[0], &msg); + spi_message_add_tail(&xfers[1], &msg); + ret = spi_sync(st->us, &msg); + if (ret) { + dev_err(&st->us->dev, + "problem when reading 16 bit register 0x%02X", + lower_reg_address); + goto error_ret; + } + *val = (st->rx[0] << 8) | st->rx[1]; + +error_ret: + mutex_unlock(&st->buf_lock); + return ret; +} + +static ssize_t adis16260_spi_read_signed(struct device *dev, + struct device_attribute *attr, + char *buf, + unsigned bits) +{ + int ret; + s16 val = 0; + unsigned shift = 16 - bits; + struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); + + ret = adis16260_spi_read_reg_16(dev, this_attr->address, (u16 *)&val); + if (ret) + return ret; + + if (val & ADIS16260_ERROR_ACTIVE) + adis16260_check_status(dev); + val = ((s16)(val << shift) >> shift); + return sprintf(buf, "%d\n", val); +} + +static ssize_t adis16260_read_12bit_unsigned(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int ret; + u16 val = 0; + struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); + + ret = adis16260_spi_read_reg_16(dev, this_attr->address, &val); + if (ret) + return ret; + + if (val & ADIS16260_ERROR_ACTIVE) + adis16260_check_status(dev); + + return sprintf(buf, "%u\n", val & 0x0FFF); +} + +static ssize_t adis16260_read_12bit_signed(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + ssize_t ret; + + /* Take the iio_dev status lock */ + mutex_lock(&indio_dev->mlock); + ret = adis16260_spi_read_signed(dev, attr, buf, 12); + mutex_unlock(&indio_dev->mlock); + + return ret; +} + +static ssize_t adis16260_read_14bit_signed(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + ssize_t ret; + + /* Take the iio_dev status lock */ + mutex_lock(&indio_dev->mlock); + ret = adis16260_spi_read_signed(dev, attr, buf, 14); + mutex_unlock(&indio_dev->mlock); + + return ret; +} + +static ssize_t adis16260_write_16bit(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t len) +{ + struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); + int ret; + long val; + + ret = strict_strtol(buf, 10, &val); + if (ret) + goto error_ret; + ret = adis16260_spi_write_reg_16(dev, this_attr->address, val); + +error_ret: + return ret ? ret : len; +} + +static ssize_t adis16260_read_frequency(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int ret, len = 0; + u16 t; + int sps; + ret = adis16260_spi_read_reg_16(dev, + ADIS16260_SMPL_PRD, + &t); + if (ret) + return ret; + sps = (t & ADIS16260_SMPL_PRD_TIME_BASE) ? 66 : 2048; + sps /= (t & ADIS16260_SMPL_PRD_DIV_MASK) + 1; + len = sprintf(buf, "%d SPS\n", sps); + return len; +} + +static ssize_t adis16260_write_frequency(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t len) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct adis16260_state *st = iio_dev_get_devdata(indio_dev); + long val; + int ret; + u8 t; + + ret = strict_strtol(buf, 10, &val); + if (ret) + return ret; + + mutex_lock(&indio_dev->mlock); + + t = (2048 / val); + if (t > 0) + t--; + t &= ADIS16260_SMPL_PRD_DIV_MASK; + if ((t & ADIS16260_SMPL_PRD_DIV_MASK) >= 0x0A) + st->us->max_speed_hz = ADIS16260_SPI_SLOW; + else + st->us->max_speed_hz = ADIS16260_SPI_FAST; + + ret = adis16260_spi_write_reg_8(dev, + ADIS16260_SMPL_PRD, + t); + + mutex_unlock(&indio_dev->mlock); + + return ret ? ret : len; +} + +static int adis16260_reset(struct device *dev) +{ + int ret; + ret = adis16260_spi_write_reg_8(dev, + ADIS16260_GLOB_CMD, + ADIS16260_GLOB_CMD_SW_RESET); + if (ret) + dev_err(dev, "problem resetting device"); + + return ret; +} + +static ssize_t adis16260_write_reset(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + if (len < 1) + return -EINVAL; + switch (buf[0]) { + case '1': + case 'y': + case 'Y': + return adis16260_reset(dev); + } + return -EINVAL; +} + +int adis16260_set_irq(struct device *dev, bool enable) +{ + int ret; + u16 msc; + ret = adis16260_spi_read_reg_16(dev, ADIS16260_MSC_CTRL, &msc); + if (ret) + goto error_ret; + + msc |= ADIS16260_MSC_CTRL_DATA_RDY_POL_HIGH; + if (enable) + msc |= ADIS16260_MSC_CTRL_DATA_RDY_EN; + else + msc &= ~ADIS16260_MSC_CTRL_DATA_RDY_EN; + + ret = adis16260_spi_write_reg_16(dev, ADIS16260_MSC_CTRL, msc); + if (ret) + goto error_ret; + +error_ret: + return ret; +} + +/* Power down the device */ +static int adis16260_stop_device(struct device *dev) +{ + int ret; + u16 val = ADIS16260_SLP_CNT_POWER_OFF; + + ret = adis16260_spi_write_reg_16(dev, ADIS16260_SLP_CNT, val); + if (ret) + dev_err(dev, "problem with turning device off: SLP_CNT"); + + return ret; +} + +static int adis16260_self_test(struct device *dev) +{ + int ret; + ret = adis16260_spi_write_reg_16(dev, + ADIS16260_MSC_CTRL, + ADIS16260_MSC_CTRL_MEM_TEST); + if (ret) { + dev_err(dev, "problem starting self test"); + goto err_ret; + } + + adis16260_check_status(dev); + +err_ret: + return ret; +} + +static int adis16260_check_status(struct device *dev) +{ + u16 status; + int ret; + + ret = adis16260_spi_read_reg_16(dev, ADIS16260_DIAG_STAT, &status); + + if (ret < 0) { + dev_err(dev, "Reading status failed\n"); + goto error_ret; + } + ret = status & 0x7F; + if (status & ADIS16260_DIAG_STAT_FLASH_CHK) + dev_err(dev, "Flash checksum error\n"); + if (status & ADIS16260_DIAG_STAT_SELF_TEST) + dev_err(dev, "Self test error\n"); + if (status & ADIS16260_DIAG_STAT_OVERFLOW) + dev_err(dev, "Sensor overrange\n"); + if (status & ADIS16260_DIAG_STAT_SPI_FAIL) + dev_err(dev, "SPI failure\n"); + if (status & ADIS16260_DIAG_STAT_FLASH_UPT) + dev_err(dev, "Flash update failed\n"); + if (status & ADIS16260_DIAG_STAT_POWER_HIGH) + dev_err(dev, "Power supply above 5.25V\n"); + if (status & ADIS16260_DIAG_STAT_POWER_LOW) + dev_err(dev, "Power supply below 4.75V\n"); + +error_ret: + return ret; +} + +static int adis16260_initial_setup(struct adis16260_state *st) +{ + int ret; + struct device *dev = &st->indio_dev->dev; + + /* Disable IRQ */ + ret = adis16260_set_irq(dev, false); + if (ret) { + dev_err(dev, "disable irq failed"); + goto err_ret; + } + + /* Do self test */ + ret = adis16260_self_test(dev); + if (ret) { + dev_err(dev, "self test failure"); + goto err_ret; + } + + /* Read status register to check the result */ + ret = adis16260_check_status(dev); + if (ret) { + adis16260_reset(dev); + dev_err(dev, "device not playing ball -> reset"); + msleep(ADIS16260_STARTUP_DELAY); + ret = adis16260_check_status(dev); + if (ret) { + dev_err(dev, "giving up"); + goto err_ret; + } + } + + printk(KERN_INFO DRIVER_NAME ": at CS%d (irq %d)\n", + st->us->chip_select, st->us->irq); + +err_ret: + return ret; +} + +static IIO_DEV_ATTR_IN_NAMED_RAW(supply, + adis16260_read_12bit_unsigned, + ADIS16260_SUPPLY_OUT); +static IIO_CONST_ATTR(in_supply_scale, "0.0018315"); + +static IIO_DEV_ATTR_GYRO(adis16260_read_14bit_signed, + ADIS16260_GYRO_OUT); +static IIO_DEV_ATTR_GYRO_SCALE(S_IWUSR | S_IRUGO, + adis16260_read_14bit_signed, + adis16260_write_16bit, + ADIS16260_GYRO_SCALE); +static IIO_DEV_ATTR_GYRO_OFFSET(S_IWUSR | S_IRUGO, + adis16260_read_12bit_signed, + adis16260_write_16bit, + ADIS16260_GYRO_OFF); + +static IIO_DEV_ATTR_TEMP_RAW(adis16260_read_12bit_unsigned); +static IIO_CONST_ATTR(temp_offset, "25"); +static IIO_CONST_ATTR(temp_scale, "0.1453"); + +static IIO_DEV_ATTR_IN_RAW(0, adis16260_read_12bit_unsigned, + ADIS16260_AUX_ADC); +static IIO_CONST_ATTR(in0_scale, "0.0006105"); + +static IIO_DEV_ATTR_SAMP_FREQ(S_IWUSR | S_IRUGO, + adis16260_read_frequency, + adis16260_write_frequency); +static IIO_DEV_ATTR_ANGL(adis16260_read_14bit_signed, + ADIS16260_ANGL_OUT); + +static IIO_DEVICE_ATTR(reset, S_IWUSR, NULL, adis16260_write_reset, 0); + +static IIO_CONST_ATTR_AVAIL_SAMP_FREQ("256 2048"); + +static IIO_CONST_ATTR(name, "adis16260"); + +static struct attribute *adis16260_event_attributes[] = { + NULL +}; + +static struct attribute_group adis16260_event_attribute_group = { + .attrs = adis16260_event_attributes, +}; + +static struct attribute *adis16260_attributes[] = { + &iio_dev_attr_in_supply_raw.dev_attr.attr, + &iio_const_attr_in_supply_scale.dev_attr.attr, + &iio_dev_attr_gyro_raw.dev_attr.attr, + &iio_dev_attr_gyro_scale.dev_attr.attr, + &iio_dev_attr_gyro_offset.dev_attr.attr, + &iio_dev_attr_angl_raw.dev_attr.attr, + &iio_dev_attr_temp_raw.dev_attr.attr, + &iio_const_attr_temp_offset.dev_attr.attr, + &iio_const_attr_temp_scale.dev_attr.attr, + &iio_dev_attr_in0_raw.dev_attr.attr, + &iio_const_attr_in0_scale.dev_attr.attr, + &iio_dev_attr_sampling_frequency.dev_attr.attr, + &iio_const_attr_available_sampling_frequency.dev_attr.attr, + &iio_dev_attr_reset.dev_attr.attr, + &iio_const_attr_name.dev_attr.attr, + NULL +}; + +static const struct attribute_group adis16260_attribute_group = { + .attrs = adis16260_attributes, +}; + +static int __devinit adis16260_probe(struct spi_device *spi) +{ + int ret, regdone = 0; + struct adis16260_state *st = kzalloc(sizeof *st, GFP_KERNEL); + if (!st) { + ret = -ENOMEM; + goto error_ret; + } + /* this is only used for removal purposes */ + spi_set_drvdata(spi, st); + + /* Allocate the comms buffers */ + st->rx = kzalloc(sizeof(*st->rx)*ADIS16260_MAX_RX, GFP_KERNEL); + if (st->rx == NULL) { + ret = -ENOMEM; + goto error_free_st; + } + st->tx = kzalloc(sizeof(*st->tx)*ADIS16260_MAX_TX, GFP_KERNEL); + if (st->tx == NULL) { + ret = -ENOMEM; + goto error_free_rx; + } + st->us = spi; + mutex_init(&st->buf_lock); + /* setup the industrialio driver allocated elements */ + st->indio_dev = iio_allocate_device(); + if (st->indio_dev == NULL) { + ret = -ENOMEM; + goto error_free_tx; + } + + st->indio_dev->dev.parent = &spi->dev; + st->indio_dev->num_interrupt_lines = 1; + st->indio_dev->event_attrs = &adis16260_event_attribute_group; + st->indio_dev->attrs = &adis16260_attribute_group; + st->indio_dev->dev_data = (void *)(st); + st->indio_dev->driver_module = THIS_MODULE; + st->indio_dev->modes = INDIO_DIRECT_MODE; + + ret = adis16260_configure_ring(st->indio_dev); + if (ret) + goto error_free_dev; + + ret = iio_device_register(st->indio_dev); + if (ret) + goto error_unreg_ring_funcs; + regdone = 1; + + ret = adis16260_initialize_ring(st->indio_dev->ring); + if (ret) { + printk(KERN_ERR "failed to initialize the ring\n"); + goto error_unreg_ring_funcs; + } + + if (spi->irq) { + ret = iio_register_interrupt_line(spi->irq, + st->indio_dev, + 0, + IRQF_TRIGGER_RISING, + "adis16260"); + if (ret) + goto error_uninitialize_ring; + + ret = adis16260_probe_trigger(st->indio_dev); + if (ret) + goto error_unregister_line; + } + + /* Get the device into a sane initial state */ + ret = adis16260_initial_setup(st); + if (ret) + goto error_remove_trigger; + return 0; + +error_remove_trigger: + adis16260_remove_trigger(st->indio_dev); +error_unregister_line: + if (spi->irq) + iio_unregister_interrupt_line(st->indio_dev, 0); +error_uninitialize_ring: + adis16260_uninitialize_ring(st->indio_dev->ring); +error_unreg_ring_funcs: + adis16260_unconfigure_ring(st->indio_dev); +error_free_dev: + if (regdone) + iio_device_unregister(st->indio_dev); + else + iio_free_device(st->indio_dev); +error_free_tx: + kfree(st->tx); +error_free_rx: + kfree(st->rx); +error_free_st: + kfree(st); +error_ret: + return ret; +} + +static int adis16260_remove(struct spi_device *spi) +{ + int ret; + struct adis16260_state *st = spi_get_drvdata(spi); + struct iio_dev *indio_dev = st->indio_dev; + + ret = adis16260_stop_device(&(indio_dev->dev)); + if (ret) + goto err_ret; + + flush_scheduled_work(); + + adis16260_remove_trigger(indio_dev); + if (spi->irq) + iio_unregister_interrupt_line(indio_dev, 0); + + adis16260_uninitialize_ring(indio_dev->ring); + iio_device_unregister(indio_dev); + adis16260_unconfigure_ring(indio_dev); + kfree(st->tx); + kfree(st->rx); + kfree(st); + + return 0; + +err_ret: + return ret; +} + +static struct spi_driver adis16260_driver = { + .driver = { + .name = "adis16260", + .owner = THIS_MODULE, + }, + .probe = adis16260_probe, + .remove = __devexit_p(adis16260_remove), +}; + +static __init int adis16260_init(void) +{ + return spi_register_driver(&adis16260_driver); +} +module_init(adis16260_init); + +static __exit void adis16260_exit(void) +{ + spi_unregister_driver(&adis16260_driver); +} +module_exit(adis16260_exit); + +MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>"); +MODULE_DESCRIPTION("Analog Devices ADIS16260/5 Digital Gyroscope Sensor"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/iio/gyro/adis16260_ring.c b/drivers/staging/iio/gyro/adis16260_ring.c new file mode 100644 index 0000000..4c4390c --- /dev/null +++ b/drivers/staging/iio/gyro/adis16260_ring.c @@ -0,0 +1,256 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../iio.h" +#include "../sysfs.h" +#include "../ring_sw.h" +#include "../accel/accel.h" +#include "../trigger.h" +#include "adis16260.h" + +/** + * combine_8_to_16() utility function to munge to u8s into u16 + **/ +static inline u16 combine_8_to_16(u8 lower, u8 upper) +{ + u16 _lower = lower; + u16 _upper = upper; + return _lower | (_upper << 8); +} + +static IIO_SCAN_EL_C(supply, ADIS16260_SCAN_SUPPLY, IIO_UNSIGNED(12), + ADIS16260_SUPPLY_OUT, NULL); +static IIO_SCAN_EL_C(gyro, ADIS16260_SCAN_GYRO, IIO_SIGNED(14), + ADIS16260_GYRO_OUT, NULL); +static IIO_SCAN_EL_C(aux_adc, ADIS16260_SCAN_AUX_ADC, IIO_SIGNED(14), + ADIS16260_AUX_ADC, NULL); +static IIO_SCAN_EL_C(temp, ADIS16260_SCAN_TEMP, IIO_UNSIGNED(12), + ADIS16260_TEMP_OUT, NULL); +static IIO_SCAN_EL_C(angl, ADIS16260_SCAN_ANGL, IIO_UNSIGNED(12), + ADIS16260_ANGL_OUT, NULL); + +static IIO_SCAN_EL_TIMESTAMP(5); + +static struct attribute *adis16260_scan_el_attrs[] = { + &iio_scan_el_supply.dev_attr.attr, + &iio_scan_el_gyro.dev_attr.attr, + &iio_scan_el_aux_adc.dev_attr.attr, + &iio_scan_el_temp.dev_attr.attr, + &iio_scan_el_angl.dev_attr.attr, + &iio_scan_el_timestamp.dev_attr.attr, + NULL, +}; + +static struct attribute_group adis16260_scan_el_group = { + .attrs = adis16260_scan_el_attrs, + .name = "scan_elements", +}; + +/** + * adis16260_poll_func_th() top half interrupt handler called by trigger + * @private_data: iio_dev + **/ +static void adis16260_poll_func_th(struct iio_dev *indio_dev) +{ + struct adis16260_state *st = iio_dev_get_devdata(indio_dev); + st->last_timestamp = indio_dev->trig->timestamp; + schedule_work(&st->work_trigger_to_ring); +} + +/** + * adis16260_read_ring_data() read data registers which will be placed into ring + * @dev: device associated with child of actual device (iio_dev or iio_trig) + * @rx: somewhere to pass back the value read + **/ +static int adis16260_read_ring_data(struct device *dev, u8 *rx) +{ + struct spi_message msg; + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct adis16260_state *st = iio_dev_get_devdata(indio_dev); + struct spi_transfer xfers[ADIS16260_OUTPUTS + 1]; + int ret; + int i; + + mutex_lock(&st->buf_lock); + + spi_message_init(&msg); + + memset(xfers, 0, sizeof(xfers)); + for (i = 0; i <= ADIS16260_OUTPUTS; i++) { + xfers[i].bits_per_word = 8; + xfers[i].cs_change = 1; + xfers[i].len = 2; + xfers[i].delay_usecs = 30; + xfers[i].tx_buf = st->tx + 2 * i; + if (i < 2) /* SUPPLY_OUT:0x02 GYRO_OUT:0x04 */ + st->tx[2 * i] + = ADIS16260_READ_REG(ADIS16260_SUPPLY_OUT + + 2 * i); + else /* 0x06 to 0x09 is reserved */ + st->tx[2 * i] + = ADIS16260_READ_REG(ADIS16260_SUPPLY_OUT + + 2 * i + 4); + st->tx[2 * i + 1] = 0; + if (i >= 1) + xfers[i].rx_buf = rx + 2 * (i - 1); + spi_message_add_tail(&xfers[i], &msg); + } + + ret = spi_sync(st->us, &msg); + if (ret) + dev_err(&st->us->dev, "problem when burst reading"); + + mutex_unlock(&st->buf_lock); + + return ret; +} + + +static void adis16260_trigger_bh_to_ring(struct work_struct *work_s) +{ + struct adis16260_state *st + = container_of(work_s, struct adis16260_state, + work_trigger_to_ring); + + int i = 0; + s16 *data; + size_t datasize = st->indio_dev + ->ring->access.get_bpd(st->indio_dev->ring); + + data = kmalloc(datasize , GFP_KERNEL); + if (data == NULL) { + dev_err(&st->us->dev, "memory alloc failed in ring bh"); + return; + } + + if (st->indio_dev->scan_count) + if (adis16260_read_ring_data(&st->indio_dev->dev, st->rx) >= 0) + for (; i < st->indio_dev->scan_count; i++) { + data[i] = combine_8_to_16(st->rx[i*2+1], + st->rx[i*2]); + } + + /* Guaranteed to be aligned with 8 byte boundary */ + if (st->indio_dev->scan_timestamp) + *((s64 *)(data + ((i + 3)/4)*4)) = st->last_timestamp; + + st->indio_dev->ring->access.store_to(st->indio_dev->ring, + (u8 *)data, + st->last_timestamp); + + iio_trigger_notify_done(st->indio_dev->trig); + kfree(data); + + return; +} + +static int adis16260_data_rdy_ring_preenable(struct iio_dev *indio_dev) +{ + size_t size; + dev_dbg(&indio_dev->dev, "%s\n", __func__); + /* Check if there are any scan elements enabled, if not fail*/ + if (!(indio_dev->scan_count || indio_dev->scan_timestamp)) + return -EINVAL; + + if (indio_dev->ring->access.set_bpd) { + if (indio_dev->scan_timestamp) + if (indio_dev->scan_count) + /* Timestamp (aligned s64) and data */ + size = (((indio_dev->scan_count * sizeof(s16)) + + sizeof(s64) - 1) + & ~(sizeof(s64) - 1)) + + sizeof(s64); + else /* Timestamp only */ + size = sizeof(s64); + else /* Data only */ + size = indio_dev->scan_count*sizeof(s16); + indio_dev->ring->access.set_bpd(indio_dev->ring, size); + } + + return 0; +} + +static int adis16260_data_rdy_ring_postenable(struct iio_dev *indio_dev) +{ + return indio_dev->trig + ? iio_trigger_attach_poll_func(indio_dev->trig, + indio_dev->pollfunc) + : 0; +} + +static int adis16260_data_rdy_ring_predisable(struct iio_dev *indio_dev) +{ + return indio_dev->trig + ? iio_trigger_dettach_poll_func(indio_dev->trig, + indio_dev->pollfunc) + : 0; +} + +void adis16260_unconfigure_ring(struct iio_dev *indio_dev) +{ + kfree(indio_dev->pollfunc); + iio_sw_rb_free(indio_dev->ring); +} + +int adis16260_configure_ring(struct iio_dev *indio_dev) +{ + int ret = 0; + struct adis16260_state *st = indio_dev->dev_data; + struct iio_ring_buffer *ring; + INIT_WORK(&st->work_trigger_to_ring, adis16260_trigger_bh_to_ring); + /* Set default scan mode */ + + iio_scan_mask_set(indio_dev, iio_scan_el_supply.number); + iio_scan_mask_set(indio_dev, iio_scan_el_gyro.number); + iio_scan_mask_set(indio_dev, iio_scan_el_aux_adc.number); + iio_scan_mask_set(indio_dev, iio_scan_el_temp.number); + iio_scan_mask_set(indio_dev, iio_scan_el_angl.number); + indio_dev->scan_timestamp = true; + + indio_dev->scan_el_attrs = &adis16260_scan_el_group; + + ring = iio_sw_rb_allocate(indio_dev); + if (!ring) { + ret = -ENOMEM; + return ret; + } + indio_dev->ring = ring; + /* Effectively select the ring buffer implementation */ + iio_ring_sw_register_funcs(&ring->access); + ring->preenable = &adis16260_data_rdy_ring_preenable; + ring->postenable = &adis16260_data_rdy_ring_postenable; + ring->predisable = &adis16260_data_rdy_ring_predisable; + ring->owner = THIS_MODULE; + + indio_dev->pollfunc = kzalloc(sizeof(*indio_dev->pollfunc), GFP_KERNEL); + if (indio_dev->pollfunc == NULL) { + ret = -ENOMEM; + goto error_iio_sw_rb_free;; + } + indio_dev->pollfunc->poll_func_main = &adis16260_poll_func_th; + indio_dev->pollfunc->private_data = indio_dev; + indio_dev->modes |= INDIO_RING_TRIGGERED; + return 0; + +error_iio_sw_rb_free: + iio_sw_rb_free(indio_dev->ring); + return ret; +} + +int adis16260_initialize_ring(struct iio_ring_buffer *ring) +{ + return iio_ring_buffer_register(ring, 0); +} + +void adis16260_uninitialize_ring(struct iio_ring_buffer *ring) +{ + iio_ring_buffer_unregister(ring); +} diff --git a/drivers/staging/iio/gyro/adis16260_trigger.c b/drivers/staging/iio/gyro/adis16260_trigger.c new file mode 100644 index 0000000..b3c5659 --- /dev/null +++ b/drivers/staging/iio/gyro/adis16260_trigger.c @@ -0,0 +1,124 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../iio.h" +#include "../sysfs.h" +#include "../trigger.h" +#include "adis16260.h" + +/** + * adis16260_data_rdy_trig_poll() the event handler for the data rdy trig + **/ +static int adis16260_data_rdy_trig_poll(struct iio_dev *dev_info, + int index, + s64 timestamp, + int no_test) +{ + struct adis16260_state *st = iio_dev_get_devdata(dev_info); + struct iio_trigger *trig = st->trig; + + trig->timestamp = timestamp; + iio_trigger_poll(trig); + + return IRQ_HANDLED; +} + +IIO_EVENT_SH(data_rdy_trig, &adis16260_data_rdy_trig_poll); + +static DEVICE_ATTR(name, S_IRUGO, iio_trigger_read_name, NULL); + +static struct attribute *adis16260_trigger_attrs[] = { + &dev_attr_name.attr, + NULL, +}; + +static const struct attribute_group adis16260_trigger_attr_group = { + .attrs = adis16260_trigger_attrs, +}; + +/** + * adis16260_data_rdy_trigger_set_state() set datardy interrupt state + **/ +static int adis16260_data_rdy_trigger_set_state(struct iio_trigger *trig, + bool state) +{ + struct adis16260_state *st = trig->private_data; + struct iio_dev *indio_dev = st->indio_dev; + int ret = 0; + + dev_dbg(&indio_dev->dev, "%s (%d)\n", __func__, state); + ret = adis16260_set_irq(&st->indio_dev->dev, state); + if (state == false) { + iio_remove_event_from_list(&iio_event_data_rdy_trig, + &indio_dev->interrupts[0] + ->ev_list); + flush_scheduled_work(); + } else { + iio_add_event_to_list(&iio_event_data_rdy_trig, + &indio_dev->interrupts[0]->ev_list); + } + return ret; +} + +/** + * adis16260_trig_try_reen() try renabling irq for data rdy trigger + * @trig: the datardy trigger + **/ +static int adis16260_trig_try_reen(struct iio_trigger *trig) +{ + struct adis16260_state *st = trig->private_data; + enable_irq(st->us->irq); + return 0; +} + +int adis16260_probe_trigger(struct iio_dev *indio_dev) +{ + int ret; + struct adis16260_state *st = indio_dev->dev_data; + + st->trig = iio_allocate_trigger(); + st->trig->name = kmalloc(IIO_TRIGGER_NAME_LENGTH, GFP_KERNEL); + if (!st->trig->name) { + ret = -ENOMEM; + goto error_free_trig; + } + snprintf((char *)st->trig->name, + IIO_TRIGGER_NAME_LENGTH, + "adis16260-dev%d", indio_dev->id); + st->trig->dev.parent = &st->us->dev; + st->trig->owner = THIS_MODULE; + st->trig->private_data = st; + st->trig->set_trigger_state = &adis16260_data_rdy_trigger_set_state; + st->trig->try_reenable = &adis16260_trig_try_reen; + st->trig->control_attrs = &adis16260_trigger_attr_group; + ret = iio_trigger_register(st->trig); + + /* select default trigger */ + indio_dev->trig = st->trig; + if (ret) + goto error_free_trig_name; + + return 0; + +error_free_trig_name: + kfree(st->trig->name); +error_free_trig: + iio_free_trigger(st->trig); + + return ret; +} + +void adis16260_remove_trigger(struct iio_dev *indio_dev) +{ + struct adis16260_state *state = indio_dev->dev_data; + + iio_trigger_unregister(state->trig); + kfree(state->trig->name); + iio_free_trigger(state->trig); +} diff --git a/drivers/staging/iio/gyro/gyro.h b/drivers/staging/iio/gyro/gyro.h index 16f6ffa..f68edab 100644 --- a/drivers/staging/iio/gyro/gyro.h +++ b/drivers/staging/iio/gyro/gyro.h @@ -39,3 +39,5 @@ #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) -- cgit v1.1 From 06f1962ab475bdee3ae17afbaecee5b23f3cd5f0 Mon Sep 17 00:00:00 2001 From: Barry Song Date: Sun, 16 May 2010 21:11:37 +0100 Subject: Staging: iio: adis16220 vibration sensor driver Signed-off-by: Barry Song Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/staging/iio/accel/Kconfig | 7 + drivers/staging/iio/accel/Makefile | 3 + drivers/staging/iio/accel/accel.h | 6 + drivers/staging/iio/accel/adis16220.h | 150 +++++++ drivers/staging/iio/accel/adis16220_core.c | 664 +++++++++++++++++++++++++++++ 5 files changed, 830 insertions(+) create mode 100644 drivers/staging/iio/accel/adis16220.h create mode 100644 drivers/staging/iio/accel/adis16220_core.c (limited to 'drivers/staging/iio') diff --git a/drivers/staging/iio/accel/Kconfig b/drivers/staging/iio/accel/Kconfig index 8f3f70f..b4e57d1 100644 --- a/drivers/staging/iio/accel/Kconfig +++ b/drivers/staging/iio/accel/Kconfig @@ -12,6 +12,13 @@ config ADIS16209 Say yes here to build support for Analog Devices adis16209 dual-axis digital inclinometer and accelerometer. +config ADIS16220 + tristate "Analog Devices ADIS16220 Programmable Digital Vibration Sensor driver" + depends on SPI + help + Say yes here to build support for Analog Devices adis16220 programmable + digital vibration sensor. + config ADIS16240 tristate "Analog Devices ADIS16240 Programmable Impact Sensor and Recorder" depends on SPI diff --git a/drivers/staging/iio/accel/Makefile b/drivers/staging/iio/accel/Makefile index 0e6762c..c34b136 100644 --- a/drivers/staging/iio/accel/Makefile +++ b/drivers/staging/iio/accel/Makefile @@ -5,6 +5,9 @@ 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 diff --git a/drivers/staging/iio/accel/accel.h b/drivers/staging/iio/accel/accel.h index 059209c..1b6e37f 100644 --- a/drivers/staging/iio/accel/accel.h +++ b/drivers/staging/iio/accel/accel.h @@ -2,6 +2,9 @@ #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) @@ -20,6 +23,9 @@ #define IIO_DEV_ATTR_ACCEL_Z_GAIN(_mode, _show, _store, _addr) \ IIO_DEVICE_ATTR(accel_z_gain, _mode, _show, _store, _addr) +#define IIO_DEV_ATTR_ACCEL(_show, _addr) \ + IIO_DEVICE_ATTR(accel_raw, S_IRUGO, _show, NULL, _addr) + #define IIO_DEV_ATTR_ACCEL_X(_show, _addr) \ IIO_DEVICE_ATTR(accel_x_raw, S_IRUGO, _show, NULL, _addr) diff --git a/drivers/staging/iio/accel/adis16220.h b/drivers/staging/iio/accel/adis16220.h new file mode 100644 index 0000000..6b49f70 --- /dev/null +++ b/drivers/staging/iio/accel/adis16220.h @@ -0,0 +1,150 @@ +#ifndef SPI_ADIS16220_H_ +#define SPI_ADIS16220_H_ + +#define ADIS16220_STARTUP_DELAY 220 /* ms */ + +#define ADIS16220_READ_REG(a) a +#define ADIS16220_WRITE_REG(a) ((a) | 0x80) + +/* Flash memory write count */ +#define ADIS16220_FLASH_CNT 0x00 +/* Control, acceleration offset adjustment control */ +#define ADIS16220_ACCL_NULL 0x02 +/* Control, AIN1 offset adjustment control */ +#define ADIS16220_AIN1_NULL 0x04 +/* Control, AIN2 offset adjustment control */ +#define ADIS16220_AIN2_NULL 0x06 +/* Output, power supply during capture */ +#define ADIS16220_CAPT_SUPPLY 0x0A +/* Output, temperature during capture */ +#define ADIS16220_CAPT_TEMP 0x0C +/* Output, peak acceleration during capture */ +#define ADIS16220_CAPT_PEAKA 0x0E +/* Output, peak AIN1 level during capture */ +#define ADIS16220_CAPT_PEAK1 0x10 +/* Output, peak AIN2 level during capture */ +#define ADIS16220_CAPT_PEAK2 0x12 +/* Output, capture buffer for acceleration */ +#define ADIS16220_CAPT_BUFA 0x14 +/* Output, capture buffer for AIN1 */ +#define ADIS16220_CAPT_BUF1 0x16 +/* Output, capture buffer for AIN2 */ +#define ADIS16220_CAPT_BUF2 0x18 +/* Control, capture buffer address pointer */ +#define ADIS16220_CAPT_PNTR 0x1A +/* Control, capture control register */ +#define ADIS16220_CAPT_CTRL 0x1C +/* Control, capture period (automatic mode) */ +#define ADIS16220_CAPT_PRD 0x1E +/* Control, Alarm A, acceleration peak threshold */ +#define ADIS16220_ALM_MAGA 0x20 +/* Control, Alarm 1, AIN1 peak threshold */ +#define ADIS16220_ALM_MAG1 0x22 +/* Control, Alarm 2, AIN2 peak threshold */ +#define ADIS16220_ALM_MAG2 0x24 +/* Control, Alarm S, peak threshold */ +#define ADIS16220_ALM_MAGS 0x26 +/* Control, alarm configuration register */ +#define ADIS16220_ALM_CTRL 0x28 +/* Control, general I/O configuration */ +#define ADIS16220_GPIO_CTRL 0x32 +/* Control, self-test control, AIN configuration */ +#define ADIS16220_MSC_CTRL 0x34 +/* Control, digital I/O configuration */ +#define ADIS16220_DIO_CTRL 0x36 +/* Control, filter configuration */ +#define ADIS16220_AVG_CNT 0x38 +/* Status, system status */ +#define ADIS16220_DIAG_STAT 0x3C +/* Control, system commands */ +#define ADIS16220_GLOB_CMD 0x3E +/* Status, self-test response */ +#define ADIS16220_ST_DELTA 0x40 +/* Lot Identification Code 1 */ +#define ADIS16220_LOT_ID1 0x52 +/* Lot Identification Code 2 */ +#define ADIS16220_LOT_ID2 0x54 +/* Product identifier; convert to decimal = 16220 */ +#define ADIS16220_PROD_ID 0x56 +/* Serial number */ +#define ADIS16220_SERIAL_NUM 0x58 + +#define ADIS16220_CAPTURE_SIZE 2048 + +/* MSC_CTRL */ +#define ADIS16220_MSC_CTRL_SELF_TEST_EN (1 << 8) +#define ADIS16220_MSC_CTRL_POWER_SUP_COM_AIN1 (1 << 1) +#define ADIS16220_MSC_CTRL_POWER_SUP_COM_AIN2 (1 << 0) + +/* DIO_CTRL */ +#define ADIS16220_MSC_CTRL_DIO2_BUSY_IND (3<<4) +#define ADIS16220_MSC_CTRL_DIO1_BUSY_IND (3<<2) +#define ADIS16220_MSC_CTRL_DIO2_ACT_HIGH (1<<1) +#define ADIS16220_MSC_CTRL_DIO1_ACT_HIGH (1<<0) + +/* DIAG_STAT */ +/* AIN2 sample > ALM_MAG2 */ +#define ADIS16220_DIAG_STAT_ALM_MAG2 (1<<14) +/* AIN1 sample > ALM_MAG1 */ +#define ADIS16220_DIAG_STAT_ALM_MAG1 (1<<13) +/* Acceleration sample > ALM_MAGA */ +#define ADIS16220_DIAG_STAT_ALM_MAGA (1<<12) +/* Error condition programmed into ALM_MAGS[11:0] and ALM_CTRL[5:4] is true */ +#define ADIS16220_DIAG_STAT_ALM_MAGS (1<<11) +/* |Peak value in AIN2 data capture| > ALM_MAG2 */ +#define ADIS16220_DIAG_STAT_PEAK_AIN2 (1<<10) +/* |Peak value in AIN1 data capture| > ALM_MAG1 */ +#define ADIS16220_DIAG_STAT_PEAK_AIN1 (1<<9) +/* |Peak value in acceleration data capture| > ALM_MAGA */ +#define ADIS16220_DIAG_STAT_PEAK_ACCEL (1<<8) +/* Data ready, capture complete */ +#define ADIS16220_DIAG_STAT_DATA_RDY (1<<7) +#define ADIS16220_DIAG_STAT_FLASH_CHK (1<<6) +#define ADIS16220_DIAG_STAT_SELF_TEST (1<<5) +/* Capture period violation/interruption */ +#define ADIS16220_DIAG_STAT_VIOLATION (1<<4) +/* SPI communications failure */ +#define ADIS16220_DIAG_STAT_SPI_FAIL (1<<3) +/* Flash update failure */ +#define ADIS16220_DIAG_STAT_FLASH_UPT (1<<2) +/* Power supply above 3.625 V */ +#define ADIS16220_DIAG_STAT_POWER_HIGH (1<<1) +/* Power supply below 3.15 V */ +#define ADIS16220_DIAG_STAT_POWER_LOW (1<<0) + +/* GLOB_CMD */ +#define ADIS16220_GLOB_CMD_SW_RESET (1<<7) +#define ADIS16220_GLOB_CMD_SELF_TEST (1<<2) +#define ADIS16220_GLOB_CMD_PWR_DOWN (1<<1) + +#define ADIS16220_MAX_TX 2048 +#define ADIS16220_MAX_RX 2048 + +#define ADIS16220_SPI_BURST (u32)(1000 * 1000) +#define ADIS16220_SPI_FAST (u32)(2000 * 1000) + +/** + * struct adis16220_state - device instance specific data + * @us: actual spi_device + * @work_trigger_to_ring: bh for triggered event handling + * @work_cont_thresh: CLEAN + * @inter: used to check if new interrupt has been triggered + * @last_timestamp: passing timestamp from th to bh of interrupt handler + * @indio_dev: industrial I/O device structure + * @trig: data ready trigger registered with iio + * @tx: transmit buffer + * @rx: recieve buffer + * @buf_lock: mutex to protect tx and rx + **/ +struct adis16220_state { + struct spi_device *us; + struct iio_dev *indio_dev; + u8 *tx; + u8 *rx; + struct bin_attribute accel_bin; + struct bin_attribute adc1_bin; + struct bin_attribute adc2_bin; + struct mutex buf_lock; +}; + +#endif /* SPI_ADIS16220_H_ */ diff --git a/drivers/staging/iio/accel/adis16220_core.c b/drivers/staging/iio/accel/adis16220_core.c new file mode 100644 index 0000000..8b845d9 --- /dev/null +++ b/drivers/staging/iio/accel/adis16220_core.c @@ -0,0 +1,664 @@ +/* + * ADIS16220 Programmable Digital Vibration Sensor driver + * + * Copyright 2010 Analog Devices Inc. + * + * Licensed under the GPL-2 or later. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "../iio.h" +#include "../sysfs.h" +#include "accel.h" +#include "../adc/adc.h" + +#include "adis16220.h" + +#define DRIVER_NAME "adis16220" + +static int adis16220_check_status(struct device *dev); + +/** + * adis16220_spi_write_reg_8() - write single byte to a register + * @dev: device associated with child of actual device (iio_dev or iio_trig) + * @reg_address: the address of the register to be written + * @val: the value to write + **/ +static int adis16220_spi_write_reg_8(struct device *dev, + u8 reg_address, + u8 val) +{ + int ret; + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct adis16220_state *st = iio_dev_get_devdata(indio_dev); + + mutex_lock(&st->buf_lock); + st->tx[0] = ADIS16220_WRITE_REG(reg_address); + st->tx[1] = val; + + ret = spi_write(st->us, st->tx, 2); + mutex_unlock(&st->buf_lock); + + return ret; +} + +/** + * adis16220_spi_write_reg_16() - write 2 bytes to a pair of registers + * @dev: device associated with child of actual device (iio_dev or iio_trig) + * @reg_address: the address of the lower of the two registers. Second register + * is assumed to have address one greater. + * @val: value to be written + **/ +static int adis16220_spi_write_reg_16(struct device *dev, + u8 lower_reg_address, + u16 value) +{ + int ret; + struct spi_message msg; + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct adis16220_state *st = iio_dev_get_devdata(indio_dev); + struct spi_transfer xfers[] = { + { + .tx_buf = st->tx, + .bits_per_word = 8, + .len = 2, + .cs_change = 1, + .delay_usecs = 25, + }, { + .tx_buf = st->tx + 2, + .bits_per_word = 8, + .len = 2, + .cs_change = 1, + .delay_usecs = 25, + }, + }; + + mutex_lock(&st->buf_lock); + st->tx[0] = ADIS16220_WRITE_REG(lower_reg_address); + st->tx[1] = value & 0xFF; + st->tx[2] = ADIS16220_WRITE_REG(lower_reg_address + 1); + st->tx[3] = (value >> 8) & 0xFF; + + spi_message_init(&msg); + spi_message_add_tail(&xfers[0], &msg); + spi_message_add_tail(&xfers[1], &msg); + ret = spi_sync(st->us, &msg); + mutex_unlock(&st->buf_lock); + + return ret; +} + +/** + * adis16220_spi_read_reg_16() - read 2 bytes from a 16-bit register + * @dev: device associated with child of actual device (iio_dev or iio_trig) + * @reg_address: the address of the lower of the two registers. Second register + * is assumed to have address one greater. + * @val: somewhere to pass back the value read + **/ +static int adis16220_spi_read_reg_16(struct device *dev, + u8 lower_reg_address, + u16 *val) +{ + struct spi_message msg; + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct adis16220_state *st = iio_dev_get_devdata(indio_dev); + int ret; + struct spi_transfer xfers[] = { + { + .tx_buf = st->tx, + .bits_per_word = 8, + .len = 2, + .cs_change = 1, + .delay_usecs = 25, + }, { + .rx_buf = st->rx, + .bits_per_word = 8, + .len = 2, + .cs_change = 1, + .delay_usecs = 25, + }, + }; + + mutex_lock(&st->buf_lock); + st->tx[0] = ADIS16220_READ_REG(lower_reg_address); + st->tx[1] = 0; + 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 adis16220_spi_read_signed(struct device *dev, + struct device_attribute *attr, + char *buf, + unsigned bits) +{ + int ret; + s16 val = 0; + unsigned shift = 16 - bits; + struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); + + ret = adis16220_spi_read_reg_16(dev, this_attr->address, (u16 *)&val); + if (ret) + return ret; + + val = ((s16)(val << shift) >> shift); + return sprintf(buf, "%d\n", val); +} + +static ssize_t adis16220_read_12bit_unsigned(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int ret; + u16 val = 0; + struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); + + ret = adis16220_spi_read_reg_16(dev, this_attr->address, &val); + if (ret) + return ret; + + return sprintf(buf, "%u\n", val & 0x0FFF); +} + +static ssize_t adis16220_read_16bit(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + ssize_t ret; + + /* Take the iio_dev status lock */ + mutex_lock(&indio_dev->mlock); + ret = adis16220_spi_read_signed(dev, attr, buf, 16); + mutex_unlock(&indio_dev->mlock); + + return ret; +} + +static ssize_t adis16220_write_16bit(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t len) +{ + struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); + int ret; + long val; + + ret = strict_strtol(buf, 10, &val); + if (ret) + goto error_ret; + ret = adis16220_spi_write_reg_16(dev, this_attr->address, val); + +error_ret: + return ret ? ret : len; +} + +static int adis16220_capture(struct device *dev) +{ + int ret; + ret = adis16220_spi_write_reg_16(dev, + ADIS16220_GLOB_CMD, + 0xBF08); /* initiates a manual data capture */ + if (ret) + dev_err(dev, "problem beginning capture"); + + msleep(10); /* delay for capture to finish */ + + return ret; +} + +static int adis16220_reset(struct device *dev) +{ + int ret; + ret = adis16220_spi_write_reg_8(dev, + ADIS16220_GLOB_CMD, + ADIS16220_GLOB_CMD_SW_RESET); + if (ret) + dev_err(dev, "problem resetting device"); + + return ret; +} + +static ssize_t adis16220_write_reset(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + if (len < 1) + return -1; + switch (buf[0]) { + case '1': + case 'y': + case 'Y': + return adis16220_reset(dev) == 0 ? len : -EIO; + } + return -1; +} + +static ssize_t adis16220_write_capture(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + if (len < 1) + return -1; + switch (buf[0]) { + case '1': + case 'y': + case 'Y': + return adis16220_capture(dev) == 0 ? len : -EIO; + } + return -1; +} + +static int adis16220_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_check_status(struct device *dev) +{ + u16 status; + int ret; + + ret = adis16220_spi_read_reg_16(dev, ADIS16220_DIAG_STAT, &status); + + if (ret < 0) { + dev_err(dev, "Reading status failed\n"); + goto error_ret; + } + ret = status & 0x7F; + + if (status & ADIS16220_DIAG_STAT_VIOLATION) + dev_err(dev, "Capture period violation/interruption\n"); + if (status & ADIS16220_DIAG_STAT_SPI_FAIL) + dev_err(dev, "SPI failure\n"); + if (status & ADIS16220_DIAG_STAT_FLASH_UPT) + dev_err(dev, "Flash update failed\n"); + if (status & ADIS16220_DIAG_STAT_POWER_HIGH) + dev_err(dev, "Power supply above 5.25V\n"); + if (status & ADIS16220_DIAG_STAT_POWER_LOW) + dev_err(dev, "Power supply below 4.75V\n"); + +error_ret: + return ret; +} + +static int adis16220_initial_setup(struct adis16220_state *st) +{ + int ret; + struct device *dev = &st->indio_dev->dev; + + /* Do self test */ + ret = adis16220_self_test(dev); + if (ret) { + dev_err(dev, "self test failure"); + goto err_ret; + } + + /* Read status register to check the result */ + ret = adis16220_check_status(dev); + if (ret) { + adis16220_reset(dev); + dev_err(dev, "device not playing ball -> reset"); + msleep(ADIS16220_STARTUP_DELAY); + ret = adis16220_check_status(dev); + if (ret) { + dev_err(dev, "giving up"); + goto err_ret; + } + } + + printk(KERN_INFO DRIVER_NAME ": at CS%d (irq %d)\n", + st->us->chip_select, st->us->irq); + +err_ret: + return ret; +} + +static ssize_t adis16220_capture_buffer_read(struct adis16220_state *st, + char *buf, + loff_t off, + size_t count, + int addr) +{ + struct spi_message msg; + struct spi_transfer xfers[] = { + { + .tx_buf = st->tx, + .bits_per_word = 8, + .len = 2, + .cs_change = 1, + .delay_usecs = 25, + }, { + .tx_buf = st->tx, + .rx_buf = st->rx, + .bits_per_word = 8, + .cs_change = 1, + .delay_usecs = 25, + }, + }; + int ret; + int i; + + if (unlikely(!count)) + return count; + + if ((off >= ADIS16220_CAPTURE_SIZE) || (count & 1) || (off & 1)) + return -EINVAL; + + if (off + count > ADIS16220_CAPTURE_SIZE) + count = ADIS16220_CAPTURE_SIZE - off; + + /* write the begin position of capture buffer */ + ret = adis16220_spi_write_reg_16(&st->indio_dev->dev, + ADIS16220_CAPT_PNTR, + off > 1); + if (ret) + return -EIO; + + /* read count/2 values from capture buffer */ + mutex_lock(&st->buf_lock); + + for (i = 0; i < count; i += 2) { + st->tx[i] = ADIS16220_READ_REG(addr); + st->tx[i + 1] = 0; + } + xfers[1].len = count; + + spi_message_init(&msg); + spi_message_add_tail(&xfers[0], &msg); + spi_message_add_tail(&xfers[1], &msg); + ret = spi_sync(st->us, &msg); + if (ret) { + + mutex_unlock(&st->buf_lock); + return -EIO; + } + + memcpy(buf, st->rx, count); + + mutex_unlock(&st->buf_lock); + return count; +} + +static ssize_t adis16220_accel_bin_read(struct kobject *kobj, + struct bin_attribute *attr, + char *buf, + loff_t off, + size_t count) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct adis16220_state *st = iio_dev_get_devdata(indio_dev); + + return adis16220_capture_buffer_read(st, buf, + off, count, + ADIS16220_CAPT_BUFA); +} + +static ssize_t adis16220_adc1_bin_read(struct kobject *kobj, + struct bin_attribute *attr, + char *buf, loff_t off, + size_t count) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct adis16220_state *st = iio_dev_get_devdata(indio_dev); + + return adis16220_capture_buffer_read(st, buf, + off, count, + ADIS16220_CAPT_BUF1); +} + +static ssize_t adis16220_adc2_bin_read(struct kobject *kobj, + struct bin_attribute *attr, + char *buf, loff_t off, + size_t count) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct adis16220_state *st = iio_dev_get_devdata(indio_dev); + + return adis16220_capture_buffer_read(st, buf, + off, count, + ADIS16220_CAPT_BUF2); +} + +static IIO_DEV_ATTR_IN_NAMED_RAW(supply, adis16220_read_12bit_unsigned, + ADIS16220_CAPT_SUPPLY); +static IIO_CONST_ATTR(in_supply_scale, "0.0012207"); +static IIO_DEV_ATTR_ACCEL(adis16220_read_16bit, ADIS16220_CAPT_BUFA); +static IIO_DEVICE_ATTR(accel_peak_raw, S_IRUGO, adis16220_read_16bit, + NULL, ADIS16220_CAPT_PEAKA); +static IIO_DEV_ATTR_ACCEL_OFFSET(S_IWUSR | S_IRUGO, + adis16220_read_16bit, + adis16220_write_16bit, + ADIS16220_ACCL_NULL); +static IIO_DEV_ATTR_TEMP_RAW(adis16220_read_12bit_unsigned); +static IIO_CONST_ATTR(temp_offset, "25"); +static IIO_CONST_ATTR(temp_scale, "-0.47"); + +static IIO_DEV_ATTR_IN_RAW(0, adis16220_read_16bit, ADIS16220_CAPT_BUF1); +static IIO_DEV_ATTR_IN_RAW(1, adis16220_read_16bit, ADIS16220_CAPT_BUF2); + +static IIO_DEVICE_ATTR(reset, S_IWUSR, NULL, + adis16220_write_reset, 0); + +#define IIO_DEV_ATTR_CAPTURE(_store) \ + IIO_DEVICE_ATTR(capture, S_IWUGO, NULL, _store, 0) + +static IIO_DEV_ATTR_CAPTURE(adis16220_write_capture); + +#define IIO_DEV_ATTR_CAPTURE_COUNT(_mode, _show, _store, _addr) \ + IIO_DEVICE_ATTR(capture_count, _mode, _show, _store, _addr) + +static IIO_DEV_ATTR_CAPTURE_COUNT(S_IWUSR | S_IRUGO, + adis16220_read_16bit, + adis16220_write_16bit, + ADIS16220_CAPT_PNTR); + +static IIO_CONST_ATTR_AVAIL_SAMP_FREQ("100200"); + +static IIO_CONST_ATTR(name, "adis16220"); + +static struct attribute *adis16220_attributes[] = { + &iio_dev_attr_in_supply_raw.dev_attr.attr, + &iio_const_attr_in_supply_scale.dev_attr.attr, + &iio_dev_attr_accel_raw.dev_attr.attr, + &iio_dev_attr_accel_offset.dev_attr.attr, + &iio_dev_attr_accel_peak_raw.dev_attr.attr, + &iio_dev_attr_temp_raw.dev_attr.attr, + &iio_dev_attr_in0_raw.dev_attr.attr, + &iio_dev_attr_in1_raw.dev_attr.attr, + &iio_const_attr_temp_offset.dev_attr.attr, + &iio_const_attr_temp_scale.dev_attr.attr, + &iio_const_attr_available_sampling_frequency.dev_attr.attr, + &iio_dev_attr_reset.dev_attr.attr, + &iio_dev_attr_capture.dev_attr.attr, + &iio_dev_attr_capture_count.dev_attr.attr, + &iio_const_attr_name.dev_attr.attr, + NULL +}; + +static const struct attribute_group adis16220_attribute_group = { + .attrs = adis16220_attributes, +}; + +static int __devinit adis16220_probe(struct spi_device *spi) +{ + int ret, regdone = 0; + struct adis16220_state *st = kzalloc(sizeof *st, GFP_KERNEL); + if (!st) { + ret = -ENOMEM; + goto error_ret; + } + /* this is only used for removal purposes */ + spi_set_drvdata(spi, st); + + /* Allocate the comms buffers */ + st->rx = kzalloc(sizeof(*st->rx)*ADIS16220_MAX_RX, GFP_KERNEL); + if (st->rx == NULL) { + ret = -ENOMEM; + goto error_free_st; + } + st->tx = kzalloc(sizeof(*st->tx)*ADIS16220_MAX_TX, GFP_KERNEL); + if (st->tx == NULL) { + ret = -ENOMEM; + goto error_free_rx; + } + st->us = spi; + mutex_init(&st->buf_lock); + /* setup the industrialio driver allocated elements */ + st->indio_dev = iio_allocate_device(); + if (st->indio_dev == NULL) { + ret = -ENOMEM; + goto error_free_tx; + } + + st->indio_dev->dev.parent = &spi->dev; + st->indio_dev->attrs = &adis16220_attribute_group; + st->indio_dev->dev_data = (void *)(st); + st->indio_dev->driver_module = THIS_MODULE; + st->indio_dev->modes = INDIO_DIRECT_MODE; + + ret = iio_device_register(st->indio_dev); + if (ret) + goto error_free_dev; + regdone = 1; + + st->accel_bin.attr.name = "accel_bin"; + st->accel_bin.attr.mode = S_IRUGO; + st->accel_bin.attr.owner = THIS_MODULE; + st->accel_bin.read = adis16220_accel_bin_read; + st->accel_bin.size = ADIS16220_CAPTURE_SIZE; + + ret = sysfs_create_bin_file(&st->indio_dev->dev.kobj, &st->accel_bin); + if (ret) + goto error_free_dev; + + st->adc1_bin.attr.name = "adc1_bin"; + st->adc1_bin.attr.mode = S_IRUGO; + st->adc1_bin.attr.owner = THIS_MODULE; + st->adc1_bin.read = adis16220_adc1_bin_read; + st->adc1_bin.size = ADIS16220_CAPTURE_SIZE; + + ret = sysfs_create_bin_file(&st->indio_dev->dev.kobj, &st->adc1_bin); + if (ret) + goto error_rm_accel_bin; + + st->adc2_bin.attr.name = "adc2_bin"; + st->adc2_bin.attr.mode = S_IRUGO; + st->adc2_bin.attr.owner = THIS_MODULE; + st->adc2_bin.read = adis16220_adc2_bin_read; + st->adc2_bin.size = ADIS16220_CAPTURE_SIZE; + + ret = sysfs_create_bin_file(&st->indio_dev->dev.kobj, &st->adc2_bin); + if (ret) + goto error_rm_adc1_bin; + + /* Get the device into a sane initial state */ + ret = adis16220_initial_setup(st); + if (ret) + goto error_rm_adc2_bin; + return 0; + +error_rm_adc2_bin: + sysfs_remove_bin_file(&st->indio_dev->dev.kobj, &st->adc2_bin); +error_rm_adc1_bin: + sysfs_remove_bin_file(&st->indio_dev->dev.kobj, &st->adc1_bin); +error_rm_accel_bin: + sysfs_remove_bin_file(&st->indio_dev->dev.kobj, &st->accel_bin); +error_free_dev: + if (regdone) + iio_device_unregister(st->indio_dev); + else + iio_free_device(st->indio_dev); +error_free_tx: + kfree(st->tx); +error_free_rx: + kfree(st->rx); +error_free_st: + kfree(st); +error_ret: + return ret; +} + +static int adis16220_remove(struct spi_device *spi) +{ + struct adis16220_state *st = spi_get_drvdata(spi); + struct iio_dev *indio_dev = st->indio_dev; + + flush_scheduled_work(); + + sysfs_remove_bin_file(&st->indio_dev->dev.kobj, &st->adc2_bin); + sysfs_remove_bin_file(&st->indio_dev->dev.kobj, &st->adc1_bin); + sysfs_remove_bin_file(&st->indio_dev->dev.kobj, &st->accel_bin); + iio_device_unregister(indio_dev); + kfree(st->tx); + kfree(st->rx); + kfree(st); + + return 0; +} + +static struct spi_driver adis16220_driver = { + .driver = { + .name = "adis16220", + .owner = THIS_MODULE, + }, + .probe = adis16220_probe, + .remove = __devexit_p(adis16220_remove), +}; + +static __init int adis16220_init(void) +{ + return spi_register_driver(&adis16220_driver); +} +module_init(adis16220_init); + +static __exit void adis16220_exit(void) +{ + spi_unregister_driver(&adis16220_driver); +} +module_exit(adis16220_exit); + +MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>"); +MODULE_DESCRIPTION("Analog Devices ADIS16220 Digital Vibration Sensor"); +MODULE_LICENSE("GPL v2"); -- cgit v1.1 From 9a3af585e7fe3df35b233977579b5ab6e4c7005f Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Sun, 16 May 2010 21:11:38 +0100 Subject: Staging: iio: adis16220 extract bin_attribute structures from state Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/staging/iio/accel/adis16220.h | 3 - drivers/staging/iio/accel/adis16220_core.c | 106 +++++++++++++++-------------- 2 files changed, 56 insertions(+), 53 deletions(-) (limited to 'drivers/staging/iio') diff --git a/drivers/staging/iio/accel/adis16220.h b/drivers/staging/iio/accel/adis16220.h index 6b49f70..2abf485 100644 --- a/drivers/staging/iio/accel/adis16220.h +++ b/drivers/staging/iio/accel/adis16220.h @@ -141,9 +141,6 @@ struct adis16220_state { struct iio_dev *indio_dev; u8 *tx; u8 *rx; - struct bin_attribute accel_bin; - struct bin_attribute adc1_bin; - struct bin_attribute adc2_bin; struct mutex buf_lock; }; diff --git a/drivers/staging/iio/accel/adis16220_core.c b/drivers/staging/iio/accel/adis16220_core.c index 8b845d9..6de439f 100644 --- a/drivers/staging/iio/accel/adis16220_core.c +++ b/drivers/staging/iio/accel/adis16220_core.c @@ -27,8 +27,6 @@ #define DRIVER_NAME "adis16220" -static int adis16220_check_status(struct device *dev); - /** * adis16220_spi_write_reg_8() - write single byte to a register * @dev: device associated with child of actual device (iio_dev or iio_trig) @@ -133,8 +131,6 @@ static int adis16220_spi_read_reg_16(struct device *dev, mutex_lock(&st->buf_lock); st->tx[0] = ADIS16220_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); @@ -275,23 +271,6 @@ static ssize_t adis16220_write_capture(struct device *dev, return -1; } -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_check_status(struct device *dev) { u16 status; @@ -320,6 +299,23 @@ 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; @@ -433,6 +429,15 @@ static ssize_t adis16220_accel_bin_read(struct kobject *kobj, ADIS16220_CAPT_BUFA); } +static struct bin_attribute accel_bin = { + .attr = { + .name = "accel_bin", + .mode = S_IRUGO, + }, + .read = adis16220_accel_bin_read, + .size = ADIS16220_CAPTURE_SIZE, +}; + static ssize_t adis16220_adc1_bin_read(struct kobject *kobj, struct bin_attribute *attr, char *buf, loff_t off, @@ -447,6 +452,15 @@ static ssize_t adis16220_adc1_bin_read(struct kobject *kobj, ADIS16220_CAPT_BUF1); } +static struct bin_attribute adc1_bin = { + .attr = { + .name = "in0_bin", + .mode = S_IRUGO, + }, + .read = adis16220_adc1_bin_read, + .size = ADIS16220_CAPTURE_SIZE, +}; + static ssize_t adis16220_adc2_bin_read(struct kobject *kobj, struct bin_attribute *attr, char *buf, loff_t off, @@ -461,6 +475,16 @@ static ssize_t adis16220_adc2_bin_read(struct kobject *kobj, ADIS16220_CAPT_BUF2); } + +static struct bin_attribute adc2_bin = { + .attr = { + .name = "in1_bin", + .mode = S_IRUGO, + }, + .read = adis16220_adc2_bin_read, + .size = ADIS16220_CAPTURE_SIZE, +}; + static IIO_DEV_ATTR_IN_NAMED_RAW(supply, adis16220_read_12bit_unsigned, ADIS16220_CAPT_SUPPLY); static IIO_CONST_ATTR(in_supply_scale, "0.0012207"); @@ -481,12 +505,12 @@ static IIO_DEV_ATTR_IN_RAW(1, adis16220_read_16bit, ADIS16220_CAPT_BUF2); static IIO_DEVICE_ATTR(reset, S_IWUSR, NULL, adis16220_write_reset, 0); -#define IIO_DEV_ATTR_CAPTURE(_store) \ +#define IIO_DEV_ATTR_CAPTURE(_store) \ IIO_DEVICE_ATTR(capture, S_IWUGO, NULL, _store, 0) static IIO_DEV_ATTR_CAPTURE(adis16220_write_capture); -#define IIO_DEV_ATTR_CAPTURE_COUNT(_mode, _show, _store, _addr) \ +#define IIO_DEV_ATTR_CAPTURE_COUNT(_mode, _show, _store, _addr) \ IIO_DEVICE_ATTR(capture_count, _mode, _show, _store, _addr) static IIO_DEV_ATTR_CAPTURE_COUNT(S_IWUSR | S_IRUGO, @@ -563,33 +587,15 @@ static int __devinit adis16220_probe(struct spi_device *spi) goto error_free_dev; regdone = 1; - st->accel_bin.attr.name = "accel_bin"; - st->accel_bin.attr.mode = S_IRUGO; - st->accel_bin.attr.owner = THIS_MODULE; - st->accel_bin.read = adis16220_accel_bin_read; - st->accel_bin.size = ADIS16220_CAPTURE_SIZE; - - ret = sysfs_create_bin_file(&st->indio_dev->dev.kobj, &st->accel_bin); + ret = sysfs_create_bin_file(&st->indio_dev->dev.kobj, &accel_bin); if (ret) goto error_free_dev; - st->adc1_bin.attr.name = "adc1_bin"; - st->adc1_bin.attr.mode = S_IRUGO; - st->adc1_bin.attr.owner = THIS_MODULE; - st->adc1_bin.read = adis16220_adc1_bin_read; - st->adc1_bin.size = ADIS16220_CAPTURE_SIZE; - - ret = sysfs_create_bin_file(&st->indio_dev->dev.kobj, &st->adc1_bin); + ret = sysfs_create_bin_file(&st->indio_dev->dev.kobj, &adc1_bin); if (ret) goto error_rm_accel_bin; - st->adc2_bin.attr.name = "adc2_bin"; - st->adc2_bin.attr.mode = S_IRUGO; - st->adc2_bin.attr.owner = THIS_MODULE; - st->adc2_bin.read = adis16220_adc2_bin_read; - st->adc2_bin.size = ADIS16220_CAPTURE_SIZE; - - ret = sysfs_create_bin_file(&st->indio_dev->dev.kobj, &st->adc2_bin); + ret = sysfs_create_bin_file(&st->indio_dev->dev.kobj, &adc2_bin); if (ret) goto error_rm_adc1_bin; @@ -600,11 +606,11 @@ static int __devinit adis16220_probe(struct spi_device *spi) return 0; error_rm_adc2_bin: - sysfs_remove_bin_file(&st->indio_dev->dev.kobj, &st->adc2_bin); + sysfs_remove_bin_file(&st->indio_dev->dev.kobj, &adc2_bin); error_rm_adc1_bin: - sysfs_remove_bin_file(&st->indio_dev->dev.kobj, &st->adc1_bin); + sysfs_remove_bin_file(&st->indio_dev->dev.kobj, &adc1_bin); error_rm_accel_bin: - sysfs_remove_bin_file(&st->indio_dev->dev.kobj, &st->accel_bin); + sysfs_remove_bin_file(&st->indio_dev->dev.kobj, &accel_bin); error_free_dev: if (regdone) iio_device_unregister(st->indio_dev); @@ -627,9 +633,9 @@ static int adis16220_remove(struct spi_device *spi) flush_scheduled_work(); - sysfs_remove_bin_file(&st->indio_dev->dev.kobj, &st->adc2_bin); - sysfs_remove_bin_file(&st->indio_dev->dev.kobj, &st->adc1_bin); - sysfs_remove_bin_file(&st->indio_dev->dev.kobj, &st->accel_bin); + 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); -- cgit v1.1 From 81b77f94a10b64a3620e32531b5d8dbc495f1727 Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Sun, 16 May 2010 21:29:25 +0100 Subject: Staging: iio: max1363 Fix two bugs in single_channel_from_ring This patch contains fixes for the two bugs Michael pointed out last week. As the other suggestion Michael made is not a bug fix (just a much more sensible way of handling things), I'll do that as a separate patch soon. The bugs were introduced with the abi changes. Signed-off-by: Jonathan Cameron Reported-by: Michael Hennerich Signed-off-by: Greg Kroah-Hartman --- drivers/staging/iio/adc/max1363_ring.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/staging/iio') diff --git a/drivers/staging/iio/adc/max1363_ring.c b/drivers/staging/iio/adc/max1363_ring.c index c8aa011..56688dc 100644 --- a/drivers/staging/iio/adc/max1363_ring.c +++ b/drivers/staging/iio/adc/max1363_ring.c @@ -51,15 +51,15 @@ int max1363_single_channel_from_ring(long mask, struct max1363_state *st) /* Need a count of channels prior to this one */ mask >>= 1; while (mask) { - if (mask && st->current_mode->modemask) + if (mask & st->current_mode->modemask) count++; mask >>= 1; } if (st->chip_info->bits != 8) - return ((int)(ring_data[count*2 + 0] & 0x0F) << 8) + ret = ((int)(ring_data[count*2 + 0] & 0x0F) << 8) + (int)(ring_data[count*2 + 1]); else - return ring_data[count]; + ret = ring_data[count]; error_free_ring_data: kfree(ring_data); -- cgit v1.1 From 5763dcab5cd7de27d6db50efd393c416177c56c7 Mon Sep 17 00:00:00 2001 From: Barry Song Date: Mon, 17 May 2010 11:40:20 +0100 Subject: staging: iio: adis16350 and similar IMU driver This version has the right part number in the commit message. Whilst technically the part I listed last time is also supported by the driver, the commit message might have caused confusion. Another driver from Barry at Analog. Again, I've lifted if from the blackfin tree and done the usual sparse and checkpatch fixes + the abi changes. I actually have one of these, so am particularly pleased to see it supported! Signed-off-by: Barry Song Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/staging/iio/imu/Kconfig | 9 + drivers/staging/iio/imu/Makefile | 4 + drivers/staging/iio/imu/adis16350.h | 193 ++++++++ drivers/staging/iio/imu/adis16350_core.c | 736 ++++++++++++++++++++++++++++ drivers/staging/iio/imu/adis16350_ring.c | 286 +++++++++++ drivers/staging/iio/imu/adis16350_trigger.c | 127 +++++ 6 files changed, 1355 insertions(+) create mode 100644 drivers/staging/iio/imu/adis16350.h create mode 100644 drivers/staging/iio/imu/adis16350_core.c create mode 100644 drivers/staging/iio/imu/adis16350_ring.c create mode 100644 drivers/staging/iio/imu/adis16350_trigger.c (limited to 'drivers/staging/iio') diff --git a/drivers/staging/iio/imu/Kconfig b/drivers/staging/iio/imu/Kconfig index 411804f..6308d6f 100644 --- a/drivers/staging/iio/imu/Kconfig +++ b/drivers/staging/iio/imu/Kconfig @@ -13,6 +13,15 @@ config ADIS16300 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 diff --git a/drivers/staging/iio/imu/Makefile b/drivers/staging/iio/imu/Makefile index de454dd..31df735 100644 --- a/drivers/staging/iio/imu/Makefile +++ b/drivers/staging/iio/imu/Makefile @@ -5,6 +5,10 @@ adis16300-y := adis16300_core.o adis16300-$(CONFIG_IIO_RING_BUFFER) += adis16300_ring.o adis16300_trigger.o obj-$(CONFIG_ADIS16300) += adis16300.o +adis16350-y := adis16350_core.o +adis16350-$(CONFIG_IIO_RING_BUFFER) += adis16350_ring.o adis16350_trigger.o +obj-$(CONFIG_ADIS16350) += adis16350.o + adis16400-y := adis16400_core.o adis16400-$(CONFIG_IIO_RING_BUFFER) += adis16400_ring.o adis16400_trigger.o obj-$(CONFIG_ADIS16400) += adis16400.o \ No newline at end of file diff --git a/drivers/staging/iio/imu/adis16350.h b/drivers/staging/iio/imu/adis16350.h new file mode 100644 index 0000000..334b18a --- /dev/null +++ b/drivers/staging/iio/imu/adis16350.h @@ -0,0 +1,193 @@ +#ifndef SPI_ADIS16350_H_ +#define SPI_ADIS16350_H_ + +#define ADIS16350_STARTUP_DELAY 220 /* ms */ + +#define ADIS16350_READ_REG(a) a +#define ADIS16350_WRITE_REG(a) ((a) | 0x80) + +#define ADIS16350_FLASH_CNT 0x00 /* Flash memory write count */ +#define ADIS16350_SUPPLY_OUT 0x02 /* Power supply measurement */ +#define ADIS16350_XGYRO_OUT 0x04 /* X-axis gyroscope output */ +#define ADIS16350_YGYRO_OUT 0x06 /* Y-axis gyroscope output */ +#define ADIS16350_ZGYRO_OUT 0x08 /* Z-axis gyroscope output */ +#define ADIS16350_XACCL_OUT 0x0A /* X-axis accelerometer output */ +#define ADIS16350_YACCL_OUT 0x0C /* Y-axis accelerometer output */ +#define ADIS16350_ZACCL_OUT 0x0E /* Z-axis accelerometer output */ +#define ADIS16350_XTEMP_OUT 0x10 /* X-axis gyroscope temperature measurement */ +#define ADIS16350_YTEMP_OUT 0x12 /* Y-axis gyroscope temperature measurement */ +#define ADIS16350_ZTEMP_OUT 0x14 /* Z-axis gyroscope temperature measurement */ +#define ADIS16350_AUX_ADC 0x16 /* Auxiliary ADC measurement */ + +/* Calibration parameters */ +#define ADIS16350_XGYRO_OFF 0x1A /* X-axis gyroscope bias offset factor */ +#define ADIS16350_YGYRO_OFF 0x1C /* Y-axis gyroscope bias offset factor */ +#define ADIS16350_ZGYRO_OFF 0x1E /* Z-axis gyroscope bias offset factor */ +#define ADIS16350_XACCL_OFF 0x20 /* X-axis acceleration bias offset factor */ +#define ADIS16350_YACCL_OFF 0x22 /* Y-axis acceleration bias offset factor */ +#define ADIS16350_ZACCL_OFF 0x24 /* Z-axis acceleration bias offset factor */ + +#define ADIS16350_GPIO_CTRL 0x32 /* Auxiliary digital input/output control */ +#define ADIS16350_MSC_CTRL 0x34 /* Miscellaneous control */ +#define ADIS16350_SMPL_PRD 0x36 /* Internal sample period (rate) control */ +#define ADIS16350_SENS_AVG 0x38 /* Dynamic range and digital filter control */ +#define ADIS16350_SLP_CNT 0x3A /* Sleep mode control */ +#define ADIS16350_DIAG_STAT 0x3C /* System status */ + +/* Alarm functions */ +#define ADIS16350_GLOB_CMD 0x3E /* System command */ +#define ADIS16350_ALM_MAG1 0x26 /* Alarm 1 amplitude threshold */ +#define ADIS16350_ALM_MAG2 0x28 /* Alarm 2 amplitude threshold */ +#define ADIS16350_ALM_SMPL1 0x2A /* Alarm 1 sample size */ +#define ADIS16350_ALM_SMPL2 0x2C /* Alarm 2 sample size */ +#define ADIS16350_ALM_CTRL 0x2E /* Alarm control */ +#define ADIS16350_AUX_DAC 0x30 /* Auxiliary DAC data */ + +#define ADIS16350_ERROR_ACTIVE (1<<14) +#define ADIS16350_NEW_DATA (1<<15) + +/* MSC_CTRL */ +#define ADIS16350_MSC_CTRL_MEM_TEST (1<<11) +#define ADIS16350_MSC_CTRL_INT_SELF_TEST (1<<10) +#define ADIS16350_MSC_CTRL_NEG_SELF_TEST (1<<9) +#define ADIS16350_MSC_CTRL_POS_SELF_TEST (1<<8) +#define ADIS16350_MSC_CTRL_GYRO_BIAS (1<<7) +#define ADIS16350_MSC_CTRL_ACCL_ALIGN (1<<6) +#define ADIS16350_MSC_CTRL_DATA_RDY_EN (1<<2) +#define ADIS16350_MSC_CTRL_DATA_RDY_POL_HIGH (1<<1) +#define ADIS16350_MSC_CTRL_DATA_RDY_DIO2 (1<<0) + +/* SMPL_PRD */ +#define ADIS16350_SMPL_PRD_TIME_BASE (1<<7) +#define ADIS16350_SMPL_PRD_DIV_MASK 0x7F + +/* DIAG_STAT */ +#define ADIS16350_DIAG_STAT_ZACCL_FAIL (1<<15) +#define ADIS16350_DIAG_STAT_YACCL_FAIL (1<<14) +#define ADIS16350_DIAG_STAT_XACCL_FAIL (1<<13) +#define ADIS16350_DIAG_STAT_XGYRO_FAIL (1<<12) +#define ADIS16350_DIAG_STAT_YGYRO_FAIL (1<<11) +#define ADIS16350_DIAG_STAT_ZGYRO_FAIL (1<<10) +#define ADIS16350_DIAG_STAT_ALARM2 (1<<9) +#define ADIS16350_DIAG_STAT_ALARM1 (1<<8) +#define ADIS16350_DIAG_STAT_FLASH_CHK (1<<6) +#define ADIS16350_DIAG_STAT_SELF_TEST (1<<5) +#define ADIS16350_DIAG_STAT_OVERFLOW (1<<4) +#define ADIS16350_DIAG_STAT_SPI_FAIL (1<<3) +#define ADIS16350_DIAG_STAT_FLASH_UPT (1<<2) +#define ADIS16350_DIAG_STAT_POWER_HIGH (1<<1) +#define ADIS16350_DIAG_STAT_POWER_LOW (1<<0) + +/* GLOB_CMD */ +#define ADIS16350_GLOB_CMD_SW_RESET (1<<7) +#define ADIS16350_GLOB_CMD_P_AUTO_NULL (1<<4) +#define ADIS16350_GLOB_CMD_FLASH_UPD (1<<3) +#define ADIS16350_GLOB_CMD_DAC_LATCH (1<<2) +#define ADIS16350_GLOB_CMD_FAC_CALIB (1<<1) +#define ADIS16350_GLOB_CMD_AUTO_NULL (1<<0) + +/* SLP_CNT */ +#define ADIS16350_SLP_CNT_POWER_OFF (1<<8) + +#define ADIS16350_MAX_TX 24 +#define ADIS16350_MAX_RX 24 + +#define ADIS16350_SPI_SLOW (u32)(300 * 1000) +#define ADIS16350_SPI_BURST (u32)(1000 * 1000) +#define ADIS16350_SPI_FAST (u32)(2000 * 1000) + +/** + * struct adis16350_state - device instance specific data + * @us: actual spi_device + * @work_trigger_to_ring: bh for triggered event handling + * @work_cont_thresh: CLEAN + * @inter: used to check if new interrupt has been triggered + * @last_timestamp: passing timestamp from th to bh of interrupt handler + * @indio_dev: industrial I/O device structure + * @trig: data ready trigger registered with iio + * @tx: transmit buffer + * @rx: recieve buffer + * @buf_lock: mutex to protect tx and rx + **/ +struct adis16350_state { + struct spi_device *us; + struct work_struct work_trigger_to_ring; + struct iio_work_cont work_cont_data_rdy; + s64 last_timestamp; + struct iio_dev *indio_dev; + struct iio_trigger *trig; + u8 *tx; + u8 *rx; + struct mutex buf_lock; +}; + +int adis16350_set_irq(struct device *dev, bool enable); + +#ifdef CONFIG_IIO_RING_BUFFER + +enum adis16350_scan { + ADIS16350_SCAN_SUPPLY, + ADIS16350_SCAN_GYRO_X, + ADIS16350_SCAN_GYRO_Y, + ADIS16350_SCAN_GYRO_Z, + ADIS16350_SCAN_ACC_X, + ADIS16350_SCAN_ACC_Y, + ADIS16350_SCAN_ACC_Z, + ADIS16350_SCAN_TEMP_X, + ADIS16350_SCAN_TEMP_Y, + ADIS16350_SCAN_TEMP_Z, + ADIS16350_SCAN_ADC_0 +}; + +void adis16350_remove_trigger(struct iio_dev *indio_dev); +int adis16350_probe_trigger(struct iio_dev *indio_dev); + +ssize_t adis16350_read_data_from_ring(struct device *dev, + struct device_attribute *attr, + char *buf); + + +int adis16350_configure_ring(struct iio_dev *indio_dev); +void adis16350_unconfigure_ring(struct iio_dev *indio_dev); + +int adis16350_initialize_ring(struct iio_ring_buffer *ring); +void adis16350_uninitialize_ring(struct iio_ring_buffer *ring); +#else /* CONFIG_IIO_RING_BUFFER */ + +static inline void adis16350_remove_trigger(struct iio_dev *indio_dev) +{ +} + +static inline int adis16350_probe_trigger(struct iio_dev *indio_dev) +{ + return 0; +} + +static inline ssize_t +adis16350_read_data_from_ring(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return 0; +} + +static int adis16350_configure_ring(struct iio_dev *indio_dev) +{ + return 0; +} + +static inline void adis16350_unconfigure_ring(struct iio_dev *indio_dev) +{ +} + +static inline int adis16350_initialize_ring(struct iio_ring_buffer *ring) +{ + return 0; +} + +static inline void adis16350_uninitialize_ring(struct iio_ring_buffer *ring) +{ +} + +#endif /* CONFIG_IIO_RING_BUFFER */ +#endif /* SPI_ADIS16350_H_ */ diff --git a/drivers/staging/iio/imu/adis16350_core.c b/drivers/staging/iio/imu/adis16350_core.c new file mode 100644 index 0000000..0edde73 --- /dev/null +++ b/drivers/staging/iio/imu/adis16350_core.c @@ -0,0 +1,736 @@ +/* + * ADIS16350/54/55/60/62/64/65 high precision tri-axis inertial sensor + * + * Copyright 2010 Analog Devices Inc. + * + * Licensed under the GPL-2 or later. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "../iio.h" +#include "../sysfs.h" +#include "../accel/accel.h" +#include "../adc/adc.h" +#include "../gyro/gyro.h" + +#include "adis16350.h" + +#define DRIVER_NAME "adis16350" + +static int adis16350_check_status(struct device *dev); + +/** + * adis16350_spi_write_reg_8() - write single byte to a register + * @dev: device associated with child of actual device (iio_dev or iio_trig) + * @reg_address: the address of the register to be written + * @val: the value to write + **/ +static int adis16350_spi_write_reg_8(struct device *dev, + u8 reg_address, + u8 val) +{ + int ret; + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct adis16350_state *st = iio_dev_get_devdata(indio_dev); + + mutex_lock(&st->buf_lock); + st->tx[0] = ADIS16350_WRITE_REG(reg_address); + st->tx[1] = val; + + ret = spi_write(st->us, st->tx, 2); + mutex_unlock(&st->buf_lock); + + return ret; +} + +/** + * adis16350_spi_write_reg_16() - write 2 bytes to a pair of registers + * @dev: device associated with child of actual device (iio_dev or iio_trig) + * @reg_address: the address of the lower of the two registers. Second register + * is assumed to have address one greater. + * @val: value to be written + **/ +static int adis16350_spi_write_reg_16(struct device *dev, + u8 lower_reg_address, + u16 value) +{ + int ret; + struct spi_message msg; + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct adis16350_state *st = iio_dev_get_devdata(indio_dev); + struct spi_transfer xfers[] = { + { + .tx_buf = st->tx, + .bits_per_word = 8, + .len = 2, + .cs_change = 1, + .delay_usecs = 25, + }, { + .tx_buf = st->tx + 2, + .bits_per_word = 8, + .len = 2, + .cs_change = 1, + .delay_usecs = 25, + }, + }; + + mutex_lock(&st->buf_lock); + st->tx[0] = ADIS16350_WRITE_REG(lower_reg_address); + st->tx[1] = value & 0xFF; + st->tx[2] = ADIS16350_WRITE_REG(lower_reg_address + 1); + st->tx[3] = (value >> 8) & 0xFF; + + spi_message_init(&msg); + spi_message_add_tail(&xfers[0], &msg); + spi_message_add_tail(&xfers[1], &msg); + ret = spi_sync(st->us, &msg); + mutex_unlock(&st->buf_lock); + + return ret; +} + +/** + * adis16350_spi_read_reg_16() - read 2 bytes from a 16-bit register + * @dev: device associated with child of actual device (iio_dev or iio_trig) + * @reg_address: the address of the lower of the two registers. Second register + * is assumed to have address one greater. + * @val: somewhere to pass back the value read + **/ +static int adis16350_spi_read_reg_16(struct device *dev, + u8 lower_reg_address, + u16 *val) +{ + struct spi_message msg; + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct adis16350_state *st = iio_dev_get_devdata(indio_dev); + int ret; + struct spi_transfer xfers[] = { + { + .tx_buf = st->tx, + .bits_per_word = 8, + .len = 2, + .cs_change = 1, + .delay_usecs = 25, + }, { + .rx_buf = st->rx, + .bits_per_word = 8, + .len = 2, + .cs_change = 1, + .delay_usecs = 25, + }, + }; + + mutex_lock(&st->buf_lock); + st->tx[0] = ADIS16350_READ_REG(lower_reg_address); + st->tx[1] = 0; + st->tx[2] = 0; + st->tx[3] = 0; + + spi_message_init(&msg); + spi_message_add_tail(&xfers[0], &msg); + spi_message_add_tail(&xfers[1], &msg); + ret = spi_sync(st->us, &msg); + if (ret) { + dev_err(&st->us->dev, + "problem when reading 16 bit register 0x%02X", + lower_reg_address); + goto error_ret; + } + *val = (st->rx[0] << 8) | st->rx[1]; + +error_ret: + mutex_unlock(&st->buf_lock); + return ret; +} + + +static ssize_t adis16350_spi_read_signed(struct device *dev, + struct device_attribute *attr, + char *buf, + unsigned bits) +{ + int ret; + s16 val = 0; + unsigned shift = 16 - bits; + struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); + + ret = adis16350_spi_read_reg_16(dev, this_attr->address, (u16 *)&val); + if (ret) + return ret; + + if (val & ADIS16350_ERROR_ACTIVE) + adis16350_check_status(dev); + val = ((s16)(val << shift) >> shift); + return sprintf(buf, "%d\n", val); +} + +static ssize_t adis16350_read_12bit_unsigned(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int ret; + u16 val = 0; + struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); + + ret = adis16350_spi_read_reg_16(dev, this_attr->address, &val); + if (ret) + return ret; + + if (val & ADIS16350_ERROR_ACTIVE) + adis16350_check_status(dev); + + return sprintf(buf, "%u\n", val & 0x0FFF); +} + +static ssize_t adis16350_read_14bit_signed(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + ssize_t ret; + + /* Take the iio_dev status lock */ + mutex_lock(&indio_dev->mlock); + ret = adis16350_spi_read_signed(dev, attr, buf, 14); + mutex_unlock(&indio_dev->mlock); + + return ret; +} + +static ssize_t adis16350_read_12bit_signed(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + ssize_t ret; + + /* Take the iio_dev status lock */ + mutex_lock(&indio_dev->mlock); + ret = adis16350_spi_read_signed(dev, attr, buf, 12); + mutex_unlock(&indio_dev->mlock); + + return ret; +} + +static ssize_t adis16350_write_16bit(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t len) +{ + struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); + int ret; + long val; + + ret = strict_strtol(buf, 10, &val); + if (ret) + goto error_ret; + ret = adis16350_spi_write_reg_16(dev, this_attr->address, val); + +error_ret: + return ret ? ret : len; +} + +static ssize_t adis16350_read_frequency(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int ret, len = 0; + u16 t; + int sps; + ret = adis16350_spi_read_reg_16(dev, + ADIS16350_SMPL_PRD, + &t); + if (ret) + return ret; + sps = (t & ADIS16350_SMPL_PRD_TIME_BASE) ? 53 : 1638; + sps /= (t & ADIS16350_SMPL_PRD_DIV_MASK) + 1; + len = sprintf(buf, "%d SPS\n", sps); + return len; +} + +static ssize_t adis16350_write_frequency(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t len) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct adis16350_state *st = iio_dev_get_devdata(indio_dev); + long val; + int ret; + u8 t; + + ret = strict_strtol(buf, 10, &val); + if (ret) + return ret; + + mutex_lock(&indio_dev->mlock); + + t = (1638 / val); + if (t > 0) + t--; + t &= ADIS16350_SMPL_PRD_DIV_MASK; + if ((t & ADIS16350_SMPL_PRD_DIV_MASK) >= 0x0A) + st->us->max_speed_hz = ADIS16350_SPI_SLOW; + else + st->us->max_speed_hz = ADIS16350_SPI_FAST; + + ret = adis16350_spi_write_reg_8(dev, + ADIS16350_SMPL_PRD, + t); + + mutex_unlock(&indio_dev->mlock); + + return ret ? ret : len; +} + +static int adis16350_reset(struct device *dev) +{ + int ret; + ret = adis16350_spi_write_reg_8(dev, + ADIS16350_GLOB_CMD, + ADIS16350_GLOB_CMD_SW_RESET); + if (ret) + dev_err(dev, "problem resetting device"); + + return ret; +} + +static ssize_t adis16350_write_reset(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + if (len < 1) + return -1; + switch (buf[0]) { + case '1': + case 'y': + case 'Y': + return adis16350_reset(dev); + } + return -1; +} + +int adis16350_set_irq(struct device *dev, bool enable) +{ + int ret; + u16 msc; + ret = adis16350_spi_read_reg_16(dev, ADIS16350_MSC_CTRL, &msc); + if (ret) + goto error_ret; + + msc |= ADIS16350_MSC_CTRL_DATA_RDY_POL_HIGH; + msc &= ~ADIS16350_MSC_CTRL_DATA_RDY_DIO2; + + if (enable) + msc |= ADIS16350_MSC_CTRL_DATA_RDY_EN; + else + msc &= ~ADIS16350_MSC_CTRL_DATA_RDY_EN; + + ret = adis16350_spi_write_reg_16(dev, ADIS16350_MSC_CTRL, msc); + if (ret) + goto error_ret; + +error_ret: + return ret; +} + +/* Power down the device */ +static int adis16350_stop_device(struct device *dev) +{ + int ret; + u16 val = ADIS16350_SLP_CNT_POWER_OFF; + + ret = adis16350_spi_write_reg_16(dev, ADIS16350_SLP_CNT, val); + if (ret) + dev_err(dev, "problem with turning device off: SLP_CNT"); + + return ret; +} + +static int adis16350_self_test(struct device *dev) +{ + int ret; + ret = adis16350_spi_write_reg_16(dev, + ADIS16350_MSC_CTRL, + ADIS16350_MSC_CTRL_MEM_TEST); + if (ret) { + dev_err(dev, "problem starting self test"); + goto err_ret; + } + + adis16350_check_status(dev); + +err_ret: + return ret; +} + +static int adis16350_check_status(struct device *dev) +{ + u16 status; + int ret; + + ret = adis16350_spi_read_reg_16(dev, ADIS16350_DIAG_STAT, &status); + + if (ret < 0) { + dev_err(dev, "Reading status failed\n"); + goto error_ret; + } + ret = status; + if (status & ADIS16350_DIAG_STAT_ZACCL_FAIL) + dev_err(dev, "Z-axis accelerometer self-test failure\n"); + if (status & ADIS16350_DIAG_STAT_YACCL_FAIL) + dev_err(dev, "Y-axis accelerometer self-test failure\n"); + if (status & ADIS16350_DIAG_STAT_XACCL_FAIL) + dev_err(dev, "X-axis accelerometer self-test failure\n"); + if (status & ADIS16350_DIAG_STAT_XGYRO_FAIL) + dev_err(dev, "X-axis gyroscope self-test failure\n"); + if (status & ADIS16350_DIAG_STAT_YGYRO_FAIL) + dev_err(dev, "Y-axis gyroscope self-test failure\n"); + if (status & ADIS16350_DIAG_STAT_ZGYRO_FAIL) + dev_err(dev, "Z-axis gyroscope self-test failure\n"); + if (status & ADIS16350_DIAG_STAT_ALARM2) + dev_err(dev, "Alarm 2 active\n"); + if (status & ADIS16350_DIAG_STAT_ALARM1) + dev_err(dev, "Alarm 1 active\n"); + if (status & ADIS16350_DIAG_STAT_FLASH_CHK) + dev_err(dev, "Flash checksum error\n"); + if (status & ADIS16350_DIAG_STAT_SELF_TEST) + dev_err(dev, "Self test error\n"); + if (status & ADIS16350_DIAG_STAT_OVERFLOW) + dev_err(dev, "Sensor overrange\n"); + if (status & ADIS16350_DIAG_STAT_SPI_FAIL) + dev_err(dev, "SPI failure\n"); + if (status & ADIS16350_DIAG_STAT_FLASH_UPT) + dev_err(dev, "Flash update failed\n"); + if (status & ADIS16350_DIAG_STAT_POWER_HIGH) + dev_err(dev, "Power supply above 5.25V\n"); + if (status & ADIS16350_DIAG_STAT_POWER_LOW) + dev_err(dev, "Power supply below 4.75V\n"); + +error_ret: + return ret; +} + +static int adis16350_initial_setup(struct adis16350_state *st) +{ + int ret; + u16 smp_prd; + struct device *dev = &st->indio_dev->dev; + + /* use low spi speed for init */ + st->us->max_speed_hz = ADIS16350_SPI_SLOW; + st->us->mode = SPI_MODE_3; + spi_setup(st->us); + + /* Disable IRQ */ + ret = adis16350_set_irq(dev, false); + if (ret) { + dev_err(dev, "disable irq failed"); + goto err_ret; + } + + /* Do self test */ + ret = adis16350_self_test(dev); + if (ret) { + dev_err(dev, "self test failure"); + goto err_ret; + } + + /* Read status register to check the result */ + ret = adis16350_check_status(dev); + if (ret) { + adis16350_reset(dev); + dev_err(dev, "device not playing ball -> reset"); + msleep(ADIS16350_STARTUP_DELAY); + ret = adis16350_check_status(dev); + if (ret) { + dev_err(dev, "giving up"); + goto err_ret; + } + } + + printk(KERN_INFO DRIVER_NAME ": at CS%d (irq %d)\n", + st->us->chip_select, st->us->irq); + + /* use high spi speed if possible */ + ret = adis16350_spi_read_reg_16(dev, ADIS16350_SMPL_PRD, &smp_prd); + if (!ret && (smp_prd & ADIS16350_SMPL_PRD_DIV_MASK) < 0x0A) { + st->us->max_speed_hz = ADIS16350_SPI_SLOW; + spi_setup(st->us); + } + +err_ret: + return ret; +} + +static IIO_DEV_ATTR_ACCEL_X_OFFSET(S_IWUSR | S_IRUGO, + adis16350_read_12bit_signed, + adis16350_write_16bit, + ADIS16350_XACCL_OFF); + +static IIO_DEV_ATTR_ACCEL_Y_OFFSET(S_IWUSR | S_IRUGO, + adis16350_read_12bit_signed, + adis16350_write_16bit, + ADIS16350_YACCL_OFF); + +static IIO_DEV_ATTR_ACCEL_Z_OFFSET(S_IWUSR | S_IRUGO, + adis16350_read_12bit_signed, + adis16350_write_16bit, + ADIS16350_ZACCL_OFF); + +static IIO_DEV_ATTR_IN_NAMED_RAW(supply, adis16350_read_12bit_unsigned, + ADIS16350_SUPPLY_OUT); +static IIO_CONST_ATTR(in_supply_scale, "0.002418"); + +static IIO_DEV_ATTR_GYRO_X(adis16350_read_14bit_signed, + ADIS16350_XGYRO_OUT); +static IIO_DEV_ATTR_GYRO_Y(adis16350_read_14bit_signed, + ADIS16350_YGYRO_OUT); +static IIO_DEV_ATTR_GYRO_Z(adis16350_read_14bit_signed, + ADIS16350_ZGYRO_OUT); +static IIO_CONST_ATTR(gyro_scale, "0.05"); + +static IIO_DEV_ATTR_ACCEL_X(adis16350_read_14bit_signed, + ADIS16350_XACCL_OUT); +static IIO_DEV_ATTR_ACCEL_Y(adis16350_read_14bit_signed, + ADIS16350_YACCL_OUT); +static IIO_DEV_ATTR_ACCEL_Z(adis16350_read_14bit_signed, + ADIS16350_ZACCL_OUT); +static IIO_CONST_ATTR(accel_scale, "0.00333"); + +static IIO_DEVICE_ATTR(temp_x_raw, S_IRUGO, adis16350_read_12bit_signed, + NULL, ADIS16350_XTEMP_OUT); +static IIO_DEVICE_ATTR(temp_y_raw, S_IRUGO, adis16350_read_12bit_signed, + NULL, ADIS16350_YTEMP_OUT); +static IIO_DEVICE_ATTR(temp_z_raw, S_IRUGO, adis16350_read_12bit_signed, + NULL, ADIS16350_ZTEMP_OUT); +static IIO_CONST_ATTR(temp_scale, "0.0005"); + +static IIO_DEV_ATTR_IN_RAW(0, adis16350_read_12bit_unsigned, + ADIS16350_AUX_ADC); +static IIO_CONST_ATTR(in0_scale, "0.000806"); + +static IIO_DEV_ATTR_SAMP_FREQ(S_IWUSR | S_IRUGO, + adis16350_read_frequency, + adis16350_write_frequency); + +static IIO_DEVICE_ATTR(reset, S_IWUSR, NULL, + adis16350_write_reset, 0); + +static IIO_CONST_ATTR_AVAIL_SAMP_FREQ("409 546 819 1638"); + +static IIO_CONST_ATTR(name, "adis16350"); + +static struct attribute *adis16350_attributes[] = { + &iio_dev_attr_accel_x_offset.dev_attr.attr, + &iio_dev_attr_accel_y_offset.dev_attr.attr, + &iio_dev_attr_accel_z_offset.dev_attr.attr, + &iio_dev_attr_in_supply_raw.dev_attr.attr, + &iio_const_attr_in_supply_scale.dev_attr.attr, + &iio_dev_attr_gyro_x_raw.dev_attr.attr, + &iio_dev_attr_gyro_y_raw.dev_attr.attr, + &iio_dev_attr_gyro_z_raw.dev_attr.attr, + &iio_const_attr_gyro_scale.dev_attr.attr, + &iio_dev_attr_accel_x_raw.dev_attr.attr, + &iio_dev_attr_accel_y_raw.dev_attr.attr, + &iio_dev_attr_accel_z_raw.dev_attr.attr, + &iio_const_attr_accel_scale.dev_attr.attr, + &iio_dev_attr_temp_x_raw.dev_attr.attr, + &iio_dev_attr_temp_y_raw.dev_attr.attr, + &iio_dev_attr_temp_z_raw.dev_attr.attr, + &iio_const_attr_temp_scale.dev_attr.attr, + &iio_dev_attr_in0_raw.dev_attr.attr, + &iio_const_attr_in0_scale.dev_attr.attr, + &iio_dev_attr_sampling_frequency.dev_attr.attr, + &iio_const_attr_available_sampling_frequency.dev_attr.attr, + &iio_dev_attr_reset.dev_attr.attr, + &iio_const_attr_name.dev_attr.attr, + NULL +}; + +static const struct attribute_group adis16350_attribute_group = { + .attrs = adis16350_attributes, +}; + +static struct attribute *adis16350_event_attributes[] = { + NULL, +}; + +static struct attribute_group adis16350_event_attribute_group = { + .attrs = adis16350_event_attributes, +}; + +static int __devinit adis16350_probe(struct spi_device *spi) +{ + int ret, regdone = 0; + struct adis16350_state *st = kzalloc(sizeof *st, GFP_KERNEL); + if (!st) { + ret = -ENOMEM; + goto error_ret; + } + /* this is only used for removal purposes */ + spi_set_drvdata(spi, st); + + /* Allocate the comms buffers */ + st->rx = kzalloc(sizeof(*st->rx)*ADIS16350_MAX_RX, GFP_KERNEL); + if (st->rx == NULL) { + ret = -ENOMEM; + goto error_free_st; + } + st->tx = kzalloc(sizeof(*st->tx)*ADIS16350_MAX_TX, GFP_KERNEL); + if (st->tx == NULL) { + ret = -ENOMEM; + goto error_free_rx; + } + st->us = spi; + mutex_init(&st->buf_lock); + /* setup the industrialio driver allocated elements */ + st->indio_dev = iio_allocate_device(); + if (st->indio_dev == NULL) { + ret = -ENOMEM; + goto error_free_tx; + } + + st->indio_dev->dev.parent = &spi->dev; + st->indio_dev->num_interrupt_lines = 1; + st->indio_dev->event_attrs = &adis16350_event_attribute_group; + st->indio_dev->attrs = &adis16350_attribute_group; + st->indio_dev->dev_data = (void *)(st); + st->indio_dev->driver_module = THIS_MODULE; + st->indio_dev->modes = INDIO_DIRECT_MODE; + + ret = adis16350_configure_ring(st->indio_dev); + if (ret) + goto error_free_dev; + + ret = iio_device_register(st->indio_dev); + if (ret) + goto error_unreg_ring_funcs; + regdone = 1; + + ret = adis16350_initialize_ring(st->indio_dev->ring); + if (ret) { + printk(KERN_ERR "failed to initialize the ring\n"); + goto error_unreg_ring_funcs; + } + + if (spi->irq) { + ret = iio_register_interrupt_line(spi->irq, + st->indio_dev, + 0, + IRQF_TRIGGER_RISING, + "adis16350"); + if (ret) + goto error_uninitialize_ring; + + ret = adis16350_probe_trigger(st->indio_dev); + if (ret) + goto error_unregister_line; + } + + /* Get the device into a sane initial state */ + ret = adis16350_initial_setup(st); + if (ret) + goto error_remove_trigger; + return 0; + +error_remove_trigger: + adis16350_remove_trigger(st->indio_dev); +error_unregister_line: + if (spi->irq) + iio_unregister_interrupt_line(st->indio_dev, 0); +error_uninitialize_ring: + adis16350_uninitialize_ring(st->indio_dev->ring); +error_unreg_ring_funcs: + adis16350_unconfigure_ring(st->indio_dev); +error_free_dev: + if (regdone) + iio_device_unregister(st->indio_dev); + else + iio_free_device(st->indio_dev); +error_free_tx: + kfree(st->tx); +error_free_rx: + kfree(st->rx); +error_free_st: + kfree(st); +error_ret: + return ret; +} + +static int adis16350_remove(struct spi_device *spi) +{ + int ret; + struct adis16350_state *st = spi_get_drvdata(spi); + struct iio_dev *indio_dev = st->indio_dev; + + ret = adis16350_stop_device(&(indio_dev->dev)); + if (ret) + goto err_ret; + + flush_scheduled_work(); + + adis16350_remove_trigger(indio_dev); + if (spi->irq) + iio_unregister_interrupt_line(indio_dev, 0); + + adis16350_uninitialize_ring(indio_dev->ring); + iio_device_unregister(indio_dev); + adis16350_unconfigure_ring(indio_dev); + kfree(st->tx); + kfree(st->rx); + kfree(st); + + return 0; + +err_ret: + return ret; +} + +static const struct spi_device_id adis16350_id[] = { + {"adis16350", 0}, + {"adis16354", 0}, + {"adis16355", 0}, + {"adis16360", 0}, + {"adis16362", 0}, + {"adis16364", 0}, + {"adis16365", 0}, + {} +}; + +static struct spi_driver adis16350_driver = { + .driver = { + .name = "adis16350", + .owner = THIS_MODULE, + }, + .probe = adis16350_probe, + .remove = __devexit_p(adis16350_remove), + .id_table = adis16350_id, +}; + +static __init int adis16350_init(void) +{ + return spi_register_driver(&adis16350_driver); +} +module_init(adis16350_init); + +static __exit void adis16350_exit(void) +{ + spi_unregister_driver(&adis16350_driver); +} +module_exit(adis16350_exit); + +MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>"); +MODULE_DESCRIPTION("Analog Devices ADIS16350/54/55/60/62/64/65 IMU SPI driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/iio/imu/adis16350_ring.c b/drivers/staging/iio/imu/adis16350_ring.c new file mode 100644 index 0000000..5e9716e --- /dev/null +++ b/drivers/staging/iio/imu/adis16350_ring.c @@ -0,0 +1,286 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../iio.h" +#include "../sysfs.h" +#include "../ring_sw.h" +#include "../accel/accel.h" +#include "../trigger.h" +#include "adis16350.h" + +/** + * combine_8_to_16() utility function to munge to u8s into u16 + **/ +static inline u16 combine_8_to_16(u8 lower, u8 upper) +{ + u16 _lower = lower; + u16 _upper = upper; + return _lower | (_upper << 8); +} + +static IIO_SCAN_EL_C(supply, ADIS16350_SCAN_SUPPLY, IIO_UNSIGNED(12), + ADIS16350_SUPPLY_OUT, NULL); + +static IIO_SCAN_EL_C(gyro_x, ADIS16350_SCAN_GYRO_X, IIO_SIGNED(14), + ADIS16350_XGYRO_OUT, NULL); +static IIO_SCAN_EL_C(gyro_y, ADIS16350_SCAN_GYRO_Y, IIO_SIGNED(14), + ADIS16350_YGYRO_OUT, NULL); +static IIO_SCAN_EL_C(gyro_z, ADIS16350_SCAN_GYRO_Z, IIO_SIGNED(14), + ADIS16350_ZGYRO_OUT, NULL); + +static IIO_SCAN_EL_C(accel_x, ADIS16350_SCAN_ACC_X, IIO_SIGNED(14), + ADIS16350_XACCL_OUT, NULL); +static IIO_SCAN_EL_C(accel_y, ADIS16350_SCAN_ACC_Y, IIO_SIGNED(14), + ADIS16350_YACCL_OUT, NULL); +static IIO_SCAN_EL_C(accel_z, ADIS16350_SCAN_ACC_Z, IIO_SIGNED(14), + ADIS16350_ZACCL_OUT, NULL); + +static IIO_SCAN_EL_C(temp_x, ADIS16350_SCAN_TEMP_X, IIO_SIGNED(12), + ADIS16350_XTEMP_OUT, NULL); +static IIO_SCAN_EL_C(temp_y, ADIS16350_SCAN_TEMP_Y, IIO_SIGNED(12), + ADIS16350_YTEMP_OUT, NULL); +static IIO_SCAN_EL_C(temp_z, ADIS16350_SCAN_TEMP_Z, IIO_SIGNED(12), + ADIS16350_ZTEMP_OUT, NULL); + +static IIO_SCAN_EL_C(adc_0, ADIS16350_SCAN_ADC_0, IIO_UNSIGNED(12), + ADIS16350_AUX_ADC, NULL); + +static IIO_SCAN_EL_TIMESTAMP(11); + +static struct attribute *adis16350_scan_el_attrs[] = { + &iio_scan_el_supply.dev_attr.attr, + &iio_scan_el_gyro_x.dev_attr.attr, + &iio_scan_el_gyro_y.dev_attr.attr, + &iio_scan_el_gyro_z.dev_attr.attr, + &iio_scan_el_accel_x.dev_attr.attr, + &iio_scan_el_accel_y.dev_attr.attr, + &iio_scan_el_accel_z.dev_attr.attr, + &iio_scan_el_temp_x.dev_attr.attr, + &iio_scan_el_temp_y.dev_attr.attr, + &iio_scan_el_temp_z.dev_attr.attr, + &iio_scan_el_adc_0.dev_attr.attr, + &iio_scan_el_timestamp.dev_attr.attr, + NULL, +}; + +static struct attribute_group adis16350_scan_el_group = { + .attrs = adis16350_scan_el_attrs, + .name = "scan_elements", +}; + +/** + * adis16350_poll_func_th() top half interrupt handler called by trigger + * @private_data: iio_dev + **/ +static void adis16350_poll_func_th(struct iio_dev *indio_dev) +{ + struct adis16350_state *st = iio_dev_get_devdata(indio_dev); + st->last_timestamp = indio_dev->trig->timestamp; + schedule_work(&st->work_trigger_to_ring); +} + +/** + * adis16350_spi_read_burst() - read all data registers + * @dev: device associated with child of actual device (iio_dev or iio_trig) + * @rx: somewhere to pass back the value read (min size is 24 bytes) + **/ +static int adis16350_spi_read_burst(struct device *dev, u8 *rx) +{ + struct spi_message msg; + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct adis16350_state *st = iio_dev_get_devdata(indio_dev); + u32 old_speed_hz = st->us->max_speed_hz; + int ret; + + struct spi_transfer xfers[] = { + { + .tx_buf = st->tx, + .bits_per_word = 8, + .len = 2, + .cs_change = 0, + }, { + .rx_buf = rx, + .bits_per_word = 8, + .len = 22, + .cs_change = 0, + }, + }; + + mutex_lock(&st->buf_lock); + st->tx[0] = ADIS16350_READ_REG(ADIS16350_GLOB_CMD); + st->tx[1] = 0; + + spi_message_init(&msg); + spi_message_add_tail(&xfers[0], &msg); + spi_message_add_tail(&xfers[1], &msg); + + st->us->max_speed_hz = ADIS16350_SPI_BURST; + spi_setup(st->us); + + ret = spi_sync(st->us, &msg); + if (ret) + dev_err(&st->us->dev, "problem when burst reading"); + + st->us->max_speed_hz = old_speed_hz; + spi_setup(st->us); + mutex_unlock(&st->buf_lock); + return ret; +} + +/* Whilst this makes a lot of calls to iio_sw_ring functions - it is to device + * specific to be rolled into the core. + */ +static void adis16350_trigger_bh_to_ring(struct work_struct *work_s) +{ + struct adis16350_state *st + = container_of(work_s, struct adis16350_state, + work_trigger_to_ring); + + int i = 0; + s16 *data; + size_t datasize = st->indio_dev + ->ring->access.get_bpd(st->indio_dev->ring); + + data = kmalloc(datasize , GFP_KERNEL); + if (data == NULL) { + dev_err(&st->us->dev, "memory alloc failed in ring bh"); + return; + } + + if (st->indio_dev->scan_count) + if (adis16350_spi_read_burst(&st->indio_dev->dev, st->rx) >= 0) + for (; i < st->indio_dev->scan_count; i++) { + data[i] = combine_8_to_16(st->rx[i*2+1], + st->rx[i*2]); + } + + /* Guaranteed to be aligned with 8 byte boundary */ + if (st->indio_dev->scan_timestamp) + *((s64 *)(data + ((i + 3)/4)*4)) = st->last_timestamp; + + st->indio_dev->ring->access.store_to(st->indio_dev->ring, + (u8 *)data, + st->last_timestamp); + + iio_trigger_notify_done(st->indio_dev->trig); + kfree(data); + + return; +} + +static int adis16350_data_rdy_ring_preenable(struct iio_dev *indio_dev) +{ + size_t size; + dev_dbg(&indio_dev->dev, "%s\n", __func__); + /* Check if there are any scan elements enabled, if not fail*/ + if (!(indio_dev->scan_count || indio_dev->scan_timestamp)) + return -EINVAL; + + if (indio_dev->ring->access.set_bpd) { + if (indio_dev->scan_timestamp) + if (indio_dev->scan_count) + /* Timestamp (aligned sizeof(s64) and data */ + size = (((indio_dev->scan_count * sizeof(s16)) + + sizeof(s64) - 1) + & ~(sizeof(s64) - 1)) + + sizeof(s64); + else /* Timestamp only */ + size = sizeof(s64); + else /* Data only */ + size = indio_dev->scan_count*sizeof(s16); + indio_dev->ring->access.set_bpd(indio_dev->ring, size); + } + + return 0; +} + +static int adis16350_data_rdy_ring_postenable(struct iio_dev *indio_dev) +{ + return indio_dev->trig + ? iio_trigger_attach_poll_func(indio_dev->trig, + indio_dev->pollfunc) + : 0; +} + +static int adis16350_data_rdy_ring_predisable(struct iio_dev *indio_dev) +{ + return indio_dev->trig + ? iio_trigger_dettach_poll_func(indio_dev->trig, + indio_dev->pollfunc) + : 0; +} + +void adis16350_unconfigure_ring(struct iio_dev *indio_dev) +{ + kfree(indio_dev->pollfunc); + iio_sw_rb_free(indio_dev->ring); +} + +int adis16350_configure_ring(struct iio_dev *indio_dev) +{ + int ret = 0; + struct adis16350_state *st = indio_dev->dev_data; + struct iio_ring_buffer *ring; + INIT_WORK(&st->work_trigger_to_ring, adis16350_trigger_bh_to_ring); + /* Set default scan mode */ + + iio_scan_mask_set(indio_dev, iio_scan_el_supply.number); + iio_scan_mask_set(indio_dev, iio_scan_el_gyro_x.number); + iio_scan_mask_set(indio_dev, iio_scan_el_gyro_y.number); + iio_scan_mask_set(indio_dev, iio_scan_el_gyro_z.number); + iio_scan_mask_set(indio_dev, iio_scan_el_accel_x.number); + iio_scan_mask_set(indio_dev, iio_scan_el_accel_y.number); + iio_scan_mask_set(indio_dev, iio_scan_el_accel_z.number); + iio_scan_mask_set(indio_dev, iio_scan_el_temp_x.number); + iio_scan_mask_set(indio_dev, iio_scan_el_temp_y.number); + iio_scan_mask_set(indio_dev, iio_scan_el_temp_z.number); + iio_scan_mask_set(indio_dev, iio_scan_el_adc_0.number); + indio_dev->scan_timestamp = true; + + indio_dev->scan_el_attrs = &adis16350_scan_el_group; + + ring = iio_sw_rb_allocate(indio_dev); + if (!ring) { + ret = -ENOMEM; + return ret; + } + indio_dev->ring = ring; + /* Effectively select the ring buffer implementation */ + iio_ring_sw_register_funcs(&ring->access); + ring->preenable = &adis16350_data_rdy_ring_preenable; + ring->postenable = &adis16350_data_rdy_ring_postenable; + ring->predisable = &adis16350_data_rdy_ring_predisable; + ring->owner = THIS_MODULE; + + indio_dev->pollfunc = kzalloc(sizeof(*indio_dev->pollfunc), GFP_KERNEL); + if (indio_dev->pollfunc == NULL) { + ret = -ENOMEM; + goto error_iio_sw_rb_free;; + } + indio_dev->pollfunc->poll_func_main = &adis16350_poll_func_th; + indio_dev->pollfunc->private_data = indio_dev; + indio_dev->modes |= INDIO_RING_TRIGGERED; + return 0; + +error_iio_sw_rb_free: + iio_sw_rb_free(indio_dev->ring); + return ret; +} + +int adis16350_initialize_ring(struct iio_ring_buffer *ring) +{ + return iio_ring_buffer_register(ring, 0); +} + +void adis16350_uninitialize_ring(struct iio_ring_buffer *ring) +{ + iio_ring_buffer_unregister(ring); +} diff --git a/drivers/staging/iio/imu/adis16350_trigger.c b/drivers/staging/iio/imu/adis16350_trigger.c new file mode 100644 index 0000000..1ffa75d --- /dev/null +++ b/drivers/staging/iio/imu/adis16350_trigger.c @@ -0,0 +1,127 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../iio.h" +#include "../sysfs.h" +#include "../trigger.h" +#include "adis16350.h" + +/** + * adis16350_data_rdy_trig_poll() the event handler for the data rdy trig + **/ +static int adis16350_data_rdy_trig_poll(struct iio_dev *dev_info, + int index, + s64 timestamp, + int no_test) +{ + struct adis16350_state *st = iio_dev_get_devdata(dev_info); + struct iio_trigger *trig = st->trig; + + trig->timestamp = timestamp; + iio_trigger_poll(trig); + + return IRQ_HANDLED; +} + +IIO_EVENT_SH(data_rdy_trig, &adis16350_data_rdy_trig_poll); + +static DEVICE_ATTR(name, S_IRUGO, iio_trigger_read_name, NULL); + +static struct attribute *adis16350_trigger_attrs[] = { + &dev_attr_name.attr, + NULL, +}; + +static const struct attribute_group adis16350_trigger_attr_group = { + .attrs = adis16350_trigger_attrs, +}; + +/** + * adis16350_data_rdy_trigger_set_state() set datardy interrupt state + **/ +static int adis16350_data_rdy_trigger_set_state(struct iio_trigger *trig, + bool state) +{ + struct adis16350_state *st = trig->private_data; + struct iio_dev *indio_dev = st->indio_dev; + int ret = 0; + + dev_dbg(&indio_dev->dev, "%s (%d)\n", __func__, state); + ret = adis16350_set_irq(&st->indio_dev->dev, state); + if (state == false) { + iio_remove_event_from_list(&iio_event_data_rdy_trig, + &indio_dev->interrupts[0] + ->ev_list); + /* possible quirk with handler currently worked around + by ensuring the work queue is empty */ + flush_scheduled_work(); + } else { + iio_add_event_to_list(&iio_event_data_rdy_trig, + &indio_dev->interrupts[0]->ev_list); + } + return ret; +} + +/** + * adis16350_trig_try_reen() try renabling irq for data rdy trigger + * @trig: the datardy trigger + **/ +static int adis16350_trig_try_reen(struct iio_trigger *trig) +{ + struct adis16350_state *st = trig->private_data; + enable_irq(st->us->irq); + /* irq reenabled so success! */ + return 0; +} + +int adis16350_probe_trigger(struct iio_dev *indio_dev) +{ + int ret; + struct adis16350_state *st = indio_dev->dev_data; + + st->trig = iio_allocate_trigger(); + st->trig->name = kmalloc(IIO_TRIGGER_NAME_LENGTH, GFP_KERNEL); + if (!st->trig->name) { + ret = -ENOMEM; + goto error_free_trig; + } + snprintf((char *)st->trig->name, + IIO_TRIGGER_NAME_LENGTH, + "adis16350-dev%d", indio_dev->id); + st->trig->dev.parent = &st->us->dev; + st->trig->owner = THIS_MODULE; + st->trig->private_data = st; + st->trig->set_trigger_state = &adis16350_data_rdy_trigger_set_state; + st->trig->try_reenable = &adis16350_trig_try_reen; + st->trig->control_attrs = &adis16350_trigger_attr_group; + ret = iio_trigger_register(st->trig); + + /* select default trigger */ + indio_dev->trig = st->trig; + if (ret) + goto error_free_trig_name; + + return 0; + +error_free_trig_name: + kfree(st->trig->name); +error_free_trig: + iio_free_trigger(st->trig); + + return ret; +} + +void adis16350_remove_trigger(struct iio_dev *indio_dev) +{ + struct adis16350_state *state = indio_dev->dev_data; + + iio_trigger_unregister(state->trig); + kfree(state->trig->name); + iio_free_trigger(state->trig); +} -- cgit v1.1