2011-03-31 14:52:55

by Jonathan Cameron

[permalink] [raw]
Subject: [RFC PATCH 00/21] IIO: Channel registration rework, buffer chardev combining and rewrite of triggers as 'virtual' irq_chips.

Dear All,

I'm afraid what in many ways makes sense as three separate
series have gotten rather intertwined. I can probably unpick
them but it will involve a lot of intermediate code in
lis3l02dq (which is our fiddly example for this set) that I'd
rather avoid. Hence lets have a guide to what various people
might be interested in:

1) Channel registration rework (this has previous been in linux-iio
but really comes into it's own after we remove various special
cases later in this code).

Patches 1, 2, 3, 21
(8) - cleanups Arnd Bergmann pointed out in passing.

2) Flattening together of (some) of the chardevs (buffer related ones).
As Arnd pointed out, there is really a use case for having multiple
watershed type events from ring buffers. Much better to have a
single one (be that at a controllable point). Firstly this removes
the need for the event escalation code. Second, this single 'event'
can then be indicated to user space purely using polling on the
access chrdev. This approach does need an additional attribute to
tell user space what the poll succeeding indicates (tbd).

I haven't for now merged the ring handling with the more general
event handling as Arnd suggested. This is for two reasons
1) It's much easier to debug this done in a couple of steps
2) The approach Arnd suggested may work well, but it is rather
different to how other bits of the kernel pass event type data
to user space. It's an interesting idea, but I'd rather any
discussion of that approach was separate from the obviously
big gains seen here.

Patches 4, 5, 6, 7, 17

3) Reworking the triggering infrastructure to use 'virtual' irq_chips
This approach was suggested by Thomas Gleixner.

Before we had explicit trigger consumer lists. This made for a very
clunky implementation when we considered moving things over to
threaded interrupts. Thomas pointed out we were reinventing the
wheel and suggested more or less what we have here (I hope ;)

Patches 9 and 10 are minor rearrangements of code in the one
driver I know of where the physical interrupt line for events
is the same as that for data ready signals (though not at the
same time).

In a rare situation we have complete control of these virtual
interrupts within the subsystem. As such we want to be able to
continue to build the subsystem as a module. This requires a
couple of additional exports in the generic irq core code and
also arm (for my test board anyway).
Patches 13 and 14 make these changes. I hope they won't prove
to controversial.

Patch 15 adds a board info built in element to the IIO subsystem
so we have a means of platform data telling us what interrupt
numbers are available for us to play with. Does anyone have
a better way of doing this? Patch 16 is an example of what
needs to go in board files.

18, 19 move lis3l02dq over to the threaded interrupts.

20 rips out the old approach. Until this patch current drivers
using the old system should still work. I make no guarantees
about combining consumers of both types though!


Note that this series sits on top of the cleanup series:
[PATCH 0/8] staging:iio:mixed bag of fixes and cleanups
http://marc.info/?l=linux-iio&m=130039556909192&w=2
on top of staging-next (which right now might mean mainline is
also fine).

There are clearly corners to be cleaned up and exact interfaces
elements to be pinned down, but I'd like to get some opinions
on what we have here.

All comments welcomed!

Note, wrt to the recent discussion on a strategy for moving IIO
out of staging, my intent is to pin down these big changes before
we attempt to move these elements out. Note that the initial
steps of that move may occur in parallel with these changes.

Some of these patches may only be applied during that move as
that is a sensible point to update and test every driver.

Thanks to Arnd and Thomas for their contributions to this.

Jonathan

Jonathan Cameron (21):
staging:iio: allow channels to be set up using a table of
iio_channel_spec structures.
staging:iio:lis3l02dq - experimental move to new channel_spec
approach.
staging:iio:max1363 - experimental move to channel_spec registration.
staging:iio: remove ability to escalate events.
staging:iio: Add polling of events on the ring access chrdev.
staging:iio: remove legacy event chrdev for the buffers
staging:iio: Buffer device flattening.
staging:iio:lis3l02dq: General cleanup
staging:iio: Push interrupt setup down into the drivers for event
lines.
staging:iio: lis3l02dq - separate entirely interrupt handling for
thesholds from that for the datardy signal.
staging:iio: Remove legacy event handling.
staging:iio:lis3l02dq make threshold interrupt threaded.
arm: irq: export set flags
irq: export handle_simple_irq and irq_to_desc to allow for virtual
irqs in IIO
staging:iio: Add infrastructure for irq_chip based triggers
stargate2 - add an IIO interrupt pool
staging:iio:Documentation generic_buffer.c update to new abi for
buffers.
staging:iio:ring_sw add function needed for threaded irq.
staging:iio:lis3l02dq move to threaded trigger handling.
staging:iio:trigger remove legacy pollfunc elements.
staging:iio: rip out scan_el attributes. Now handled as
iio_dev_attrs like everything else.

arch/arm/kernel/irq.c | 1 +
arch/arm/mach-pxa/stargate2.c | 6 +-
drivers/staging/Makefile | 2 +-
drivers/staging/iio/Documentation/generic_buffer.c | 53 +-
drivers/staging/iio/Kconfig | 12 +
drivers/staging/iio/Makefile | 2 +
drivers/staging/iio/accel/lis3l02dq.h | 25 +-
drivers/staging/iio/accel/lis3l02dq_core.c | 700 +++++-------
drivers/staging/iio/accel/lis3l02dq_ring.c | 421 +++----
drivers/staging/iio/adc/max1363.h | 8 +-
drivers/staging/iio/adc/max1363_core.c | 1277 ++++++++------------
drivers/staging/iio/adc/max1363_ring.c | 1 -
drivers/staging/iio/chrdev.h | 33 +-
drivers/staging/iio/iio-board-info.h | 24 +
drivers/staging/iio/iio-core.h | 13 +
drivers/staging/iio/iio.h | 250 +++--
drivers/staging/iio/industrialio-board-info.c | 25 +
drivers/staging/iio/industrialio-core.c | 724 ++++++++---
drivers/staging/iio/industrialio-ring.c | 315 +++---
drivers/staging/iio/industrialio-trigger.c | 150 ++--
drivers/staging/iio/ring_generic.h | 79 +-
drivers/staging/iio/ring_sw.c | 42 +-
drivers/staging/iio/ring_sw.h | 1 +
drivers/staging/iio/sysfs.h | 152 +--
drivers/staging/iio/trigger.h | 68 +-
kernel/irq/chip.c | 1 +
kernel/irq/irqdesc.c | 1 +
27 files changed, 2116 insertions(+), 2270 deletions(-)
create mode 100644 drivers/staging/iio/iio-board-info.h
create mode 100644 drivers/staging/iio/iio-core.h
create mode 100644 drivers/staging/iio/industrialio-board-info.c

--
1.7.3.4


2011-03-31 14:52:58

by Jonathan Cameron

[permalink] [raw]
Subject: [PATCH 04/21] staging:iio: remove ability to escalate events.

Whilst it is possible to output events to say buffers have passed
a particular level there are no obvious reasons to actually do so.

The upshot of this patch is that buffers will only ever have
one threshold turned on at a time.

This will need to propogated into some drivers (e.g. sca3000)

Signed-off-by: Jonathan Cameron <[email protected]>
---
drivers/staging/iio/chrdev.h | 13 -------------
drivers/staging/iio/iio.h | 16 ++--------------
drivers/staging/iio/industrialio-core.c | 30 ++----------------------------
drivers/staging/iio/industrialio-ring.c | 23 ++---------------------
drivers/staging/iio/ring_generic.h | 3 ---
drivers/staging/iio/ring_sw.c | 14 +-------------
6 files changed, 7 insertions(+), 92 deletions(-)

diff --git a/drivers/staging/iio/chrdev.h b/drivers/staging/iio/chrdev.h
index 95b439e..c1e86a9 100644
--- a/drivers/staging/iio/chrdev.h
+++ b/drivers/staging/iio/chrdev.h
@@ -45,23 +45,10 @@ struct iio_event_data {
* struct iio_detected_event_list - list element for events that have occured
* @list: linked list header
* @ev: the event itself
- * @shared_pointer: used when the event is shared - i.e. can be escallated
- * on demand (eg ring buffer 50%->100% full)
*/
struct iio_detected_event_list {
struct list_head list;
struct iio_event_data ev;
- struct iio_shared_ev_pointer *shared_pointer;
-};
-/**
- * struct iio_shared_ev_pointer - allows shared events to identify if currently
- * in the detected event list
- * @ev_p: pointer to detected event list element (null if not in list)
- * @lock: protect this element to prevent simultaneous edit and remove
- */
-struct iio_shared_ev_pointer {
- struct iio_detected_event_list *ev_p;
- spinlock_t lock;
};

/**
diff --git a/drivers/staging/iio/iio.h b/drivers/staging/iio/iio.h
index 578d078..1496f0b 100644
--- a/drivers/staging/iio/iio.h
+++ b/drivers/staging/iio/iio.h
@@ -346,22 +346,10 @@ int iio_push_event(struct iio_dev *dev_info,
* @ev_int: the event interface to which we are pushing the event
* @ev_code: the outgoing event code
* @timestamp: timestamp of the event
- * @shared_pointer_p: the shared event pointer
**/
int __iio_push_event(struct iio_event_interface *ev_int,
- int ev_code,
- s64 timestamp,
- struct iio_shared_ev_pointer*
- shared_pointer_p);
-/**
- * __iio_change_event() - change an event code in case of event escalation
- * @ev: the event to be changed
- * @ev_code: new event code
- * @timestamp: new timestamp
- **/
-void __iio_change_event(struct iio_detected_event_list *ev,
- int ev_code,
- s64 timestamp);
+ int ev_code,
+ s64 timestamp);

/**
* iio_setup_ev_int() - configure an event interface (chrdev)
diff --git a/drivers/staging/iio/industrialio-core.c b/drivers/staging/iio/industrialio-core.c
index 809588f..5b7b5df 100644
--- a/drivers/staging/iio/industrialio-core.c
+++ b/drivers/staging/iio/industrialio-core.c
@@ -66,15 +66,6 @@ static const char * const iio_chan_info_postfix[] = {
[IIO_CHAN_INFO_CALIBBIAS_SHARED/2] = "calibbias",
};

-void __iio_change_event(struct iio_detected_event_list *ev,
- int ev_code,
- s64 timestamp)
-{
- ev->ev.id = ev_code;
- ev->ev.timestamp = timestamp;
-}
-EXPORT_SYMBOL(__iio_change_event);
-
/* Used both in the interrupt line put events and the ring buffer ones */

/* Note that in it's current form someone has to be listening before events
@@ -83,9 +74,7 @@ EXPORT_SYMBOL(__iio_change_event);
*/
int __iio_push_event(struct iio_event_interface *ev_int,
int ev_code,
- s64 timestamp,
- struct iio_shared_ev_pointer *
- shared_pointer_p)
+ s64 timestamp)
{
struct iio_detected_event_list *ev;
int ret = 0;
@@ -105,9 +94,6 @@ int __iio_push_event(struct iio_event_interface *ev_int,
}
ev->ev.id = ev_code;
ev->ev.timestamp = timestamp;
- ev->shared_pointer = shared_pointer_p;
- if (ev->shared_pointer)
- shared_pointer_p->ev_p = ev;

list_add_tail(&ev->list, &ev_int->det_events.list);
ev_int->current_events++;
@@ -127,7 +113,7 @@ int iio_push_event(struct iio_dev *dev_info,
s64 timestamp)
{
return __iio_push_event(&dev_info->event_interfaces[ev_line],
- ev_code, timestamp, NULL);
+ ev_code, timestamp);
}
EXPORT_SYMBOL(iio_push_event);

@@ -295,18 +281,6 @@ static ssize_t iio_event_chrdev_read(struct file *filep,
list_del(&el->list);
ev_int->current_events--;
mutex_unlock(&ev_int->event_list_lock);
- /*
- * Possible concurency issue if an update of this event is on its way
- * through. May lead to new event being removed whilst the reported
- * event was the unescalated event. In typical use case this is not a
- * problem as userspace will say read half the buffer due to a 50%
- * full event which would make the correct 100% full incorrect anyway.
- */
- if (el->shared_pointer) {
- spin_lock(&el->shared_pointer->lock);
- (el->shared_pointer->ev_p) = NULL;
- spin_unlock(&el->shared_pointer->lock);
- }
kfree(el);

return len;
diff --git a/drivers/staging/iio/industrialio-ring.c b/drivers/staging/iio/industrialio-ring.c
index 1dfbc6e..466b9fa 100644
--- a/drivers/staging/iio/industrialio-ring.c
+++ b/drivers/staging/iio/industrialio-ring.c
@@ -27,28 +27,11 @@ int iio_push_ring_event(struct iio_ring_buffer *ring_buf,
s64 timestamp)
{
return __iio_push_event(&ring_buf->ev_int,
- event_code,
- timestamp,
- &ring_buf->shared_ev_pointer);
+ event_code,
+ timestamp);
}
EXPORT_SYMBOL(iio_push_ring_event);

-int iio_push_or_escallate_ring_event(struct iio_ring_buffer *ring_buf,
- int event_code,
- s64 timestamp)
-{
- if (ring_buf->shared_ev_pointer.ev_p)
- __iio_change_event(ring_buf->shared_ev_pointer.ev_p,
- event_code,
- timestamp);
- else
- return iio_push_ring_event(ring_buf,
- event_code,
- timestamp);
- return 0;
-}
-EXPORT_SYMBOL(iio_push_or_escallate_ring_event);
-
/**
* iio_ring_open() - chrdev file open for ring buffer access
*
@@ -228,8 +211,6 @@ void iio_ring_buffer_init(struct iio_ring_buffer *ring,
ring->indio_dev = dev_info;
ring->ev_int.private = ring;
ring->access_handler.private = ring;
- ring->shared_ev_pointer.ev_p = NULL;
- spin_lock_init(&ring->shared_ev_pointer.lock);
}
EXPORT_SYMBOL(iio_ring_buffer_init);

diff --git a/drivers/staging/iio/ring_generic.h b/drivers/staging/iio/ring_generic.h
index 6c7e073..320a11d 100644
--- a/drivers/staging/iio/ring_generic.h
+++ b/drivers/staging/iio/ring_generic.h
@@ -107,8 +107,6 @@ struct iio_ring_access_funcs {
* @scan_timestamp: [INTERN] does the scan mode include a timestamp
* @access_handler: [INTERN] chrdev access handling
* @ev_int: [INTERN] chrdev interface for the event chrdev
- * @shared_ev_pointer: [INTERN] the shared event pointer to allow escalation of
- * events
* @access: [DRIVER] ring access functions associated with the
* implementation.
* @preenable: [DRIVER] function to run prior to marking ring enabled
@@ -133,7 +131,6 @@ struct iio_ring_buffer {
bool scan_timestamp;
struct iio_handler access_handler;
struct iio_event_interface ev_int;
- struct iio_shared_ev_pointer shared_ev_pointer;
struct iio_ring_access_funcs access;
int (*preenable)(struct iio_dev *);
int (*postenable)(struct iio_dev *);
diff --git a/drivers/staging/iio/ring_sw.c b/drivers/staging/iio/ring_sw.c
index ea0015e..0004814 100644
--- a/drivers/staging/iio/ring_sw.c
+++ b/drivers/staging/iio/ring_sw.c
@@ -123,14 +123,6 @@ static int iio_store_to_sw_ring(struct iio_sw_ring_buffer *ring,
*/
if (change_test_ptr == ring->read_p)
ring->read_p = temp_ptr;
-
- spin_lock(&ring->buf.shared_ev_pointer.lock);
-
- ret = iio_push_or_escallate_ring_event(&ring->buf,
- IIO_EVENT_CODE_RING_100_FULL, timestamp);
- spin_unlock(&ring->buf.shared_ev_pointer.lock);
- if (ret)
- goto error_ret;
}
/* investigate if our event barrier has been passed */
/* There are definite 'issues' with this and chances of
@@ -140,15 +132,11 @@ static int iio_store_to_sw_ring(struct iio_sw_ring_buffer *ring,
if (ring->half_p == ring->data + ring->buf.length*ring->buf.bytes_per_datum)
ring->half_p = ring->data;
if (ring->half_p == ring->read_p) {
- spin_lock(&ring->buf.shared_ev_pointer.lock);
code = IIO_EVENT_CODE_RING_50_FULL;
ret = __iio_push_event(&ring->buf.ev_int,
code,
- timestamp,
- &ring->buf.shared_ev_pointer);
- spin_unlock(&ring->buf.shared_ev_pointer.lock);
+ timestamp);
}
-error_ret:
return ret;
}

--
1.7.3.4

2011-03-31 14:52:57

by Jonathan Cameron

[permalink] [raw]
Subject: [PATCH 05/21] staging:iio: Add polling of events on the ring access chrdev.

Staging one of combining the ring chrdevs.

Signed-off-by: Jonathan Cameron <[email protected]>
---
drivers/staging/iio/industrialio-ring.c | 29 ++++++++++++++++++++++++-----
drivers/staging/iio/ring_generic.h | 3 +++
drivers/staging/iio/ring_sw.c | 8 ++++++++
3 files changed, 35 insertions(+), 5 deletions(-)

diff --git a/drivers/staging/iio/industrialio-ring.c b/drivers/staging/iio/industrialio-ring.c
index 466b9fa..464fec2 100644
--- a/drivers/staging/iio/industrialio-ring.c
+++ b/drivers/staging/iio/industrialio-ring.c
@@ -18,6 +18,7 @@
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/slab.h>
+#include <linux/poll.h>

#include "iio.h"
#include "ring_generic.h"
@@ -90,10 +91,27 @@ static ssize_t iio_ring_read_first_n_outer(struct file *filp, char __user *buf,
return ret;
}

+/**
+ * iio_ring_poll() - poll the ring to find out if it has data
+ */
+static unsigned int iio_ring_poll(struct file *filp,
+ struct poll_table_struct *wait)
+{
+ struct iio_ring_buffer *rb = filp->private_data;
+ int ret = 0;
+
+ poll_wait(filp, &rb->pollq, wait);
+ if (rb->stufftoread)
+ return POLLIN | POLLRDNORM;
+ /* need a way of knowing if there may be enough data... */
+ return ret;
+}
+
static const struct file_operations iio_ring_fileops = {
.read = iio_ring_read_first_n_outer,
.release = iio_ring_release,
.open = iio_ring_open,
+ .poll = iio_ring_poll,
.owner = THIS_MODULE,
.llseek = noop_llseek,
};
@@ -211,6 +229,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;
+ init_waitqueue_head(&ring->pollq);
}
EXPORT_SYMBOL(iio_ring_buffer_init);

@@ -392,7 +411,7 @@ int iio_ring_buffer_register_ex(struct iio_ring_buffer *ring, int id,
ring->owner);

if (ret)
- goto error_free_ring_buffer_event_chrdev;
+ goto error_ret;

if (ring->scan_el_attrs) {
ret = sysfs_create_group(&ring->dev.kobj,
@@ -400,13 +419,13 @@ int iio_ring_buffer_register_ex(struct iio_ring_buffer *ring, int id,
if (ret) {
dev_err(&ring->dev,
"Failed to add sysfs scan elements\n");
- goto error_free_ring_buffer_event_chrdev;
+ goto error_free_ring_buffer_access_chrdev;
}
} else if (channels) {
ret = sysfs_create_group(&ring->dev.kobj,
&iio_scan_el_dummy_group);
if (ret)
- goto error_free_ring_buffer_event_chrdev;
+ goto error_free_ring_buffer_access_chrdev;
}


@@ -424,8 +443,8 @@ int iio_ring_buffer_register_ex(struct iio_ring_buffer *ring, int id,
return 0;
error_cleanup_dynamic:
__iio_ring_attr_cleanup(ring);
-error_free_ring_buffer_event_chrdev:
- __iio_free_ring_buffer_event_chrdev(ring);
+error_free_ring_buffer_access_chrdev:
+ __iio_free_ring_buffer_access_chrdev(ring);
error_remove_device:
device_del(&ring->dev);
error_ret:
diff --git a/drivers/staging/iio/ring_generic.h b/drivers/staging/iio/ring_generic.h
index 320a11d..37b5eda 100644
--- a/drivers/staging/iio/ring_generic.h
+++ b/drivers/staging/iio/ring_generic.h
@@ -139,6 +139,9 @@ struct iio_ring_buffer {

struct list_head scan_el_dev_attr_list;
struct list_head scan_el_en_attr_list;
+
+ wait_queue_head_t pollq;
+ bool stufftoread;
};

/**
diff --git a/drivers/staging/iio/ring_sw.c b/drivers/staging/iio/ring_sw.c
index 0004814..53c6774 100644
--- a/drivers/staging/iio/ring_sw.c
+++ b/drivers/staging/iio/ring_sw.c
@@ -12,6 +12,7 @@
#include <linux/module.h>
#include <linux/device.h>
#include <linux/workqueue.h>
+#include <linux/sched.h>
#include <linux/poll.h>
#include "ring_sw.h"
#include "trigger.h"
@@ -136,6 +137,8 @@ static int iio_store_to_sw_ring(struct iio_sw_ring_buffer *ring,
ret = __iio_push_event(&ring->buf.ev_int,
code,
timestamp);
+ ring->buf.stufftoread = true;
+ wake_up_interruptible(&ring->buf.pollq);
}
return ret;
}
@@ -261,6 +264,10 @@ int iio_read_first_n_sw_rb(struct iio_ring_buffer *r,
ret = -EFAULT;
goto error_free_data_cpy;
}
+
+ if (bytes_to_rip >= ring->buf.length*ring->buf.bytes_per_datum/2)
+ ring->buf.stufftoread = 0;
+
error_free_data_cpy:
kfree(data);
error_ret:
@@ -310,6 +317,7 @@ int iio_request_update_sw_rb(struct iio_ring_buffer *r)
int ret = 0;
struct iio_sw_ring_buffer *ring = iio_to_sw_ring(r);

+ r->stufftoread = false;
spin_lock(&ring->use_lock);
if (!ring->update_needed)
goto error_ret;
--
1.7.3.4

2011-03-31 14:53:30

by Jonathan Cameron

[permalink] [raw]
Subject: [PATCH 18/21] staging:iio:ring_sw add function needed for threaded irq.

Signed-off-by: Jonathan Cameron <[email protected]>
---
drivers/staging/iio/ring_sw.c | 14 ++++++++++----
drivers/staging/iio/ring_sw.h | 1 +
2 files changed, 11 insertions(+), 4 deletions(-)

diff --git a/drivers/staging/iio/ring_sw.c b/drivers/staging/iio/ring_sw.c
index 5fbf5ff..bc028ca 100644
--- a/drivers/staging/iio/ring_sw.c
+++ b/drivers/staging/iio/ring_sw.c
@@ -456,11 +456,8 @@ int iio_sw_ring_preenable(struct iio_dev *indio_dev)
}
EXPORT_SYMBOL(iio_sw_ring_preenable);

-void iio_sw_trigger_bh_to_ring(struct work_struct *work_s)
+void iio_sw_trigger_to_ring(struct iio_sw_ring_helper_state *st)
{
- struct iio_sw_ring_helper_state *st
- = container_of(work_s, struct iio_sw_ring_helper_state,
- work_trigger_to_ring);
struct iio_ring_buffer *ring = st->indio_dev->ring;
int len = 0;
size_t datasize = ring->access.get_bytes_per_datum(ring);
@@ -489,6 +486,15 @@ void iio_sw_trigger_bh_to_ring(struct work_struct *work_s)

return;
}
+EXPORT_SYMBOL(iio_sw_trigger_to_ring);
+
+void iio_sw_trigger_bh_to_ring(struct work_struct *work_s)
+{
+ struct iio_sw_ring_helper_state *st
+ = container_of(work_s, struct iio_sw_ring_helper_state,
+ work_trigger_to_ring);
+ iio_sw_trigger_to_ring(st);
+}
EXPORT_SYMBOL(iio_sw_trigger_bh_to_ring);

void iio_sw_poll_func_th(struct iio_dev *indio_dev, s64 time)
diff --git a/drivers/staging/iio/ring_sw.h b/drivers/staging/iio/ring_sw.h
index ee86020..5b6a802 100644
--- a/drivers/staging/iio/ring_sw.h
+++ b/drivers/staging/iio/ring_sw.h
@@ -218,6 +218,7 @@ struct iio_sw_ring_helper_state {

void iio_sw_poll_func_th(struct iio_dev *indio_dev, s64 time);
void iio_sw_trigger_bh_to_ring(struct work_struct *work_s);
+void iio_sw_trigger_to_ring(struct iio_sw_ring_helper_state *st);

#else /* CONFIG_IIO_RING_BUFFER*/
struct iio_sw_ring_helper_state {
--
1.7.3.4

2011-03-31 14:53:28

by Jonathan Cameron

[permalink] [raw]
Subject: [PATCH 20/21] staging:iio:trigger remove legacy pollfunc elements.

Signed-off-by: Jonathan Cameron <[email protected]>
---
drivers/staging/iio/industrialio-trigger.c | 98 +++-------------------------
drivers/staging/iio/trigger.h | 28 +-------
2 files changed, 14 insertions(+), 112 deletions(-)

diff --git a/drivers/staging/iio/industrialio-trigger.c b/drivers/staging/iio/industrialio-trigger.c
index 8100822..a350f25 100644
--- a/drivers/staging/iio/industrialio-trigger.c
+++ b/drivers/staging/iio/industrialio-trigger.c
@@ -164,21 +164,6 @@ static struct iio_trigger *iio_trigger_find_by_name(const char *name,
void iio_trigger_poll(struct iio_trigger *trig, s64 time)
{
int i;
- struct iio_poll_func *pf_cursor;
-
- list_for_each_entry(pf_cursor, &trig->pollfunc_list, list) {
- if (pf_cursor->poll_func_immediate) {
- pf_cursor->poll_func_immediate(pf_cursor->private_data);
- trig->use_count++;
- }
- }
- list_for_each_entry(pf_cursor, &trig->pollfunc_list, list) {
- if (pf_cursor->poll_func_main) {
- pf_cursor->poll_func_main(pf_cursor->private_data,
- time);
- trig->use_count++;
- }
- }
for (i = 0; i < CONFIG_IIO_CONSUMERS_PER_TRIGGER; i++)
if (trig->subirqs[i].enabled) {
trig->use_count++;
@@ -223,24 +208,13 @@ int iio_trigger_attach_poll_func(struct iio_trigger *trig,
struct iio_poll_func *pf)
{
int ret = 0;
- unsigned long flags;
-
- if (pf->handler) {
- pf->irq = iio_trigger_get_irq(trig);
- ret = request_threaded_irq(pf->irq, NULL, pf->handler,
- pf->type, pf->name,
- pf);
- } else {
- spin_lock_irqsave(&trig->pollfunc_list_lock, flags);
- list_add_tail(&pf->list, &trig->pollfunc_list);
- spin_unlock_irqrestore(&trig->pollfunc_list_lock, flags);
- }
+
+ pf->irq = iio_trigger_get_irq(trig);
+ ret = request_threaded_irq(pf->irq, NULL, pf->handler,
+ pf->type, pf->name,
+ pf);
if (trig->set_trigger_state)
ret = trig->set_trigger_state(trig, true);
- if (ret) {
- printk(KERN_ERR "set trigger state failed\n");
- list_del(&pf->list);
- }

return ret;
}
@@ -249,46 +223,11 @@ EXPORT_SYMBOL(iio_trigger_attach_poll_func);
int iio_trigger_dettach_poll_func(struct iio_trigger *trig,
struct iio_poll_func *pf)
{
- struct iio_poll_func *pf_cursor;
- unsigned long flags;
- int ret = -EINVAL;
-
- if (pf->handler) {
- ret = trig->set_trigger_state(trig, false);
- if (ret)
- goto error_ret;
- iio_trigger_put_irq(trig, pf->irq);
- free_irq(pf->irq, pf);
- } else {
- spin_lock_irqsave(&trig->pollfunc_list_lock, flags);
- list_for_each_entry(pf_cursor, &trig->pollfunc_list, list)
- if (pf_cursor == pf) {
- ret = 0;
- break;
- }
- if (!ret) {
- if (list_is_singular(&trig->pollfunc_list)
- && trig->set_trigger_state) {
- spin_unlock_irqrestore(&trig
- ->pollfunc_list_lock,
- flags);
- /* May sleep hence cannot hold the spin lock */
- ret = trig->set_trigger_state(trig, false);
- if (ret)
- goto error_ret;
- spin_lock_irqsave(&trig->pollfunc_list_lock,
- flags);
- }
- /*
- * Now we can delete safe in the knowledge that, if
- * this is the last pollfunc then we have disabled
- * the trigger anyway and so nothing should be able
- * to call the pollfunc.
- */
- list_del(&pf_cursor->list);
- }
- spin_unlock_irqrestore(&trig->pollfunc_list_lock, flags);
- }
+ int ret = trig->set_trigger_state(trig, false);
+ if (ret)
+ goto error_ret;
+ iio_trigger_put_irq(trig, pf->irq);
+ free_irq(pf->irq, pf);

error_ret:
return ret;
@@ -412,9 +351,6 @@ struct iio_trigger *iio_allocate_trigger_named(const char *name)
trig->dev.bus = &iio_bus_type;
device_initialize(&trig->dev);
dev_set_drvdata(&trig->dev, (void *)trig);
- spin_lock_init(&trig->pollfunc_list_lock);
- INIT_LIST_HEAD(&trig->list);
- INIT_LIST_HEAD(&trig->pollfunc_list);

if (name) {
mutex_init(&trig->pool_lock);
@@ -477,20 +413,6 @@ int iio_device_unregister_trigger_consumer(struct iio_dev *dev_info)
}
EXPORT_SYMBOL(iio_device_unregister_trigger_consumer);

-int iio_alloc_pollfunc(struct iio_dev *indio_dev,
- void (*immediate)(struct iio_dev *indio_dev),
- void (*main)(struct iio_dev *private_data, s64 time))
-{
- indio_dev->pollfunc = kzalloc(sizeof(*indio_dev->pollfunc), GFP_KERNEL);
- if (indio_dev->pollfunc == NULL)
- return -ENOMEM;
- indio_dev->pollfunc->poll_func_immediate = immediate;
- indio_dev->pollfunc->poll_func_main = main;
- indio_dev->pollfunc->private_data = indio_dev;
- return 0;
-}
-EXPORT_SYMBOL(iio_alloc_pollfunc);
-
int iio_triggered_ring_postenable(struct iio_dev *indio_dev)
{
return indio_dev->trig
diff --git a/drivers/staging/iio/trigger.h b/drivers/staging/iio/trigger.h
index 2e52004..f4a9b54 100644
--- a/drivers/staging/iio/trigger.h
+++ b/drivers/staging/iio/trigger.h
@@ -24,8 +24,6 @@ struct iio_subirq {
* @private_data: [DRIVER] device specific data
* @list: [INTERN] used in maintenance of global trigger list
* @alloc_list: [DRIVER] used for driver specific trigger list
- * @pollfunc_list_lock: [INTERN] protection of the polling function list
- * @pollfunc_list: [INTERN] list of functions to run on trigger.
* @control_attrs: [DRIVER] sysfs attributes relevant to trigger type
* @owner: [DRIVER] used to monitor usage count of the trigger.
* @use_count: use count for the trigger
@@ -41,8 +39,6 @@ struct iio_trigger {
void *private_data;
struct list_head list;
struct list_head alloc_list;
- spinlock_t pollfunc_list_lock;
- struct list_head pollfunc_list;
const struct attribute_group *control_attrs;
struct module *owner;
int use_count;
@@ -151,36 +147,20 @@ static inline void iio_trigger_put_irq(struct iio_trigger *trig, int irq)
/**
* struct iio_poll_func - poll function pair
*
- * @list: associate this with a triggers pollfunc_list
* @private_data: data specific to device (passed into poll func)
- * @poll_func_immediate: function in here is run first. They should be
- * extremely lightweight. Typically used for latch
- * control on sensor supporting it.
- * @poll_func_main: function in here is run after all immediates.
- * Reading from sensor etc typically involves
- * scheduling from here.
- *
- * The two stage approach used here is only important when multiple sensors are
- * being triggered by a single trigger. This really comes into its own with
- * simultaneous sampling devices where a simple latch command can be used to
- * make the device store the values on all inputs.
+ * @handler: the function that is actually run on trigger
+ * @type: the type of interrupt (basically if oneshot)
+ * @irq: the corresponding irq as allocated from the
+ * trigger pool
**/
struct iio_poll_func {
- struct list_head list;
void *private_data;
- void (*poll_func_immediate)(struct iio_dev *indio_dev);
- void (*poll_func_main)(struct iio_dev *private_data, s64 time);
-
irqreturn_t (*handler)(int irq, void *p);
int type;
char *name;
int irq;
};

-int iio_alloc_pollfunc(struct iio_dev *indio_dev,
- void (*immediate)(struct iio_dev *indio_dev),
- void (*main)(struct iio_dev *private_data, s64 time));
-
/*
* Two functions for common case where all that happens is a pollfunc
* is attached and detached from a trigger
--
1.7.3.4

2011-03-31 14:53:29

by Jonathan Cameron

[permalink] [raw]
Subject: [PATCH 19/21] staging:iio:lis3l02dq move to threaded trigger handling.

Signed-off-by: Jonathan Cameron <[email protected]>
---
drivers/staging/iio/accel/lis3l02dq.h | 2 -
drivers/staging/iio/accel/lis3l02dq_core.c | 2 -
drivers/staging/iio/accel/lis3l02dq_ring.c | 96 ++++++++++-----------------
3 files changed, 36 insertions(+), 64 deletions(-)

diff --git a/drivers/staging/iio/accel/lis3l02dq.h b/drivers/staging/iio/accel/lis3l02dq.h
index 6d73325..e7f64bd 100644
--- a/drivers/staging/iio/accel/lis3l02dq.h
+++ b/drivers/staging/iio/accel/lis3l02dq.h
@@ -150,7 +150,6 @@ Form of high byte dependant on justification set in ctrl reg */
* struct lis3l02dq_state - device instance specific data
* @helper: data and func pointer allowing generic functions
* @us: actual spi_device
- * @inter: used to check if new interrupt has been triggered
* @trig: data ready trigger registered with iio
* @tx: transmit buffer
* @rx: recieve buffer
@@ -159,7 +158,6 @@ Form of high byte dependant on justification set in ctrl reg */
struct lis3l02dq_state {
struct iio_sw_ring_helper_state help;
struct spi_device *us;
- bool inter;
struct iio_trigger *trig;
u8 *tx;
u8 *rx;
diff --git a/drivers/staging/iio/accel/lis3l02dq_core.c b/drivers/staging/iio/accel/lis3l02dq_core.c
index 2bded87..46f0633 100644
--- a/drivers/staging/iio/accel/lis3l02dq_core.c
+++ b/drivers/staging/iio/accel/lis3l02dq_core.c
@@ -15,7 +15,6 @@
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/gpio.h>
-#include <linux/workqueue.h>
#include <linux/mutex.h>
#include <linux/device.h>
#include <linux/kernel.h>
@@ -726,7 +725,6 @@ static int __devinit lis3l02dq_probe(struct spi_device *spi)
}

if (spi->irq && gpio_is_valid(irq_to_gpio(spi->irq)) > 0) {
- st->inter = 0;
ret = lis3l02dq_probe_trigger(st->help.indio_dev);
if (ret)
goto error_uninitialize_ring;
diff --git a/drivers/staging/iio/accel/lis3l02dq_ring.c b/drivers/staging/iio/accel/lis3l02dq_ring.c
index 9b15cad..04a0265 100644
--- a/drivers/staging/iio/accel/lis3l02dq_ring.c
+++ b/drivers/staging/iio/accel/lis3l02dq_ring.c
@@ -29,31 +29,10 @@ static inline u16 combine_8_to_16(u8 lower, u8 upper)
}

/**
- * lis3l02dq_poll_func_th() top half interrupt handler called by trigger
- * @private_data: iio_dev
- **/
-static void lis3l02dq_poll_func_th(struct iio_dev *indio_dev, s64 time)
-{
- struct iio_sw_ring_helper_state *h
- = iio_dev_get_devdata(indio_dev);
- struct lis3l02dq_state *st = lis3l02dq_h_to_s(h);
- /* in this case we need to slightly extend the helper function */
- iio_sw_poll_func_th(indio_dev, time);
-
- /* Indicate that this interrupt is being handled */
- /* Technically this is trigger related, but without this
- * handler running there is currently now way for the interrupt
- * to clear.
- */
- st->inter = 1;
-}
-
-/**
* lis3l02dq_data_rdy_trig_poll() the event handler for the data rdy trig
**/
static irqreturn_t lis3l02dq_data_rdy_trig_poll(int irq, void *private)
{
- disable_irq_nosync(irq);
iio_trigger_poll(private, iio_get_time_ns());

return IRQ_HANDLED;
@@ -153,15 +132,16 @@ static int lis3l02dq_read_all(struct lis3l02dq_state *st, u8 *rx_array)
return ret;
}

-static void lis3l02dq_trigger_bh_to_ring(struct work_struct *work_s)
+static irqreturn_t lis3l02dq_trigger_handler(int irq, void *p)
{
- struct iio_sw_ring_helper_state *h
- = container_of(work_s, struct iio_sw_ring_helper_state,
- work_trigger_to_ring);
- struct lis3l02dq_state *st = lis3l02dq_h_to_s(h);
+ struct iio_poll_func *pf = p;
+ struct iio_dev *indio_dev = pf->private_data;
+ struct iio_sw_ring_helper_state *h = iio_dev_get_devdata(indio_dev);

- st->inter = 0;
- iio_sw_trigger_bh_to_ring(work_s);
+ h->last_timestamp = iio_get_time_ns();
+ iio_sw_trigger_to_ring(h);
+
+ return IRQ_HANDLED;
}

static int lis3l02dq_get_ring_element(struct iio_sw_ring_helper_state *h,
@@ -271,10 +251,10 @@ static int lis3l02dq_data_rdy_trigger_set_state(struct iio_trigger *trig,
u8 t;
__lis3l02dq_write_data_ready_config(&st->help.indio_dev->dev, state);
if (state == false) {
- /* possible quirk with handler currently worked around
- by ensuring the work queue is empty */
- flush_scheduled_work();
- /* Clear any outstanding ready events */
+ /*
+ * A possible quirk with teh handler is currently worked around
+ * by ensuring outstanding read events are cleared.
+ */
ret = lis3l02dq_read_all(st, NULL);
}
lis3l02dq_spi_read_reg_8(st->help.indio_dev,
@@ -297,32 +277,15 @@ static const struct attribute_group lis3l02dq_trigger_attr_group = {
/**
* lis3l02dq_trig_try_reen() try renabling irq for data rdy trigger
* @trig: the datardy trigger
- *
- * As the trigger may occur on any data element being updated it is
- * really rather likely to occur during the read from the previous
- * trigger event. The only way to discover if this has occured on
- * boards not supporting level interrupts is to take a look at the line.
- * If it is indicating another interrupt and we don't seem to have a
- * handler looking at it, then we need to notify the core that we need
- * to tell the triggering core to try reading all these again.
- **/
+ */
static int lis3l02dq_trig_try_reen(struct iio_trigger *trig)
{
struct lis3l02dq_state *st = trig->private_data;
- enable_irq(st->us->irq);
/* If gpio still high (or high again) */
+ /* In theory possible we will need to do this several times */
if (gpio_get_value(irq_to_gpio(st->us->irq)))
- if (st->inter == 0) {
- /* already interrupt handler dealing with it */
- disable_irq_nosync(st->us->irq);
- if (st->inter == 1) {
- /* interrupt handler snuck in between test
- * and disable */
- enable_irq(st->us->irq);
- return 0;
- }
- return -EAGAIN;
- }
+ lis3l02dq_read_all(st, NULL);
+
/* irq reenabled so success! */
return 0;
}
@@ -333,14 +296,16 @@ int lis3l02dq_probe_trigger(struct iio_dev *indio_dev)
struct iio_sw_ring_helper_state *h
= iio_dev_get_devdata(indio_dev);
struct lis3l02dq_state *st = lis3l02dq_h_to_s(h);
+ char *name;

- st->trig = iio_allocate_trigger();
+ name = kasprintf(GFP_KERNEL,
+ "lis3l02dq-dev%d",
+ indio_dev->id);
+ st->trig = iio_allocate_trigger_named(name);
if (!st->trig)
return -ENOMEM;

- st->trig->name = kasprintf(GFP_KERNEL,
- "lis3l02dq-dev%d",
- indio_dev->id);
+ st->trig->name = name;
if (!st->trig->name) {
ret = -ENOMEM;
goto error_free_trig;
@@ -379,6 +344,7 @@ void lis3l02dq_remove_trigger(struct iio_dev *indio_dev)

void lis3l02dq_unconfigure_ring(struct iio_dev *indio_dev)
{
+ kfree(indio_dev->pollfunc->name);
kfree(indio_dev->pollfunc);
lis3l02dq_free_buf(indio_dev->ring);
}
@@ -458,7 +424,7 @@ int lis3l02dq_configure_ring(struct iio_dev *indio_dev)
int ret;
struct iio_sw_ring_helper_state *h = iio_dev_get_devdata(indio_dev);
struct iio_ring_buffer *ring;
- INIT_WORK(&h->work_trigger_to_ring, lis3l02dq_trigger_bh_to_ring);
+
h->get_ring_element = &lis3l02dq_get_ring_element;

ring = lis3l02dq_alloc_buf(indio_dev);
@@ -481,9 +447,19 @@ int lis3l02dq_configure_ring(struct iio_dev *indio_dev)
iio_scan_mask_set(ring, 1);
iio_scan_mask_set(ring, 2);

- ret = iio_alloc_pollfunc(indio_dev, NULL, &lis3l02dq_poll_func_th);
- if (ret)
+ /* Functions are NULL as we set handler below */
+ 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->private_data = indio_dev;
+ indio_dev->pollfunc->handler = &lis3l02dq_trigger_handler;
+ indio_dev->pollfunc->type = IRQF_ONESHOT;
+ indio_dev->pollfunc->name
+ = kasprintf(GFP_KERNEL, "lis3l02dq_consumer%d", indio_dev->id);
+
indio_dev->modes |= INDIO_RING_TRIGGERED;
return 0;

--
1.7.3.4

2011-03-31 14:53:26

by Jonathan Cameron

[permalink] [raw]
Subject: [PATCH 21/21] staging:iio: rip out scan_el attributes. Now handled as iio_dev_attrs like everything else.

Drivers have no need to use this functionality any more and we save a lot of
code by getting rid of it.

Signed-off-by: Jonathan Cameron <[email protected]>
---
drivers/staging/iio/industrialio-ring.c | 101 ++++++++-----------------------
drivers/staging/iio/ring_generic.h | 24 -------
2 files changed, 26 insertions(+), 99 deletions(-)

diff --git a/drivers/staging/iio/industrialio-ring.c b/drivers/staging/iio/industrialio-ring.c
index 07d64bc..815be33 100644
--- a/drivers/staging/iio/industrialio-ring.c
+++ b/drivers/staging/iio/industrialio-ring.c
@@ -193,53 +193,6 @@ static ssize_t iio_show_fixed_type(struct device *dev,
this_attr->c->scan_type.shift);
}

-static int __iio_add_chan_scan_elattr(const char *postfix,
- const char *group,
- struct iio_chan_spec *chan,
- struct device *dev,
- struct list_head *attr_list)
-{
- int ret;
- struct iio_scan_el *scan_el;
-
- scan_el = kzalloc(sizeof *scan_el, GFP_KERNEL);
- if (scan_el == NULL) {
- ret = -ENOMEM;
- goto error_ret;
- }
- if (chan->type != IIO_TIMESTAMP)
- ret = __iio_device_attr_init(&scan_el->dev_attr, postfix, chan,
- iio_scan_el_show,
- iio_scan_el_store, 0);
- else /*
- * Timestamp handled separately because it simplifies a lot of
- * drivers by ensuring they don't have to know its magic index
- */
- ret = __iio_device_attr_init(&scan_el->dev_attr, postfix, chan,
- iio_scan_el_ts_show,
- iio_scan_el_ts_store, 0);
- if (ret)
- goto error_free_scan_el;
-
- scan_el->number = chan->scan_index;
-
- ret = sysfs_add_file_to_group(&dev->kobj,
- &scan_el->dev_attr.attr,
- group);
- if (ret < 0)
- goto error_device_attr_deinit;
-
- list_add(&scan_el->l, attr_list);
-
- return 0;
-error_device_attr_deinit:
- __iio_device_attr_deinit(&scan_el->dev_attr);
-error_free_scan_el:
- kfree(scan_el);
-error_ret:
- return ret;
-}
-
static int iio_ring_add_channel_sysfs(struct iio_ring_buffer *ring,
struct iio_chan_spec *chan)
{
@@ -268,23 +221,28 @@ static int iio_ring_add_channel_sysfs(struct iio_ring_buffer *ring,
if (ret)
goto error_ret;

- ret = __iio_add_chan_scan_elattr("en", "scan_elements",
- chan, &ring->dev,
- &ring->scan_el_en_attr_list);
-
+ if (chan->type != IIO_TIMESTAMP)
+ ret = __iio_add_chan_devattr("en", "scan_elements",
+ chan,
+ &iio_scan_el_show,
+ &iio_scan_el_store,
+ chan->scan_index,
+ 0,
+ &ring->dev,
+ &ring->scan_el_dev_attr_list);
+ else
+ ret = __iio_add_chan_devattr("en", "scan_elements",
+ chan,
+ &iio_scan_el_ts_show,
+ &iio_scan_el_ts_store,
+ chan->scan_index,
+ 0,
+ &ring->dev,
+ &ring->scan_el_dev_attr_list);
error_ret:
return ret;
}

-static void iio_ring_remove_and_free_scan_el_attr(struct iio_ring_buffer *ring,
- struct iio_scan_el *p)
-{
- sysfs_remove_file_from_group(&ring->dev.kobj,
- &p->dev_attr.attr, "scan_elements");
- kfree(p->dev_attr.attr.name);
- kfree(p);
-}
-
static void iio_ring_remove_and_free_scan_dev_attr(struct iio_ring_buffer *ring,
struct iio_dev_attr *p)
{
@@ -306,15 +264,10 @@ static struct attribute_group iio_scan_el_dummy_group = {
static void __iio_ring_attr_cleanup(struct iio_ring_buffer *ring)
{
struct iio_dev_attr *p, *n;
- struct iio_scan_el *q, *m;
- int anydynamic = !(list_empty(&ring->scan_el_dev_attr_list) &&
- list_empty(&ring->scan_el_en_attr_list));
+ int anydynamic = !list_empty(&ring->scan_el_dev_attr_list);
list_for_each_entry_safe(p, n,
&ring->scan_el_dev_attr_list, l)
iio_ring_remove_and_free_scan_dev_attr(ring, p);
- list_for_each_entry_safe(q, m,
- &ring->scan_el_en_attr_list, l)
- iio_ring_remove_and_free_scan_el_attr(ring, q);

if (ring->scan_el_attrs)
sysfs_remove_group(&ring->dev.kobj,
@@ -352,7 +305,6 @@ int iio_ring_buffer_register_ex(struct iio_ring_buffer *ring, int id,
}

INIT_LIST_HEAD(&ring->scan_el_dev_attr_list);
- INIT_LIST_HEAD(&ring->scan_el_en_attr_list);
if (channels) {
/* new magic */
for (i = 0; i < num_channels; i++) {
@@ -554,9 +506,9 @@ ssize_t iio_scan_el_show(struct device *dev,
{
int ret;
struct iio_ring_buffer *ring = dev_get_drvdata(dev);
- struct iio_scan_el *this_el = to_iio_scan_el(attr);
+ struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);

- ret = iio_scan_mask_query(ring, this_el->number);
+ ret = iio_scan_mask_query(ring, this_attr->address);
if (ret < 0)
return ret;
return sprintf(buf, "%d\n", ret);
@@ -572,7 +524,7 @@ ssize_t iio_scan_el_store(struct device *dev,
bool state;
struct iio_ring_buffer *ring = dev_get_drvdata(dev);
struct iio_dev *indio_dev = ring->indio_dev;
- struct iio_scan_el *this_el = to_iio_scan_el(attr);
+ struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);

state = !(buf[0] == '0');
mutex_lock(&indio_dev->mlock);
@@ -580,20 +532,19 @@ ssize_t iio_scan_el_store(struct device *dev,
ret = -EBUSY;
goto error_ret;
}
- ret = iio_scan_mask_query(ring, this_el->number);
+ ret = iio_scan_mask_query(ring, this_attr->address);
if (ret < 0)
goto error_ret;
if (!state && ret) {
- ret = iio_scan_mask_clear(ring, this_el->number);
+ ret = iio_scan_mask_clear(ring, this_attr->address);
if (ret)
goto error_ret;
} else if (state && !ret) {
- ret = iio_scan_mask_set(ring, this_el->number);
+ ret = iio_scan_mask_set(ring, this_attr->address);
if (ret)
goto error_ret;
}
- if (this_el->set_state)
- ret = this_el->set_state(this_el, indio_dev, state);
+
error_ret:
mutex_unlock(&indio_dev->mlock);

diff --git a/drivers/staging/iio/ring_generic.h b/drivers/staging/iio/ring_generic.h
index ba441d9..801ec34 100644
--- a/drivers/staging/iio/ring_generic.h
+++ b/drivers/staging/iio/ring_generic.h
@@ -109,7 +109,6 @@ struct iio_ring_buffer {
int (*postdisable)(struct iio_dev *);

struct list_head scan_el_dev_attr_list;
- struct list_head scan_el_en_attr_list;

wait_queue_head_t pollq;
bool stufftoread;
@@ -138,29 +137,6 @@ static inline void __iio_update_ring_buffer(struct iio_ring_buffer *ring,
}

/**
- * struct iio_scan_el - an individual element of a scan
- * @dev_attr: control attribute (if directly controllable)
- * @number: unique identifier of element (used for bit mask)
- * @label: useful data for the scan el (often reg address)
- * @set_state: for some devices datardy signals are generated
- * for any enabled lines. This allows unwanted lines
- * to be disabled and hence not get in the way.
- **/
-struct iio_scan_el {
- struct device_attribute dev_attr;
- unsigned int number;
- unsigned int label;
- struct list_head l;
-
- int (*set_state)(struct iio_scan_el *scanel,
- struct iio_dev *dev_info,
- bool state);
-};
-
-#define to_iio_scan_el(_dev_attr) \
- container_of(_dev_attr, struct iio_scan_el, dev_attr);
-
-/**
* iio_scan_el_store() - sysfs scan element selection interface
* @dev: the target device
* @attr: the device attribute that is being processed
--
1.7.3.4

2011-03-31 14:54:24

by Jonathan Cameron

[permalink] [raw]
Subject: [PATCH 16/21] stargate2 - add an IIO interrupt pool

Included here as a somewhat obvious example and because it's my test platform.

Signed-off-by: Jonathan Cameron <[email protected]>
---
arch/arm/mach-pxa/stargate2.c | 6 ++++--
1 files changed, 4 insertions(+), 2 deletions(-)

diff --git a/arch/arm/mach-pxa/stargate2.c b/arch/arm/mach-pxa/stargate2.c
index cb5611d..3005b9c 100644
--- a/arch/arm/mach-pxa/stargate2.c
+++ b/arch/arm/mach-pxa/stargate2.c
@@ -56,8 +56,9 @@

#include "devices.h"
#include "generic.h"
-
-#define STARGATE_NR_IRQS (IRQ_BOARD_START + 8)
+#include "../../../drivers/staging/iio/iio-board-info.h"
+#define STARGATE_IIO_IRQ_POOL 16
+#define STARGATE_NR_IRQS (IRQ_BOARD_START + STARGATE_IIO_IRQ_POOL)

/* Bluetooth */
#define SG2_BT_RESET 81
@@ -408,6 +409,7 @@ static struct i2c_pxa_platform_data i2c_pdata = {

static void __init imote2_stargate2_init(void)
{
+ iio_set_irq_pool(IRQ_BOARD_START, STARGATE_IIO_IRQ_POOL);

pxa2xx_mfp_config(ARRAY_AND_SIZE(sg2_im2_unified_pin_config));

--
1.7.3.4

2011-03-31 14:54:19

by Jonathan Cameron

[permalink] [raw]
Subject: [PATCH 17/21] staging:iio:Documentation generic_buffer.c update to new abi for buffers.

Trivial space before newline fix incorporated.

Signed-off-by: Jonathan Cameron <[email protected]>
---
drivers/staging/iio/Documentation/generic_buffer.c | 53 +++++--------------
1 files changed, 14 insertions(+), 39 deletions(-)

diff --git a/drivers/staging/iio/Documentation/generic_buffer.c b/drivers/staging/iio/Documentation/generic_buffer.c
index eb72e95..3c41d7d 100644
--- a/drivers/staging/iio/Documentation/generic_buffer.c
+++ b/drivers/staging/iio/Documentation/generic_buffer.c
@@ -27,6 +27,7 @@
#include <sys/dir.h>
#include <linux/types.h>
#include <string.h>
+#include <poll.h>
#include "iio_utils.h"

/**
@@ -135,7 +136,7 @@ int main(int argc, char **argv)
ssize_t read_size;
struct iio_event_data dat;
int dev_num, trig_num;
- char *buffer_access, *buffer_event;
+ char *buffer_access;
int scan_size;
int noevents = 0;
char *dummy;
@@ -210,7 +211,7 @@ int main(int argc, char **argv)
*/
ret = build_channel_array(dev_dir_name, &infoarray, &num_channels);
if (ret) {
- printf("Problem reading scan element information \n");
+ printf("Problem reading scan element information\n");
goto error_free_triggername;
}

@@ -251,54 +252,32 @@ int main(int argc, char **argv)
}

ret = asprintf(&buffer_access,
- "/dev/device%d:buffer0:access0",
+ "/dev/device%d:buffer0",
dev_num);
if (ret < 0) {
ret = -ENOMEM;
goto error_free_data;
}

- ret = asprintf(&buffer_event, "/dev/device%d:buffer0:event0", dev_num);
- if (ret < 0) {
- ret = -ENOMEM;
- goto error_free_buffer_access;
- }
/* Attempt to open non blocking the access dev */
fp = open(buffer_access, O_RDONLY | O_NONBLOCK);
if (fp == -1) { /*If it isn't there make the node */
printf("Failed to open %s\n", buffer_access);
ret = -errno;
- goto error_free_buffer_event;
- }
- /* Attempt to open the event access dev (blocking this time) */
- fp_ev = fopen(buffer_event, "rb");
- if (fp_ev == NULL) {
- printf("Failed to open %s\n", buffer_event);
- ret = -errno;
- goto error_close_buffer_access;
+ goto error_free_buffer_access;
}

/* Wait for events 10 times */
for (j = 0; j < num_loops; j++) {
if (!noevents) {
- read_size = fread(&dat,
- 1,
- sizeof(struct iio_event_data),
- fp_ev);
- switch (dat.id) {
- case IIO_EVENT_CODE_RING_100_FULL:
- toread = buf_len;
- break;
- case IIO_EVENT_CODE_RING_75_FULL:
- toread = buf_len*3/4;
- break;
- case IIO_EVENT_CODE_RING_50_FULL:
- toread = buf_len/2;
- break;
- default:
- printf("Unexpecteded event code\n");
- continue;
- }
+ struct pollfd pfd = {
+ .fd = fp,
+ .events = POLLIN,
+ };
+
+ poll(&pfd, 1, -1);
+ toread = buf_len;
+
} else {
usleep(timedelay);
toread = 64;
@@ -320,22 +299,18 @@ int main(int argc, char **argv)
/* Stop the ring buffer */
ret = write_sysfs_int("enable", buf_dir_name, 0);
if (ret < 0)
- goto error_close_buffer_event;
+ goto error_close_buffer_access;

/* Disconnect from the trigger - just write a dummy name.*/
write_sysfs_string("trigger/current_trigger",
dev_dir_name, "NULL");

-error_close_buffer_event:
- fclose(fp_ev);
error_close_buffer_access:
close(fp);
error_free_data:
free(data);
error_free_buffer_access:
free(buffer_access);
-error_free_buffer_event:
- free(buffer_event);
error_free_buf_dir_name:
free(buf_dir_name);
error_free_triggername:
--
1.7.3.4

2011-03-31 14:54:22

by Jonathan Cameron

[permalink] [raw]
Subject: [PATCH 15/21] staging:iio: Add infrastructure for irq_chip based triggers

---
drivers/staging/Makefile | 2 +-
drivers/staging/iio/Kconfig | 12 ++
drivers/staging/iio/Makefile | 2 +
drivers/staging/iio/iio-board-info.h | 24 ++++
drivers/staging/iio/iio-core.h | 13 ++
drivers/staging/iio/iio.h | 13 ++
drivers/staging/iio/industrialio-board-info.c | 25 ++++
drivers/staging/iio/industrialio-core.c | 28 +++++
drivers/staging/iio/industrialio-trigger.c | 146 ++++++++++++++++++++-----
drivers/staging/iio/trigger.h | 40 +++++++-
10 files changed, 275 insertions(+), 30 deletions(-)

diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile
index cfd13cd..ee7594c 100644
--- a/drivers/staging/Makefile
+++ b/drivers/staging/Makefile
@@ -42,7 +42,7 @@ obj-$(CONFIG_HYPERV) += hv/
obj-$(CONFIG_VME_BUS) += vme/
obj-$(CONFIG_MRST_RAR_HANDLER) += memrar/
obj-$(CONFIG_DX_SEP) += sep/
-obj-$(CONFIG_IIO) += iio/
+obj-y += iio/
obj-$(CONFIG_CS5535_GPIO) += cs5535_gpio/
obj-$(CONFIG_ZRAM) += zram/
obj-$(CONFIG_XVMALLOC) += zram/
diff --git a/drivers/staging/iio/Kconfig b/drivers/staging/iio/Kconfig
index 6775bf9..df9aa08 100644
--- a/drivers/staging/iio/Kconfig
+++ b/drivers/staging/iio/Kconfig
@@ -12,6 +12,10 @@ menuconfig IIO
Documentation/industrialio for more information.
if IIO

+config IIO_BOARDINFO
+ boolean
+ default y
+
config IIO_RING_BUFFER
bool "Enable ring buffer support within IIO"
help
@@ -42,12 +46,20 @@ endif # IIO_RINGBUFFER

config IIO_TRIGGER
boolean "Enable triggered sampling support"
+ depends on IIO_BOARDINFO
help
Provides IIO core support for triggers. Currently these
are used to initialize capture of samples to push into
ring buffers. The triggers are effectively a 'capture
data now' interrupt.

+config IIO_CONSUMERS_PER_TRIGGER
+ int "Maximum number of consumers per trigger"
+ depends on IIO_TRIGGER
+ default "2"
+ help
+ This value controls the maximum number of consumers that a
+ given trigger may handle. Default is 2.

source "drivers/staging/iio/accel/Kconfig"
source "drivers/staging/iio/adc/Kconfig"
diff --git a/drivers/staging/iio/Makefile b/drivers/staging/iio/Makefile
index bb5c95c..37b4d12 100644
--- a/drivers/staging/iio/Makefile
+++ b/drivers/staging/iio/Makefile
@@ -2,6 +2,8 @@
# Makefile for the industrial I/O core.
#

+obj-$(CONFIG_IIO_BOARDINFO) += industrialio-board-info.o
+
obj-$(CONFIG_IIO) += industrialio.o
industrialio-y := industrialio-core.o
industrialio-$(CONFIG_IIO_RING_BUFFER) += industrialio-ring.o
diff --git a/drivers/staging/iio/iio-board-info.h b/drivers/staging/iio/iio-board-info.h
new file mode 100644
index 0000000..c608a4d
--- /dev/null
+++ b/drivers/staging/iio/iio-board-info.h
@@ -0,0 +1,24 @@
+/*
+ * Board information for IIO.
+ *
+ * Copyright (c) 2011 Jonathan Cameron <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifdef CONFIG_IIO_BOARDINFO
+
+/**
+ * iio_set_irq_pool() - provid iio with a pool of virtaul interrupts to use
+ * @start: start of the pool
+ * @num: number of interupts in the pool
+ **/
+void iio_set_irq_pool(int start, int num);
+
+#else
+
+static inline void iio_set_irq_pool(int start, int num) {};
+
+#endif
diff --git a/drivers/staging/iio/iio-core.h b/drivers/staging/iio/iio-core.h
new file mode 100644
index 0000000..2c4f433
--- /dev/null
+++ b/drivers/staging/iio/iio-core.h
@@ -0,0 +1,13 @@
+/*
+ * IIO core header. Should never be included by drivers.
+ *
+ * Copyright (c) 2011 Jonathan Cameron <[email protected]>
+ *
+ * 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.
+ */
+
+#define IIO_MAX_POOL 128
+extern int __iio_irqstart;
+extern int __iio_irqnum;
diff --git a/drivers/staging/iio/iio.h b/drivers/staging/iio/iio.h
index fb6caae..add0659 100644
--- a/drivers/staging/iio/iio.h
+++ b/drivers/staging/iio/iio.h
@@ -304,6 +304,19 @@ extern dev_t iio_devt;
extern struct bus_type iio_bus_type;

/**
+ * iio_irq_pool_get_range() - get a set of irqs from the pool
+ * @num: number of interrupts requested
+ **/
+int iio_irq_pool_get_range(int num);
+
+/**
+ * iio_irq_pool_put_range()
+ * @start: first irq to return to the pool
+ * @num: number to return
+ **/
+void iio_irq_pool_put_range(int start, int num);
+
+/**
* iio_put_device() - reference counted deallocation of struct device
* @dev: the iio_device containing the device
**/
diff --git a/drivers/staging/iio/industrialio-board-info.c b/drivers/staging/iio/industrialio-board-info.c
new file mode 100644
index 0000000..28f9156
--- /dev/null
+++ b/drivers/staging/iio/industrialio-board-info.c
@@ -0,0 +1,25 @@
+/* IIO Board info
+ *
+ * Copyright (c) 2011 Jonathan Cameron
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include "iio-core.h"
+
+int __iio_irqstart;
+EXPORT_SYMBOL(__iio_irqstart);
+int __iio_irqnum;
+EXPORT_SYMBOL(__iio_irqnum);
+
+void __init iio_set_irq_pool(int start, int num)
+{
+ __iio_irqstart = start;
+ __iio_irqnum = num;
+ if (num > IIO_MAX_POOL)
+ __iio_irqnum = IIO_MAX_POOL;
+}
diff --git a/drivers/staging/iio/industrialio-core.c b/drivers/staging/iio/industrialio-core.c
index 0473f55..39b987d 100644
--- a/drivers/staging/iio/industrialio-core.c
+++ b/drivers/staging/iio/industrialio-core.c
@@ -23,6 +23,7 @@
#include <linux/cdev.h>
#include <linux/slab.h>
#include "iio.h"
+#include "iio-core.h"
#include "trigger_consumer.h"

#define IIO_ID_PREFIX "device"
@@ -35,6 +36,33 @@ static DEFINE_IDA(iio_chrdev_ida);
/* Lock used to protect both of the above */
static DEFINE_SPINLOCK(iio_ida_lock);

+static DEFINE_MUTEX(iio_irq_pool_lock);
+static unsigned long iio_irq_pool_mask[BITS_TO_LONGS(IIO_MAX_POOL)] = {};
+
+int iio_irq_pool_get_range(int num)
+{
+ int ret;
+
+ mutex_lock(&iio_irq_pool_lock);
+ ret = bitmap_find_free_region(iio_irq_pool_mask,
+ __iio_irqnum, ilog2(num));
+ if (ret >= 0)
+ ret += __iio_irqstart;
+ mutex_unlock(&iio_irq_pool_lock);
+
+ return ret;
+}
+EXPORT_SYMBOL(iio_irq_pool_get_range);
+
+void iio_irq_pool_put_range(int start, int num)
+{
+ mutex_lock(&iio_irq_pool_lock);
+ bitmap_release_region(iio_irq_pool_mask,
+ start - __iio_irqstart, ilog2(num));
+ mutex_unlock(&iio_irq_pool_lock);
+}
+EXPORT_SYMBOL(iio_irq_pool_put_range);
+
dev_t iio_devt;
EXPORT_SYMBOL(iio_devt);

diff --git a/drivers/staging/iio/industrialio-trigger.c b/drivers/staging/iio/industrialio-trigger.c
index 083847c..8100822 100644
--- a/drivers/staging/iio/industrialio-trigger.c
+++ b/drivers/staging/iio/industrialio-trigger.c
@@ -163,6 +163,7 @@ static struct iio_trigger *iio_trigger_find_by_name(const char *name,

void iio_trigger_poll(struct iio_trigger *trig, s64 time)
{
+ int i;
struct iio_poll_func *pf_cursor;

list_for_each_entry(pf_cursor, &trig->pollfunc_list, list) {
@@ -178,6 +179,11 @@ void iio_trigger_poll(struct iio_trigger *trig, s64 time)
trig->use_count++;
}
}
+ for (i = 0; i < CONFIG_IIO_CONSUMERS_PER_TRIGGER; i++)
+ if (trig->subirqs[i].enabled) {
+ trig->use_count++;
+ generic_handle_irq(trig->subirq_base + i);
+ }
}
EXPORT_SYMBOL(iio_trigger_poll);

@@ -219,16 +225,23 @@ int iio_trigger_attach_poll_func(struct iio_trigger *trig,
int ret = 0;
unsigned long flags;

- spin_lock_irqsave(&trig->pollfunc_list_lock, flags);
- list_add_tail(&pf->list, &trig->pollfunc_list);
- spin_unlock_irqrestore(&trig->pollfunc_list_lock, flags);
-
+ if (pf->handler) {
+ pf->irq = iio_trigger_get_irq(trig);
+ ret = request_threaded_irq(pf->irq, NULL, pf->handler,
+ pf->type, pf->name,
+ pf);
+ } else {
+ spin_lock_irqsave(&trig->pollfunc_list_lock, flags);
+ list_add_tail(&pf->list, &trig->pollfunc_list);
+ spin_unlock_irqrestore(&trig->pollfunc_list_lock, flags);
+ }
if (trig->set_trigger_state)
ret = trig->set_trigger_state(trig, true);
if (ret) {
printk(KERN_ERR "set trigger state failed\n");
list_del(&pf->list);
}
+
return ret;
}
EXPORT_SYMBOL(iio_trigger_attach_poll_func);
@@ -240,31 +253,42 @@ int iio_trigger_dettach_poll_func(struct iio_trigger *trig,
unsigned long flags;
int ret = -EINVAL;

- spin_lock_irqsave(&trig->pollfunc_list_lock, flags);
- list_for_each_entry(pf_cursor, &trig->pollfunc_list, list)
- if (pf_cursor == pf) {
- ret = 0;
- break;
+ if (pf->handler) {
+ ret = trig->set_trigger_state(trig, false);
+ if (ret)
+ goto error_ret;
+ iio_trigger_put_irq(trig, pf->irq);
+ free_irq(pf->irq, pf);
+ } else {
+ spin_lock_irqsave(&trig->pollfunc_list_lock, flags);
+ list_for_each_entry(pf_cursor, &trig->pollfunc_list, list)
+ if (pf_cursor == pf) {
+ ret = 0;
+ break;
+ }
+ if (!ret) {
+ if (list_is_singular(&trig->pollfunc_list)
+ && trig->set_trigger_state) {
+ spin_unlock_irqrestore(&trig
+ ->pollfunc_list_lock,
+ flags);
+ /* May sleep hence cannot hold the spin lock */
+ ret = trig->set_trigger_state(trig, false);
+ if (ret)
+ goto error_ret;
+ spin_lock_irqsave(&trig->pollfunc_list_lock,
+ flags);
+ }
+ /*
+ * Now we can delete safe in the knowledge that, if
+ * this is the last pollfunc then we have disabled
+ * the trigger anyway and so nothing should be able
+ * to call the pollfunc.
+ */
+ list_del(&pf_cursor->list);
}
- if (!ret) {
- if (list_is_singular(&trig->pollfunc_list)
- && trig->set_trigger_state) {
- spin_unlock_irqrestore(&trig->pollfunc_list_lock,
- flags);
- /* May sleep hence cannot hold the spin lock */
- ret = trig->set_trigger_state(trig, false);
- if (ret)
- goto error_ret;
- spin_lock_irqsave(&trig->pollfunc_list_lock, flags);
- }
- /*
- * Now we can delete safe in the knowledge that, if this is
- * the last pollfunc then we have disabled the trigger anyway
- * and so nothing should be able to call the pollfunc.
- */
- list_del(&pf_cursor->list);
+ spin_unlock_irqrestore(&trig->pollfunc_list_lock, flags);
}
- spin_unlock_irqrestore(&trig->pollfunc_list_lock, flags);

error_ret:
return ret;
@@ -337,6 +361,21 @@ static const struct attribute_group iio_trigger_consumer_attr_group = {
static void iio_trig_release(struct device *device)
{
struct iio_trigger *trig = to_iio_trigger(device);
+ int i;
+
+ if (trig->subirq_base) {
+ for (i = 0; i < CONFIG_IIO_CONSUMERS_PER_TRIGGER; i++) {
+ set_irq_flags(trig->subirq_base + i, 0);
+ irq_set_chip(trig->subirq_base + i,
+ NULL);
+ irq_set_handler(trig->subirq_base + i,
+ NULL);
+ }
+
+ kfree(trig->subirq_chip.name);
+ iio_irq_pool_put_range(trig->subirq_base,
+ CONFIG_IIO_CONSUMERS_PER_TRIGGER);
+ }
kfree(trig);
iio_put();
}
@@ -345,11 +384,30 @@ static struct device_type iio_trig_type = {
.release = iio_trig_release,
};

-struct iio_trigger *iio_allocate_trigger(void)
+static void iio_trig_subirqmask(struct irq_data *d)
+{
+ struct irq_chip *chip = irq_data_get_irq_chip(d);
+ struct iio_trigger *trig
+ = container_of(chip,
+ struct iio_trigger, subirq_chip);
+ trig->subirqs[d->irq - trig->subirq_base].enabled = false;
+}
+
+static void iio_trig_subirqunmask(struct irq_data *d)
+{
+ struct irq_chip *chip = irq_data_get_irq_chip(d);
+ struct iio_trigger *trig
+ = container_of(chip,
+ struct iio_trigger, subirq_chip);
+ trig->subirqs[d->irq - trig->subirq_base].enabled = true;
+}
+
+struct iio_trigger *iio_allocate_trigger_named(const char *name)
{
struct iio_trigger *trig;
trig = kzalloc(sizeof *trig, GFP_KERNEL);
if (trig) {
+ int i;
trig->dev.type = &iio_trig_type;
trig->dev.bus = &iio_bus_type;
device_initialize(&trig->dev);
@@ -357,10 +415,42 @@ struct iio_trigger *iio_allocate_trigger(void)
spin_lock_init(&trig->pollfunc_list_lock);
INIT_LIST_HEAD(&trig->list);
INIT_LIST_HEAD(&trig->pollfunc_list);
+
+ if (name) {
+ mutex_init(&trig->pool_lock);
+ trig->subirq_base
+ = iio_irq_pool_get_range(
+ CONFIG_IIO_CONSUMERS_PER_TRIGGER);
+ if (trig->subirq_base < 0) {
+ kfree(trig);
+ return NULL;
+ }
+ trig->subirq_chip.name = kstrdup(name, GFP_KERNEL);
+ if (trig->subirq_chip.name == NULL) {
+ kfree(trig);
+ return NULL;
+ }
+ trig->subirq_chip.irq_mask = &iio_trig_subirqmask;
+ trig->subirq_chip.irq_unmask = &iio_trig_subirqunmask;
+ for (i = 0; i < CONFIG_IIO_CONSUMERS_PER_TRIGGER; i++) {
+ irq_set_chip(trig->subirq_base + i,
+ &trig->subirq_chip);
+ irq_set_handler(trig->subirq_base + i,
+ &handle_simple_irq);
+ set_irq_flags(trig->subirq_base + i,
+ IRQF_VALID);
+ }
+ }
iio_get();
}
return trig;
}
+EXPORT_SYMBOL(iio_allocate_trigger_named);
+
+struct iio_trigger *iio_allocate_trigger(void)
+{
+ return iio_allocate_trigger_named(NULL);
+}
EXPORT_SYMBOL(iio_allocate_trigger);

void iio_free_trigger(struct iio_trigger *trig)
diff --git a/drivers/staging/iio/trigger.h b/drivers/staging/iio/trigger.h
index c6ab32f..2e52004 100644
--- a/drivers/staging/iio/trigger.h
+++ b/drivers/staging/iio/trigger.h
@@ -6,9 +6,15 @@
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation.
*/
+#include <linux/irq.h>
+
#ifndef _IIO_TRIGGER_H_
#define _IIO_TRIGGER_H_

+struct iio_subirq {
+ bool enabled;
+};
+
/**
* struct iio_trigger - industrial I/O trigger device
*
@@ -43,6 +49,13 @@ struct iio_trigger {

int (*set_trigger_state)(struct iio_trigger *trig, bool state);
int (*try_reenable)(struct iio_trigger *trig);
+
+ struct irq_chip subirq_chip;
+ int subirq_base;
+
+ struct iio_subirq subirqs[CONFIG_IIO_CONSUMERS_PER_TRIGGER];
+ unsigned long pool[BITS_TO_LONGS(CONFIG_IIO_CONSUMERS_PER_TRIGGER)];
+ struct mutex pool_lock;
};

static inline struct iio_trigger *to_iio_trigger(struct device *d)
@@ -114,6 +127,27 @@ int iio_trigger_dettach_poll_func(struct iio_trigger *trig,
void iio_trigger_poll(struct iio_trigger *trig, s64 time);
void iio_trigger_notify_done(struct iio_trigger *trig);

+static inline int iio_trigger_get_irq(struct iio_trigger *trig)
+{
+ int ret;
+ mutex_lock(&trig->pool_lock);
+ ret = bitmap_find_free_region(trig->pool,
+ CONFIG_IIO_CONSUMERS_PER_TRIGGER,
+ ilog2(1));
+ mutex_unlock(&trig->pool_lock);
+ if (ret >= 0)
+ ret += trig->subirq_base;
+
+ return ret;
+};
+
+static inline void iio_trigger_put_irq(struct iio_trigger *trig, int irq)
+{
+ mutex_lock(&trig->pool_lock);
+ clear_bit(irq - trig->subirq_base, trig->pool);
+ mutex_unlock(&trig->pool_lock);
+};
+
/**
* struct iio_poll_func - poll function pair
*
@@ -137,6 +171,10 @@ struct iio_poll_func {
void (*poll_func_immediate)(struct iio_dev *indio_dev);
void (*poll_func_main)(struct iio_dev *private_data, s64 time);

+ irqreturn_t (*handler)(int irq, void *p);
+ int type;
+ char *name;
+ int irq;
};

int iio_alloc_pollfunc(struct iio_dev *indio_dev,
@@ -151,7 +189,7 @@ int iio_triggered_ring_postenable(struct iio_dev *indio_dev);
int iio_triggered_ring_predisable(struct iio_dev *indio_dev);

struct iio_trigger *iio_allocate_trigger(void);
-
+struct iio_trigger *iio_allocate_trigger_named(const char *name);
void iio_free_trigger(struct iio_trigger *trig);

#endif /* _IIO_TRIGGER_H_ */
--
1.7.3.4

2011-03-31 14:54:55

by Jonathan Cameron

[permalink] [raw]
Subject: [PATCH 13/21] arm: irq: export set flags

---
arch/arm/kernel/irq.c | 1 +
1 files changed, 1 insertions(+), 0 deletions(-)

diff --git a/arch/arm/kernel/irq.c b/arch/arm/kernel/irq.c
index 3535d37..e4fd9e4 100644
--- a/arch/arm/kernel/irq.c
+++ b/arch/arm/kernel/irq.c
@@ -163,6 +163,7 @@ void set_irq_flags(unsigned int irq, unsigned int iflags)
desc->status &= ~IRQ_NOAUTOEN;
raw_spin_unlock_irqrestore(&desc->lock, flags);
}
+EXPORT_SYMBOL(set_irq_flags);

void __init init_IRQ(void)
{
--
1.7.3.4

2011-03-31 14:54:54

by Jonathan Cameron

[permalink] [raw]
Subject: [PATCH 14/21] irq: export handle_simple_irq and irq_to_desc to allow for virtual irqs in IIO

---
kernel/irq/chip.c | 1 +
kernel/irq/irqdesc.c | 1 +
2 files changed, 2 insertions(+), 0 deletions(-)

diff --git a/kernel/irq/chip.c b/kernel/irq/chip.c
index c9c0601..4647fe9 100644
--- a/kernel/irq/chip.c
+++ b/kernel/irq/chip.c
@@ -431,6 +431,7 @@ handle_simple_irq(unsigned int irq, struct irq_desc *desc)
out_unlock:
raw_spin_unlock(&desc->lock);
}
+EXPORT_SYMBOL_GPL(handle_simple_irq);

/**
* handle_level_irq - Level type irq handler
diff --git a/kernel/irq/irqdesc.c b/kernel/irq/irqdesc.c
index 6fb014f..0403c5a 100644
--- a/kernel/irq/irqdesc.c
+++ b/kernel/irq/irqdesc.c
@@ -110,6 +110,7 @@ struct irq_desc *irq_to_desc(unsigned int irq)
{
return radix_tree_lookup(&irq_desc_tree, irq);
}
+EXPORT_SYMBOL_GPL(irq_to_desc);

static void delete_irq_desc(unsigned int irq)
{
--
1.7.3.4

2011-03-31 14:55:57

by Jonathan Cameron

[permalink] [raw]
Subject: [PATCH 11/21] staging:iio: Remove legacy event handling.

This requires all drivers to change slightly

- need to check what from the lis3l02dq should be in earlier patches!
---
drivers/staging/iio/accel/lis3l02dq_core.c | 33 +---
drivers/staging/iio/chrdev.h | 21 ---
drivers/staging/iio/iio.h | 90 +----------
drivers/staging/iio/industrialio-core.c | 250 +++-------------------------
drivers/staging/iio/sysfs.h | 129 --------------
5 files changed, 38 insertions(+), 485 deletions(-)

diff --git a/drivers/staging/iio/accel/lis3l02dq_core.c b/drivers/staging/iio/accel/lis3l02dq_core.c
index 96cdb82..70b1ae0 100644
--- a/drivers/staging/iio/accel/lis3l02dq_core.c
+++ b/drivers/staging/iio/accel/lis3l02dq_core.c
@@ -415,10 +415,9 @@ static IIO_DEV_ATTR_SAMP_FREQ(S_IWUSR | S_IRUGO,

static IIO_CONST_ATTR_SAMP_FREQ_AVAIL("280 560 1120 4480");

-static irqreturn_t lis3l02dq_event_handler(int irq, void *_int_info)
+static irqreturn_t lis3l02dq_event_handler(int irq, void *private)
{
- struct iio_interrupt *int_info = _int_info;
- struct iio_dev *indio_dev = int_info->dev_info;
+ struct iio_dev *indio_dev = private;
struct iio_sw_ring_helper_state *h
= iio_dev_get_devdata(indio_dev);
struct lis3l02dq_state *st = lis3l02dq_h_to_s(h);
@@ -442,13 +441,13 @@ static irqreturn_t lis3l02dq_event_handler(int irq, void *_int_info)
static struct iio_chan_spec lis3l02dq_channels[] = {
IIO_CHAN_EV(IIO_ACCEL, 'x', LIS3L02DQ_INFO_MASK,
0, 0, IIO_ST('s', 12, 16, 0),
- LIS3L02DQ_EVENT_MASK, NULL),
+ LIS3L02DQ_EVENT_MASK),
IIO_CHAN_EV(IIO_ACCEL, 'y', LIS3L02DQ_INFO_MASK,
1, 1, IIO_ST('s', 12, 16, 0),
- LIS3L02DQ_EVENT_MASK, NULL),
+ LIS3L02DQ_EVENT_MASK),
IIO_CHAN_EV(IIO_ACCEL, 'z', LIS3L02DQ_INFO_MASK,
2, 2, IIO_ST('s', 12, 16, 0),
- LIS3L02DQ_EVENT_MASK, NULL),
+ LIS3L02DQ_EVENT_MASK),
IIO_CHAN_SOFT_TIMESTAMP(3)
};

@@ -507,7 +506,7 @@ int lis3l02dq_disable_all_events(struct iio_dev *indio_dev)
goto error_ret;

if (irqtofree)
- free_irq(st->us->irq, indio_dev->interrupts[0]);
+ free_irq(st->us->irq, indio_dev);

ret = control;
error_ret:
@@ -516,7 +515,6 @@ error_ret:

static int lis3l02dq_write_event_config(struct iio_dev *indio_dev,
int event_code,
- struct iio_event_handler_list *list_el,
int state)
{
struct iio_sw_ring_helper_state *h
@@ -558,7 +556,7 @@ static int lis3l02dq_write_event_config(struct iio_dev *indio_dev,
&lis3l02dq_event_handler,
IRQF_TRIGGER_RISING,
"lis3l02dq_event",
- indio_dev->interrupts[0]);
+ indio_dev);
if (ret)
goto error_ret;
}
@@ -579,7 +577,7 @@ static int lis3l02dq_write_event_config(struct iio_dev *indio_dev,

/* remove interrupt handler if nothing is still on */
if (!(val & 0x3f))
- free_irq(st->us->irq, indio_dev->interrupts[0]);
+ free_irq(st->us->irq, indio_dev);
}

error_ret:
@@ -743,16 +741,9 @@ static int __devinit lis3l02dq_probe(struct spi_device *spi)

if (spi->irq && gpio_is_valid(irq_to_gpio(spi->irq)) > 0) {
st->inter = 0;
- ret = iio_register_interrupt_line(spi->irq,
- st->help.indio_dev,
- 0,
- IRQF_TRIGGER_RISING,
- "lis3l02dq");
- if (ret)
- goto error_uninitialize_ring;
ret = lis3l02dq_probe_trigger(st->help.indio_dev);
if (ret)
- goto error_unregister_line;
+ goto error_uninitialize_ring;
}

/* Get the device into a sane initial state */
@@ -764,9 +755,6 @@ static int __devinit lis3l02dq_probe(struct spi_device *spi)
error_remove_trigger:
if (st->help.indio_dev->modes & INDIO_RING_TRIGGERED)
lis3l02dq_remove_trigger(st->help.indio_dev);
-error_unregister_line:
- if (st->help.indio_dev->modes & INDIO_RING_TRIGGERED)
- iio_unregister_interrupt_line(st->help.indio_dev, 0);
error_uninitialize_ring:
iio_ring_buffer_unregister(st->help.indio_dev->ring);
error_unreg_ring_funcs:
@@ -831,9 +819,6 @@ static int lis3l02dq_remove(struct spi_device *spi)
flush_scheduled_work();

lis3l02dq_remove_trigger(indio_dev);
- if (spi->irq && gpio_is_valid(irq_to_gpio(spi->irq)) > 0)
- iio_unregister_interrupt_line(indio_dev, 0);
-
iio_ring_buffer_unregister(indio_dev->ring);
lis3l02dq_unconfigure_ring(indio_dev);
iio_device_unregister(indio_dev);
diff --git a/drivers/staging/iio/chrdev.h b/drivers/staging/iio/chrdev.h
index c1e86a9..8e76dd4 100644
--- a/drivers/staging/iio/chrdev.h
+++ b/drivers/staging/iio/chrdev.h
@@ -78,28 +78,7 @@ struct iio_event_interface {
void *private;
char _name[35];
char _attrname[20];
-
- struct list_head event_attr_list;
struct list_head dev_attr_list;
};

-/**
- * struct iio_event_handler_list - element in list of handlers for events
- * @list: list header
- * @refcount: as the handler may be shared between multiple device
- * side events, reference counting ensures clean removal
- * @exist_lock: prevents race conditions related to refcount useage.
- * @handler: event handler function - called on event if this
- * event_handler is enabled.
- *
- * Each device has one list of these per interrupt line.
- **/
-struct iio_event_handler_list {
- struct list_head list;
- int refcount;
- struct mutex exist_lock;
- int (*handler)(struct iio_dev *dev_info, int index, s64 timestamp,
- int no_test);
-};
-
#endif
diff --git a/drivers/staging/iio/iio.h b/drivers/staging/iio/iio.h
index 341724f..fb6caae 100644
--- a/drivers/staging/iio/iio.h
+++ b/drivers/staging/iio/iio.h
@@ -74,7 +74,6 @@ enum iio_chan_info_enum {
* @info_mask: what information is to be exported about this channel.
* This includes calibbias, scale etc.
* @event_mask: what events can this channel produce.
- * @shared_handler: single handler for the events registered.
*/
struct iio_chan_spec {
enum iio_chan_type type;
@@ -90,9 +89,6 @@ struct iio_chan_spec {
} scan_type;
const long info_mask;
const long event_mask;
- /* TODO: investigate pushing shared event handling out to
- * the drivers */
- struct iio_event_handler_list *shared_handler;
};
/* Meant for internal use only */
void __iio_device_attr_deinit(struct device_attribute *dev_attr);
@@ -116,14 +112,13 @@ int __iio_device_attr_init(struct device_attribute *dev_attr,
.scan_index = _si, .scan_type = _stype }

#define IIO_CHAN_EV(_type, _chan, _inf_mask, _address, _si, \
- _stype, _event_mask, _shared_h) \
+ _stype, _event_mask) \
{ .type = _type, \
.channel = _chan, \
.info_mask = _inf_mask, \
.address = _address, \
.scan_index = _si, .scan_type = _stype, \
- .event_mask = _event_mask, \
- .shared_handler = _shared_h }
+ .event_mask = _event_mask }

#define IIO_CHAN_COMPOUND(_type, _chan1, _chan2, _inf_mask, \
_address, _si, _stype) \
@@ -133,14 +128,13 @@ int __iio_device_attr_init(struct device_attribute *dev_attr,
.scan_index = _si, .scan_type = _stype }

#define IIO_CHAN_COMPOUND_EV(_type, _chan1, _chan2, _inf_mask, \
- _address, _si, _stype, _event_mask, _shared_h) \
+ _address, _si, _stype, _event_mask) \
{ .type = _type, \
.channel = _chan1, .channel2 = _chan2, \
.info_mask = _inf_mask, \
.address = _address, \
.scan_index = _si, .scan_type = _stype, \
- .event_mask = _event_mask, \
- .shared_handler = _shared_h}
+ .event_mask = _event_mask }

#define IIO_CHAN_SOFT_TIMESTAMP(_si) \
{ .type = IIO_TIMESTAMP, .channel = -1, \
@@ -175,26 +169,6 @@ static inline s64 iio_get_time_ns(void)
return timespec_to_ns(&ts);
}

-/**
- * iio_add_event_to_list() - Wraps adding to event lists
- * @el: the list element of the event to be handled.
- * @head: the list associated with the event handler being used.
- *
- * Does reference counting to allow shared handlers.
- **/
-void iio_add_event_to_list(struct iio_event_handler_list *el,
- struct list_head *head);
-
-/**
- * iio_remove_event_from_list() - Wraps removing from event list
- * @el: element to be removed
- * @head: associate list head for the interrupt handler.
- *
- * Does reference counting to allow shared handlers.
- **/
-void iio_remove_event_from_list(struct iio_event_handler_list *el,
- struct list_head *head);
-
/* Device operating modes */
#define INDIO_DIRECT_MODE 0x01
#define INDIO_RING_TRIGGERED 0x02
@@ -217,7 +191,6 @@ void iio_remove_event_from_list(struct iio_event_handler_list *el,
* @driver_module: [DRIVER] module structure used to ensure correct
* ownership of chrdevs etc
* @num_interrupt_lines:[DRIVER] number of physical interrupt lines from device
- * @interrupts: [INTERN] interrupt line specific event lists etc
* @event_attrs: [DRIVER] event control attributes
* @event_conf_attrs: [DRIVER] event configuration attributes
* @event_interfaces: [INTERN] event chrdevs associated with interrupt lines
@@ -238,7 +211,6 @@ struct iio_dev {
struct module *driver_module;

int num_interrupt_lines;
- struct iio_interrupt **interrupts;
struct attribute_group *event_attrs;
struct attribute_group *event_conf_attrs;

@@ -266,7 +238,6 @@ struct iio_dev {

int (*write_event_config)(struct iio_dev *indio_dev,
int event_code,
- struct iio_event_handler_list *listel,
int state);

int (*read_event_value)(struct iio_dev *indio_dev,
@@ -290,49 +261,6 @@ int iio_device_register(struct iio_dev *dev_info);
void iio_device_unregister(struct iio_dev *dev_info);

/**
- * struct iio_interrupt - wrapper used to allow easy handling of multiple
- * physical interrupt lines
- * @dev_info: the iio device for which the is an interrupt line
- * @line_number: associated line number
- * @id: ida allocated unique id number
- * @irq: associate interrupt number
- * @ev_list: event handler list for associated events
- * @ev_list_lock: ensure only one access to list at a time
- **/
-struct iio_interrupt {
- struct iio_dev *dev_info;
- int line_number;
- int id;
- int irq;
- struct list_head ev_list;
- spinlock_t ev_list_lock;
-};
-
-#define to_iio_interrupt(i) container_of(i, struct iio_interrupt, ev_list)
-
-/**
- * iio_register_interrupt_line() - Tell IIO about interrupt lines
- *
- * @irq: Typically provided via platform data
- * @dev_info: IIO device info structure for device
- * @line_number: Which interrupt line of the device is this?
- * @type: Interrupt type (e.g. edge triggered etc)
- * @name: Identifying name.
- **/
-int iio_register_interrupt_line(unsigned int irq,
- struct iio_dev *dev_info,
- int line_number,
- unsigned long type,
- const char *name);
-
-void iio_unregister_interrupt_line(struct iio_dev *dev_info,
- int line_number);
-
-
-/* temporarily exported to allow moving of interrupt requesting into drivers */
-irqreturn_t iio_interrupt_handler(int irq, void *_int_info);
-
-/**
* iio_push_event() - try to add event to the list for userspace reading
* @dev_info: IIO device structure
* @ev_line: Which event line (hardware interrupt)
@@ -345,16 +273,6 @@ int iio_push_event(struct iio_dev *dev_info,
s64 timestamp);

/**
- * __iio_push_event() - tries to add an event to the list associated with a chrdev
- * @ev_int: the event interface to which we are pushing the event
- * @ev_code: the outgoing event code
- * @timestamp: timestamp of the event
- **/
-int __iio_push_event(struct iio_event_interface *ev_int,
- int ev_code,
- s64 timestamp);
-
-/**
* iio_setup_ev_int() - configure an event interface (chrdev)
* @name: name used for resulting sysfs directory etc.
* @ev_int: interface we are configuring
diff --git a/drivers/staging/iio/industrialio-core.c b/drivers/staging/iio/industrialio-core.c
index 7cee697..0473f55 100644
--- a/drivers/staging/iio/industrialio-core.c
+++ b/drivers/staging/iio/industrialio-core.c
@@ -66,16 +66,13 @@ static const char * const iio_chan_info_postfix[] = {
[IIO_CHAN_INFO_CALIBBIAS_SHARED/2] = "calibbias",
};

-/* Used both in the interrupt line put events and the ring buffer ones */
-
-/* Note that in it's current form someone has to be listening before events
- * are queued. Hence a client MUST open the chrdev before the ring buffer is
- * switched on.
- */
-int __iio_push_event(struct iio_event_interface *ev_int,
- int ev_code,
- s64 timestamp)
+int iio_push_event(struct iio_dev *dev_info,
+ int ev_line,
+ int ev_code,
+ s64 timestamp)
{
+ struct iio_event_interface *ev_int
+ = &dev_info->event_interfaces[ev_line];
struct iio_detected_event_list *ev;
int ret = 0;

@@ -105,76 +102,8 @@ int __iio_push_event(struct iio_event_interface *ev_int,
error_ret:
return ret;
}
-EXPORT_SYMBOL(__iio_push_event);
-
-int iio_push_event(struct iio_dev *dev_info,
- int ev_line,
- int ev_code,
- s64 timestamp)
-{
- return __iio_push_event(&dev_info->event_interfaces[ev_line],
- ev_code, timestamp);
-}
EXPORT_SYMBOL(iio_push_event);

-/* Generic interrupt line interrupt handler */
-irqreturn_t iio_interrupt_handler(int irq, void *_int_info)
-{
- struct iio_interrupt *int_info = _int_info;
- struct iio_dev *dev_info = int_info->dev_info;
- struct iio_event_handler_list *p;
- s64 time_ns;
- unsigned long flags;
-
- spin_lock_irqsave(&int_info->ev_list_lock, flags);
- if (list_empty(&int_info->ev_list)) {
- spin_unlock_irqrestore(&int_info->ev_list_lock, flags);
- return IRQ_NONE;
- }
-
- time_ns = iio_get_time_ns();
- list_for_each_entry(p, &int_info->ev_list, list) {
- disable_irq_nosync(irq);
- p->handler(dev_info, 1, time_ns, !(p->refcount > 1));
- }
- spin_unlock_irqrestore(&int_info->ev_list_lock, flags);
-
- return IRQ_HANDLED;
-}
-EXPORT_SYMBOL(iio_interrupt_handler);
-
-static struct iio_interrupt *iio_allocate_interrupt(void)
-{
- struct iio_interrupt *i = kmalloc(sizeof *i, GFP_KERNEL);
- if (i) {
- spin_lock_init(&i->ev_list_lock);
- INIT_LIST_HEAD(&i->ev_list);
- }
- return i;
-}
-
-/* Confirming the validity of supplied irq is left to drivers.*/
-int iio_register_interrupt_line(unsigned int irq,
- struct iio_dev *dev_info,
- int line_number,
- unsigned long type,
- const char *name)
-{
- int ret = 0;
-
- dev_info->interrupts[line_number] = iio_allocate_interrupt();
- if (dev_info->interrupts[line_number] == NULL) {
- ret = -ENOMEM;
- goto error_ret;
- }
- dev_info->interrupts[line_number]->line_number = line_number;
- dev_info->interrupts[line_number]->irq = irq;
- dev_info->interrupts[line_number]->dev_info = dev_info;
-
-error_ret:
- return ret;
-}
-EXPORT_SYMBOL(iio_register_interrupt_line);

/* This turns up an awful lot */
ssize_t iio_read_const_attr(struct device *dev,
@@ -185,52 +114,6 @@ ssize_t iio_read_const_attr(struct device *dev,
}
EXPORT_SYMBOL(iio_read_const_attr);

-/* Before this runs the interrupt generator must have been disabled */
-void iio_unregister_interrupt_line(struct iio_dev *dev_info, int line_number)
-{
- /* make sure the interrupt handlers are all done */
- flush_scheduled_work();
- kfree(dev_info->interrupts[line_number]);
-}
-EXPORT_SYMBOL(iio_unregister_interrupt_line);
-
-/* Reference counted add and remove */
-void iio_add_event_to_list(struct iio_event_handler_list *el,
- struct list_head *head)
-{
- unsigned long flags;
- struct iio_interrupt *inter = to_iio_interrupt(head);
-
- /* take mutex to protect this element */
- mutex_lock(&el->exist_lock);
- if (el->refcount == 0) {
- /* Take the event list spin lock */
- spin_lock_irqsave(&inter->ev_list_lock, flags);
- list_add(&el->list, head);
- spin_unlock_irqrestore(&inter->ev_list_lock, flags);
- }
- el->refcount++;
- mutex_unlock(&el->exist_lock);
-}
-EXPORT_SYMBOL(iio_add_event_to_list);
-
-void iio_remove_event_from_list(struct iio_event_handler_list *el,
- struct list_head *head)
-{
- unsigned long flags;
- struct iio_interrupt *inter = to_iio_interrupt(head);
-
- mutex_lock(&el->exist_lock);
- el->refcount--;
- if (el->refcount == 0) {
- /* Take the event list spin lock */
- spin_lock_irqsave(&inter->ev_list_lock, flags);
- list_del_init(&el->list);
- spin_unlock_irqrestore(&inter->ev_list_lock, flags);
- }
- mutex_unlock(&el->exist_lock);
-}
-EXPORT_SYMBOL(iio_remove_event_from_list);

static ssize_t iio_event_chrdev_read(struct file *filep,
char __user *buf,
@@ -798,15 +681,14 @@ static ssize_t iio_ev_state_store(struct device *dev,
size_t len)
{
struct iio_dev *indio_dev = dev_get_drvdata(dev);
- struct iio_event_attr *this_attr = to_iio_event_attr(attr);
+ struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
int ret;
unsigned long val;
ret = strict_strtoul(buf, 10, &val);
if (ret || val < 0 || val > 1)
return -EINVAL;

- ret = indio_dev->write_event_config(indio_dev, this_attr->mask,
- this_attr->listel,
+ ret = indio_dev->write_event_config(indio_dev, this_attr->address,
val);
return (ret < 0) ? ret : len;
}
@@ -816,8 +698,8 @@ static ssize_t iio_ev_state_show(struct device *dev,
char *buf)
{
struct iio_dev *indio_dev = dev_get_drvdata(dev);
- struct iio_event_attr *this_attr = to_iio_event_attr(attr);
- int val = indio_dev->read_event_config(indio_dev, this_attr->mask);
+ struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+ int val = indio_dev->read_event_config(indio_dev, this_attr->address);

if (val < 0)
return val;
@@ -863,67 +745,6 @@ static ssize_t iio_ev_value_store(struct device *dev,
return len;
}

-static int __iio_add_chan_event_attr(const char *postfix,
- const char *group,
- struct iio_chan_spec *chan,
- unsigned int mask,
- struct device *dev,
- struct list_head *attr_list)
-{
- char *name_format;
- int ret;
- struct iio_event_attr *iio_ev_attr;
-
- iio_ev_attr = kzalloc(sizeof *iio_ev_attr, GFP_KERNEL);
- if (iio_ev_attr == NULL) {
- ret = -ENOMEM;
- goto error_ret;
- }
-
- sysfs_attr_init(&iio_ev_attr->dev_attr.attr);
-
- name_format = kasprintf(GFP_KERNEL, "%s_%s",
- iio_chan_type_name_spec[chan->type],
- postfix);
- if (name_format == NULL) {
- ret = -ENOMEM;
- goto error_free_attr;
- }
-
- iio_ev_attr->dev_attr.attr.name = kasprintf(GFP_KERNEL,
- name_format,
- chan->channel,
- chan->channel2);
- if (iio_ev_attr->dev_attr.attr.name == NULL) {
- ret = -ENOMEM;
- goto error_free_name_format;
- }
-
- iio_ev_attr->dev_attr.attr.mode = S_IRUGO | S_IWUSR;
- iio_ev_attr->dev_attr.show = &iio_ev_state_show;
- iio_ev_attr->dev_attr.store = &iio_ev_state_store;
- iio_ev_attr->mask = mask;
- iio_ev_attr->listel = chan->shared_handler;
- ret = sysfs_add_file_to_group(&dev->kobj,
- &iio_ev_attr->dev_attr.attr,
- group);
- if (ret < 0)
- goto error_free_name;
- list_add(&iio_ev_attr->l, attr_list);
- kfree(name_format);
- return 0;
-
-error_free_name:
- kfree(iio_ev_attr->dev_attr.attr.name);
-error_free_name_format:
- kfree(name_format);
-error_free_attr:
- kfree(iio_ev_attr);
-error_ret:
- return ret;
-}
-
-
static int iio_device_add_event_sysfs(struct iio_dev *dev_info,
struct iio_chan_spec *chan)
{
@@ -957,18 +778,21 @@ static int iio_device_add_event_sysfs(struct iio_dev *dev_info,
default:
printk("currently unhandled\n");
}
- ret = __iio_add_chan_event_attr(postfix,
- NULL,
- chan,
- mask,
- /*HACK. - limits us to one
- event interface - fix by
- extending the bitmask - but
- how far*/
- &dev_info->event_interfaces[0]
- .dev,
- &dev_info->event_interfaces[0].
- event_attr_list);
+ ret = __iio_add_chan_devattr(postfix,
+ NULL,
+ chan,
+ &iio_ev_state_show,
+ iio_ev_state_store,
+ mask,
+ /*HACK. - limits us to one
+ event interface - fix by
+ extending the bitmask - but
+ how far*/
+ 0,
+ &dev_info->event_interfaces[0]
+ .dev,
+ &dev_info->event_interfaces[0].
+ dev_attr_list);
kfree(postfix);
if (ret)
goto error_ret;
@@ -1004,7 +828,6 @@ static inline void __iio_remove_all_event_sysfs(struct iio_dev *dev_info,
int num)
{
struct iio_dev_attr *p, *n;
- struct iio_event_attr *q, *m;
list_for_each_entry_safe(p, n,
&dev_info->event_interfaces[num].
dev_attr_list, l) {
@@ -1015,23 +838,12 @@ static inline void __iio_remove_all_event_sysfs(struct iio_dev *dev_info,
kfree(p->dev_attr.attr.name);
kfree(p);
}
- list_for_each_entry_safe(q, m,
- &dev_info->event_interfaces[num].
- event_attr_list, l) {
- sysfs_remove_file_from_group(&dev_info
- ->event_interfaces[num].dev.kobj,
- &q->dev_attr.attr,
- groupname);
- kfree(q->dev_attr.attr.name);
- kfree(q);
- }
}

static inline int __iio_add_event_config_attrs(struct iio_dev *dev_info, int i)
{
int j;
int ret;
- /*p for adding, q for removing */
struct attribute **attrp, **attrq;

if (dev_info->event_conf_attrs && dev_info->event_conf_attrs[i].attrs) {
@@ -1047,7 +859,6 @@ static inline int __iio_add_event_config_attrs(struct iio_dev *dev_info, int i)
attrp++;
}
}
- INIT_LIST_HEAD(&dev_info->event_interfaces[0].event_attr_list);
INIT_LIST_HEAD(&dev_info->event_interfaces[0].dev_attr_list);
/* Dynically created from the channels array */
if (dev_info->channels) {
@@ -1118,14 +929,6 @@ static int iio_device_register_eventset(struct iio_dev *dev_info)
goto error_ret;
}

- dev_info->interrupts = kzalloc(sizeof(struct iio_interrupt *)
- *dev_info->num_interrupt_lines,
- GFP_KERNEL);
- if (dev_info->interrupts == NULL) {
- ret = -ENOMEM;
- goto error_free_event_interfaces;
- }
-
for (i = 0; i < dev_info->num_interrupt_lines; i++) {
dev_info->event_interfaces[i].owner = dev_info->driver_module;

@@ -1183,8 +986,6 @@ error_remove_sysfs_interfaces:
error_free_setup_ev_ints:
for (j = 0; j < i; j++)
iio_free_ev_int(&dev_info->event_interfaces[j]);
- kfree(dev_info->interrupts);
-error_free_event_interfaces:
kfree(dev_info->event_interfaces);
error_ret:

@@ -1207,7 +1008,6 @@ static void iio_device_unregister_eventset(struct iio_dev *dev_info)

for (i = 0; i < dev_info->num_interrupt_lines; i++)
iio_free_ev_int(&dev_info->event_interfaces[i]);
- kfree(dev_info->interrupts);
kfree(dev_info->event_interfaces);
}

diff --git a/drivers/staging/iio/sysfs.h b/drivers/staging/iio/sysfs.h
index 1a2e46e..bec000d 100644
--- a/drivers/staging/iio/sysfs.h
+++ b/drivers/staging/iio/sysfs.h
@@ -15,22 +15,6 @@
#include "iio.h"

/**
- * struct iio_event_attr - event control attribute
- * @dev_attr: underlying device attribute
- * @mask: mask for the event when detecting
- * @listel: list header to allow addition to list of event handlers
-*/
-struct iio_event_attr {
- struct device_attribute dev_attr;
- int mask;
- struct iio_event_handler_list *listel;
- struct list_head l;
-};
-
-#define to_iio_event_attr(_dev_attr) \
- container_of(_dev_attr, struct iio_event_attr, dev_attr)
-
-/**
* struct iio_dev_attr - iio specific device attribute
* @dev_attr: underlying device attribute
* @address: associated register address
@@ -184,85 +168,6 @@ struct iio_const_attr {
#define IIO_CONST_ATTR_TEMP_SCALE(_string) \
IIO_CONST_ATTR(temp_scale, _string)

-/**
- * IIO_EVENT_SH - generic shared event handler
- * @_name: event name
- * @_handler: handler function to be called
- *
- * This is used in cases where more than one event may result from a single
- * handler. Often the case that some alarm register must be read and multiple
- * alarms may have been triggered.
- **/
-#define IIO_EVENT_SH(_name, _handler) \
- static struct iio_event_handler_list \
- iio_event_##_name = { \
- .handler = _handler, \
- .refcount = 0, \
- .exist_lock = __MUTEX_INITIALIZER(iio_event_##_name \
- .exist_lock), \
- .list = { \
- .next = &iio_event_##_name.list, \
- .prev = &iio_event_##_name.list, \
- }, \
- };
-
-/**
- * IIO_EVENT_ATTR_SH - generic shared event attribute
- * @_name: event name
- * @_ev_list: event handler list
- * @_show: output method for the attribute
- * @_store: input method for the attribute
- * @_mask: mask used when detecting the event
- *
- * An attribute with an associated IIO_EVENT_SH
- **/
-#define IIO_EVENT_ATTR_SH(_name, _ev_list, _show, _store, _mask) \
- static struct iio_event_attr \
- iio_event_attr_##_name \
- = { .dev_attr = __ATTR(_name, S_IRUGO | S_IWUSR, \
- _show, _store), \
- .mask = _mask, \
- .listel = &_ev_list };
-
-#define IIO_EVENT_ATTR_NAMED_SH(_vname, _name, _ev_list, _show, _store, _mask) \
- static struct iio_event_attr \
- iio_event_attr_##_vname \
- = { .dev_attr = __ATTR(_name, S_IRUGO | S_IWUSR, \
- _show, _store), \
- .mask = _mask, \
- .listel = &_ev_list };
-
-/**
- * IIO_EVENT_ATTR - non-shared event attribute
- * @_name: event name
- * @_show: output method for the attribute
- * @_store: input method for the attribute
- * @_mask: mask used when detecting the event
- * @_handler: handler function to be called
- **/
-#define IIO_EVENT_ATTR(_name, _show, _store, _mask, _handler) \
- IIO_EVENT_SH(_name, _handler); \
- static struct \
- iio_event_attr \
- iio_event_attr_##_name \
- = { .dev_attr = __ATTR(_name, S_IRUGO | S_IWUSR, \
- _show, _store), \
- .mask = _mask, \
- .listel = &iio_event_##_name }; \
-
-/**
- * IIO_EVENT_ATTR_DATA_RDY - event driven by data ready signal
- * @_show: output method for the attribute
- * @_store: input method for the attribute
- * @_mask: mask used when detecting the event
- * @_handler: handler function to be called
- *
- * Not typically implemented in devices where full triggering support
- * has been implemented.
- **/
-#define IIO_EVENT_ATTR_DATA_RDY(_show, _store, _mask, _handler) \
- IIO_EVENT_ATTR(data_rdy, _show, _store, _mask, _handler)
-
/* must match our channel defs */
#define IIO_EV_CLASS_IN IIO_IN
#define IIO_EV_CLASS_ACCEL IIO_ACCEL
@@ -320,38 +225,4 @@ struct iio_const_attr {

#define IIO_EVENT_CODE_EXTRACT_MODIFIER(mask) ((mask >> 13) & 0x7)

-/**
- * IIO_EVENT_ATTR_RING_50_FULL - ring buffer event to indicate 50% full
- * @_show: output method for the attribute
- * @_store: input method for the attribute
- * @_mask: mask used when detecting the event
- * @_handler: handler function to be called
- **/
-#define IIO_EVENT_ATTR_RING_50_FULL(_show, _store, _mask, _handler) \
- IIO_EVENT_ATTR(ring_50_full, _show, _store, _mask, _handler)
-
-/**
- * IIO_EVENT_ATTR_RING_50_FULL_SH - shared ring event to indicate 50% full
- * @_evlist: event handler list
- * @_show: output method for the attribute
- * @_store: input method for the attribute
- * @_mask: mask used when detecting the event
- **/
-#define IIO_EVENT_ATTR_RING_50_FULL_SH(_evlist, _show, _store, _mask) \
- IIO_EVENT_ATTR_SH(ring_50_full, _evlist, _show, _store, _mask)
-
-/**
- * IIO_EVENT_ATTR_RING_75_FULL_SH - shared ring event to indicate 75% full
- * @_evlist: event handler list
- * @_show: output method for the attribute
- * @_store: input method for the attribute
- * @_mask: mask used when detecting the event
- **/
-#define IIO_EVENT_ATTR_RING_75_FULL_SH(_evlist, _show, _store, _mask) \
- IIO_EVENT_ATTR_SH(ring_75_full, _evlist, _show, _store, _mask)
-
-#define IIO_EVENT_CODE_RING_50_FULL IIO_BUFFER_EVENT_CODE(0)
-#define IIO_EVENT_CODE_RING_75_FULL IIO_BUFFER_EVENT_CODE(1)
-#define IIO_EVENT_CODE_RING_100_FULL IIO_BUFFER_EVENT_CODE(2)
-
#endif /* _INDUSTRIAL_IO_SYSFS_H_ */
--
1.7.3.4

2011-03-31 14:55:55

by Jonathan Cameron

[permalink] [raw]
Subject: [PATCH 12/21] staging:iio:lis3l02dq make threshold interrupt threaded.

We have moved the timestamp acquisition into the bottom half. It may
technically be less accurate but for this device I very much doubt
anyone cares!

Signed-off-by: Jonathan Cameron <[email protected]>
---
drivers/staging/iio/accel/lis3l02dq.h | 4 -
drivers/staging/iio/accel/lis3l02dq_core.c | 160 +++++++++++++---------------
2 files changed, 73 insertions(+), 91 deletions(-)

diff --git a/drivers/staging/iio/accel/lis3l02dq.h b/drivers/staging/iio/accel/lis3l02dq.h
index ba40f50..6d73325 100644
--- a/drivers/staging/iio/accel/lis3l02dq.h
+++ b/drivers/staging/iio/accel/lis3l02dq.h
@@ -150,8 +150,6 @@ Form of high byte dependant on justification set in ctrl reg */
* struct lis3l02dq_state - device instance specific data
* @helper: data and func pointer allowing generic functions
* @us: actual spi_device
- * @work_thresh: bh for threshold events
- * @thresh_timestamp: timestamp for threshold interrupts.
* @inter: used to check if new interrupt has been triggered
* @trig: data ready trigger registered with iio
* @tx: transmit buffer
@@ -161,8 +159,6 @@ Form of high byte dependant on justification set in ctrl reg */
struct lis3l02dq_state {
struct iio_sw_ring_helper_state help;
struct spi_device *us;
- struct work_struct work_thresh;
- s64 thresh_timestamp;
bool inter;
struct iio_trigger *trig;
u8 *tx;
diff --git a/drivers/staging/iio/accel/lis3l02dq_core.c b/drivers/staging/iio/accel/lis3l02dq_core.c
index 70b1ae0..2bded87 100644
--- a/drivers/staging/iio/accel/lis3l02dq_core.c
+++ b/drivers/staging/iio/accel/lis3l02dq_core.c
@@ -421,10 +421,72 @@ static irqreturn_t lis3l02dq_event_handler(int irq, void *private)
struct iio_sw_ring_helper_state *h
= iio_dev_get_devdata(indio_dev);
struct lis3l02dq_state *st = lis3l02dq_h_to_s(h);
+ u8 t;
+
+ s64 timestamp = iio_get_time_ns();
+
+ lis3l02dq_spi_read_reg_8(st->help.indio_dev,
+ LIS3L02DQ_REG_WAKE_UP_SRC_ADDR,
+ &t);

- disable_irq_nosync(irq);
- st->thresh_timestamp = iio_get_time_ns();
- schedule_work(&st->work_thresh);
+ if (t & LIS3L02DQ_REG_WAKE_UP_SRC_INTERRUPT_Z_HIGH)
+ iio_push_event(st->help.indio_dev, 0,
+ IIO_MOD_EVENT_CODE(IIO_EV_CLASS_ACCEL,
+ 0,
+ IIO_EV_MOD_Z,
+ IIO_EV_TYPE_THRESH,
+ IIO_EV_DIR_RISING),
+ timestamp);
+
+ if (t & LIS3L02DQ_REG_WAKE_UP_SRC_INTERRUPT_Z_LOW)
+ iio_push_event(st->help.indio_dev, 0,
+ IIO_MOD_EVENT_CODE(IIO_EV_CLASS_ACCEL,
+ 0,
+ IIO_EV_MOD_Z,
+ IIO_EV_TYPE_THRESH,
+ IIO_EV_DIR_FALLING),
+ timestamp);
+
+ if (t & LIS3L02DQ_REG_WAKE_UP_SRC_INTERRUPT_Y_HIGH)
+ iio_push_event(st->help.indio_dev, 0,
+ IIO_MOD_EVENT_CODE(IIO_EV_CLASS_ACCEL,
+ 0,
+ IIO_EV_MOD_Y,
+ IIO_EV_TYPE_THRESH,
+ IIO_EV_DIR_RISING),
+ timestamp);
+
+ if (t & LIS3L02DQ_REG_WAKE_UP_SRC_INTERRUPT_Y_LOW)
+ iio_push_event(st->help.indio_dev, 0,
+ IIO_MOD_EVENT_CODE(IIO_EV_CLASS_ACCEL,
+ 0,
+ IIO_EV_MOD_Y,
+ IIO_EV_TYPE_THRESH,
+ IIO_EV_DIR_FALLING),
+ timestamp);
+
+ if (t & LIS3L02DQ_REG_WAKE_UP_SRC_INTERRUPT_X_HIGH)
+ iio_push_event(st->help.indio_dev, 0,
+ IIO_MOD_EVENT_CODE(IIO_EV_CLASS_ACCEL,
+ 0,
+ IIO_EV_MOD_X,
+ IIO_EV_TYPE_THRESH,
+ IIO_EV_DIR_RISING),
+ timestamp);
+
+ if (t & LIS3L02DQ_REG_WAKE_UP_SRC_INTERRUPT_X_LOW)
+ iio_push_event(st->help.indio_dev, 0,
+ IIO_MOD_EVENT_CODE(IIO_EV_CLASS_ACCEL,
+ 0,
+ IIO_EV_MOD_X,
+ IIO_EV_TYPE_THRESH,
+ IIO_EV_DIR_FALLING),
+ timestamp);
+
+ /* Ack and allow for new interrupts */
+ lis3l02dq_spi_read_reg_8(st->help.indio_dev,
+ LIS3L02DQ_REG_WAKE_UP_ACK_ADDR,
+ &t);

return IRQ_HANDLED;
}
@@ -552,11 +614,13 @@ static int lis3l02dq_write_event_config(struct iio_dev *indio_dev,

if (changed) {
if (!(control & LIS3L02DQ_REG_CTRL_2_ENABLE_INTERRUPT)) {
- ret = request_irq(st->us->irq,
- &lis3l02dq_event_handler,
- IRQF_TRIGGER_RISING,
- "lis3l02dq_event",
- indio_dev);
+ ret = request_threaded_irq(st->us->irq,
+ NULL,
+ &lis3l02dq_event_handler,
+ IRQF_TRIGGER_RISING |
+ IRQF_ONESHOT,
+ "lis3l02dq_event",
+ indio_dev);
if (ret)
goto error_ret;
}
@@ -585,84 +649,6 @@ error_ret:
return ret;
}

-/* Unforunately it appears the interrupt won't clear unless you read from the
- * src register.
- */
-static void lis3l02dq_thresh_handler_bh_no_check(struct work_struct *work_s)
-{
- struct lis3l02dq_state *st
- = container_of(work_s,
- struct lis3l02dq_state, work_thresh);
-
- u8 t;
-
- lis3l02dq_spi_read_reg_8(st->help.indio_dev,
- LIS3L02DQ_REG_WAKE_UP_SRC_ADDR,
- &t);
-
- if (t & LIS3L02DQ_REG_WAKE_UP_SRC_INTERRUPT_Z_HIGH)
- iio_push_event(st->help.indio_dev, 0,
- IIO_MOD_EVENT_CODE(IIO_EV_CLASS_ACCEL,
- 0,
- IIO_EV_MOD_Z,
- IIO_EV_TYPE_THRESH,
- IIO_EV_DIR_RISING),
- st->thresh_timestamp);
-
- if (t & LIS3L02DQ_REG_WAKE_UP_SRC_INTERRUPT_Z_LOW)
- iio_push_event(st->help.indio_dev, 0,
- IIO_MOD_EVENT_CODE(IIO_EV_CLASS_ACCEL,
- 0,
- IIO_EV_MOD_Z,
- IIO_EV_TYPE_THRESH,
- IIO_EV_DIR_FALLING),
- st->thresh_timestamp);
-
- if (t & LIS3L02DQ_REG_WAKE_UP_SRC_INTERRUPT_Y_HIGH)
- iio_push_event(st->help.indio_dev, 0,
- IIO_MOD_EVENT_CODE(IIO_EV_CLASS_ACCEL,
- 0,
- IIO_EV_MOD_Y,
- IIO_EV_TYPE_THRESH,
- IIO_EV_DIR_RISING),
- st->thresh_timestamp);
-
- if (t & LIS3L02DQ_REG_WAKE_UP_SRC_INTERRUPT_Y_LOW)
- iio_push_event(st->help.indio_dev, 0,
- IIO_MOD_EVENT_CODE(IIO_EV_CLASS_ACCEL,
- 0,
- IIO_EV_MOD_Y,
- IIO_EV_TYPE_THRESH,
- IIO_EV_DIR_FALLING),
- st->thresh_timestamp);
-
- if (t & LIS3L02DQ_REG_WAKE_UP_SRC_INTERRUPT_X_HIGH)
- iio_push_event(st->help.indio_dev, 0,
- IIO_MOD_EVENT_CODE(IIO_EV_CLASS_ACCEL,
- 0,
- IIO_EV_MOD_X,
- IIO_EV_TYPE_THRESH,
- IIO_EV_DIR_RISING),
- st->thresh_timestamp);
-
- if (t & LIS3L02DQ_REG_WAKE_UP_SRC_INTERRUPT_X_LOW)
- iio_push_event(st->help.indio_dev, 0,
- IIO_MOD_EVENT_CODE(IIO_EV_CLASS_ACCEL,
- 0,
- IIO_EV_MOD_X,
- IIO_EV_TYPE_THRESH,
- IIO_EV_DIR_FALLING),
- st->thresh_timestamp);
- /* reenable the irq */
- enable_irq(st->us->irq);
- /* Ack and allow for new interrupts */
- lis3l02dq_spi_read_reg_8(st->help.indio_dev,
- LIS3L02DQ_REG_WAKE_UP_ACK_ADDR,
- &t);
-
- return;
-}
-
static IIO_CONST_ATTR_NAME("lis3l02dq");

static struct attribute *lis3l02dq_attributes[] = {
@@ -684,7 +670,7 @@ static int __devinit lis3l02dq_probe(struct spi_device *spi)
ret = -ENOMEM;
goto error_ret;
}
- INIT_WORK(&st->work_thresh, lis3l02dq_thresh_handler_bh_no_check);
+
/* this is only used tor removal purposes */
spi_set_drvdata(spi, st);

--
1.7.3.4

2011-03-31 14:56:31

by Jonathan Cameron

[permalink] [raw]
Subject: [PATCH 10/21] staging:iio: lis3l02dq - separate entirely interrupt handling for thesholds from that for the datardy signal.

This removes the one and only real user of the rather complex event list management.

Signed-off-by: Jonathan Cameron <[email protected]>
---
drivers/staging/iio/accel/lis3l02dq.h | 2 +
drivers/staging/iio/accel/lis3l02dq_core.c | 104 +++++++++++++++++++++-------
drivers/staging/iio/accel/lis3l02dq_ring.c | 56 ++++++++-------
3 files changed, 111 insertions(+), 51 deletions(-)

diff --git a/drivers/staging/iio/accel/lis3l02dq.h b/drivers/staging/iio/accel/lis3l02dq.h
index 5925405..ba40f50 100644
--- a/drivers/staging/iio/accel/lis3l02dq.h
+++ b/drivers/staging/iio/accel/lis3l02dq.h
@@ -181,6 +181,8 @@ int lis3l02dq_spi_write_reg_8(struct iio_dev *indio_dev,
u8 reg_address,
u8 *val);

+int lis3l02dq_disable_all_events(struct iio_dev *indio_dev);
+
#ifdef CONFIG_IIO_RING_BUFFER
/* At the moment triggers are only used for ring buffer
* filling. This may change!
diff --git a/drivers/staging/iio/accel/lis3l02dq_core.c b/drivers/staging/iio/accel/lis3l02dq_core.c
index 42f4d9b..96cdb82 100644
--- a/drivers/staging/iio/accel/lis3l02dq_core.c
+++ b/drivers/staging/iio/accel/lis3l02dq_core.c
@@ -242,7 +242,7 @@ static int lis3l02dq_read_raw(struct iio_dev *indio_dev,
else {
reg = lis3l02dq_axis_map
[LIS3L02DQ_ACCEL][chan->address];
- ret = lis3l02dq_read_16bit_s(st, reg, val);
+ ret = lis3l02dq_read_reg_s16(indio_dev, reg, val);
}
mutex_unlock(&indio_dev->mlock);
break;
@@ -415,26 +415,21 @@ static IIO_DEV_ATTR_SAMP_FREQ(S_IWUSR | S_IRUGO,

static IIO_CONST_ATTR_SAMP_FREQ_AVAIL("280 560 1120 4480");

-static int lis3l02dq_thresh_handler_th(struct iio_dev *indio_dev,
- int index,
- s64 timestamp,
- int no_test)
+static irqreturn_t lis3l02dq_event_handler(int irq, void *_int_info)
{
+ struct iio_interrupt *int_info = _int_info;
+ struct iio_dev *indio_dev = int_info->dev_info;
struct iio_sw_ring_helper_state *h
= iio_dev_get_devdata(indio_dev);
struct lis3l02dq_state *st = lis3l02dq_h_to_s(h);

- /* Stash the timestamp somewhere convenient for the bh */
- st->thresh_timestamp = timestamp;
+ disable_irq_nosync(irq);
+ st->thresh_timestamp = iio_get_time_ns();
schedule_work(&st->work_thresh);

- return 0;
+ return IRQ_HANDLED;
}

-/* A shared handler for a number of threshold types */
-IIO_EVENT_SH(threshold, &lis3l02dq_thresh_handler_th);
-
-
#define LIS3L02DQ_INFO_MASK \
((1 << IIO_CHAN_INFO_SCALE_SHARED) | \
(1 << IIO_CHAN_INFO_CALIBSCALE_SEPARATE) | \
@@ -447,12 +442,13 @@ IIO_EVENT_SH(threshold, &lis3l02dq_thresh_handler_th);
static struct iio_chan_spec lis3l02dq_channels[] = {
IIO_CHAN_EV(IIO_ACCEL, 'x', LIS3L02DQ_INFO_MASK,
0, 0, IIO_ST('s', 12, 16, 0),
- LIS3L02DQ_EVENT_MASK, &iio_event_threshold),
+ LIS3L02DQ_EVENT_MASK, NULL),
IIO_CHAN_EV(IIO_ACCEL, 'y', LIS3L02DQ_INFO_MASK,
1, 1, IIO_ST('s', 12, 16, 0),
- LIS3L02DQ_EVENT_MASK, &iio_event_threshold),
- IIO_CHAN(IIO_ACCEL, 'z', LIS3L02DQ_INFO_MASK,
- 2, 2, IIO_ST('s', 12, 16, 0)),
+ LIS3L02DQ_EVENT_MASK, NULL),
+ IIO_CHAN_EV(IIO_ACCEL, 'z', LIS3L02DQ_INFO_MASK,
+ 2, 2, IIO_ST('s', 12, 16, 0),
+ LIS3L02DQ_EVENT_MASK, NULL),
IIO_CHAN_SOFT_TIMESTAMP(3)
};

@@ -475,11 +471,57 @@ static ssize_t lis3l02dq_read_event_config(struct iio_dev *indio_dev,
return !!(val & mask);
}

+int lis3l02dq_disable_all_events(struct iio_dev *indio_dev)
+{
+ struct iio_sw_ring_helper_state *h
+ = iio_dev_get_devdata(indio_dev);
+ struct lis3l02dq_state *st = lis3l02dq_h_to_s(h);
+ int ret;
+ u8 control, val;
+ bool irqtofree;
+
+ ret = lis3l02dq_spi_read_reg_8(indio_dev,
+ LIS3L02DQ_REG_CTRL_2_ADDR,
+ &control);
+
+ irqtofree = !!(control & LIS3L02DQ_REG_CTRL_2_ENABLE_INTERRUPT);
+
+ control &= ~LIS3L02DQ_REG_CTRL_2_ENABLE_INTERRUPT;
+ ret = lis3l02dq_spi_write_reg_8(indio_dev,
+ LIS3L02DQ_REG_CTRL_2_ADDR,
+ &control);
+ if (ret)
+ goto error_ret;
+ /* Also for consistency clear the mask */
+ ret = lis3l02dq_spi_read_reg_8(indio_dev,
+ LIS3L02DQ_REG_WAKE_UP_CFG_ADDR,
+ &val);
+ if (ret)
+ goto error_ret;
+ val &= ~0x3f;
+
+ ret = lis3l02dq_spi_write_reg_8(indio_dev,
+ LIS3L02DQ_REG_WAKE_UP_CFG_ADDR,
+ &val);
+ if (ret)
+ goto error_ret;
+
+ if (irqtofree)
+ free_irq(st->us->irq, indio_dev->interrupts[0]);
+
+ ret = control;
+error_ret:
+ return ret;
+}
+
static int lis3l02dq_write_event_config(struct iio_dev *indio_dev,
int event_code,
struct iio_event_handler_list *list_el,
int state)
{
+ struct iio_sw_ring_helper_state *h
+ = iio_dev_get_devdata(indio_dev);
+ struct lis3l02dq_state *st = lis3l02dq_h_to_s(h);
int ret = 0;
u8 val, control;
u8 currentlyset;
@@ -505,27 +547,39 @@ static int lis3l02dq_write_event_config(struct iio_dev *indio_dev,
if (!currentlyset && state) {
changed = true;
val |= mask;
- iio_add_event_to_list(list_el,
- &indio_dev->interrupts[0]->ev_list);
-
} else if (currentlyset && !state) {
changed = true;
val &= ~mask;
- iio_remove_event_from_list(list_el,
- &indio_dev->interrupts[0]->ev_list);
}
+
if (changed) {
+ if (!(control & LIS3L02DQ_REG_CTRL_2_ENABLE_INTERRUPT)) {
+ ret = request_irq(st->us->irq,
+ &lis3l02dq_event_handler,
+ IRQF_TRIGGER_RISING,
+ "lis3l02dq_event",
+ indio_dev->interrupts[0]);
+ if (ret)
+ goto error_ret;
+ }
+
ret = lis3l02dq_spi_write_reg_8(indio_dev,
LIS3L02DQ_REG_WAKE_UP_CFG_ADDR,
&val);
if (ret)
goto error_ret;
- control = list_el->refcount ?
+ control = val & 0x3f ?
(control | LIS3L02DQ_REG_CTRL_2_ENABLE_INTERRUPT) :
(control & ~LIS3L02DQ_REG_CTRL_2_ENABLE_INTERRUPT);
ret = lis3l02dq_spi_write_reg_8(indio_dev,
LIS3L02DQ_REG_CTRL_2_ADDR,
&control);
+ if (ret)
+ goto error_ret;
+
+ /* remove interrupt handler if nothing is still on */
+ if (!(val & 0x3f))
+ free_irq(st->us->irq, indio_dev->interrupts[0]);
}

error_ret:
@@ -538,7 +592,7 @@ error_ret:
*/
static void lis3l02dq_thresh_handler_bh_no_check(struct work_struct *work_s)
{
- struct lis3l02dq_state *st
+ struct lis3l02dq_state *st
= container_of(work_s,
struct lis3l02dq_state, work_thresh);

@@ -696,7 +750,6 @@ static int __devinit lis3l02dq_probe(struct spi_device *spi)
"lis3l02dq");
if (ret)
goto error_uninitialize_ring;
-
ret = lis3l02dq_probe_trigger(st->help.indio_dev);
if (ret)
goto error_unregister_line;
@@ -767,6 +820,9 @@ static int lis3l02dq_remove(struct spi_device *spi)
int ret;
struct lis3l02dq_state *st = spi_get_drvdata(spi);
struct iio_dev *indio_dev = st->help.indio_dev;
+ ret = lis3l02dq_disable_all_events(indio_dev);
+ if (ret)
+ goto err_ret;

ret = lis3l02dq_stop_device(indio_dev);
if (ret)
diff --git a/drivers/staging/iio/accel/lis3l02dq_ring.c b/drivers/staging/iio/accel/lis3l02dq_ring.c
index 1a4b3fc..9b15cad 100644
--- a/drivers/staging/iio/accel/lis3l02dq_ring.c
+++ b/drivers/staging/iio/accel/lis3l02dq_ring.c
@@ -51,23 +51,14 @@ static void lis3l02dq_poll_func_th(struct iio_dev *indio_dev, s64 time)
/**
* lis3l02dq_data_rdy_trig_poll() the event handler for the data rdy trig
**/
-static int lis3l02dq_data_rdy_trig_poll(struct iio_dev *indio_dev,
- int index,
- s64 timestamp,
- int no_test)
+static irqreturn_t lis3l02dq_data_rdy_trig_poll(int irq, void *private)
{
- struct iio_sw_ring_helper_state *h
- = iio_dev_get_devdata(indio_dev);
- struct lis3l02dq_state *st = lis3l02dq_h_to_s(h);
-
- iio_trigger_poll(st->trig, timestamp);
+ disable_irq_nosync(irq);
+ iio_trigger_poll(private, iio_get_time_ns());

return IRQ_HANDLED;
}

-/* This is an event as it is a response to a physical interrupt */
-IIO_EVENT_SH(data_rdy_trig, &lis3l02dq_data_rdy_trig_poll);
-
/**
* lis3l02dq_read_accel_from_ring() individual acceleration read from ring
**/
@@ -196,14 +187,15 @@ static int lis3l02dq_get_ring_element(struct iio_sw_ring_helper_state *h,

/* Caller responsible for locking as necessary. */
static int
-__lis3l02dq_write_data_ready_config(struct device *dev,
- struct iio_event_handler_list *list,
- bool state)
+__lis3l02dq_write_data_ready_config(struct device *dev, bool state)
{
int ret;
u8 valold;
bool currentlyset;
struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct iio_sw_ring_helper_state *h
+ = iio_dev_get_devdata(indio_dev);
+ struct lis3l02dq_state *st = lis3l02dq_h_to_s(h);

/* Get the current event mask register */
ret = lis3l02dq_spi_read_reg_8(indio_dev,
@@ -217,8 +209,9 @@ __lis3l02dq_write_data_ready_config(struct device *dev,

/* Disable requested */
if (!state && currentlyset) {
-
+ /* disable the data ready signal */
valold &= ~LIS3L02DQ_REG_CTRL_2_ENABLE_DATA_READY_GENERATION;
+
/* The double write is to overcome a hardware bug?*/
ret = lis3l02dq_spi_write_reg_8(indio_dev,
LIS3L02DQ_REG_CTRL_2_ADDR,
@@ -231,20 +224,31 @@ __lis3l02dq_write_data_ready_config(struct device *dev,
if (ret)
goto error_ret;

- iio_remove_event_from_list(list,
- &indio_dev->interrupts[0]
- ->ev_list);
-
+ free_irq(st->us->irq, st->trig);
/* Enable requested */
} else if (state && !currentlyset) {
/* if not set, enable requested */
- valold |= LIS3L02DQ_REG_CTRL_2_ENABLE_DATA_READY_GENERATION;
- iio_add_event_to_list(list, &indio_dev->interrupts[0]->ev_list);
+ /* first disable all events */
+ ret = lis3l02dq_disable_all_events(indio_dev);
+ if (ret < 0)
+ goto error_ret;
+
+ valold = ret |
+ LIS3L02DQ_REG_CTRL_2_ENABLE_DATA_READY_GENERATION;
+ ret = request_irq(st->us->irq,
+ lis3l02dq_data_rdy_trig_poll,
+ IRQF_TRIGGER_RISING, "lis3l02dq_datardy",
+ st->trig);
+ if (ret)
+ goto error_ret;
+
ret = lis3l02dq_spi_write_reg_8(indio_dev,
LIS3L02DQ_REG_CTRL_2_ADDR,
&valold);
- if (ret)
+ if (ret) {
+ free_irq(st->us->irq, st->trig);
goto error_ret;
+ }
}

return 0;
@@ -265,10 +269,8 @@ static int lis3l02dq_data_rdy_trigger_set_state(struct iio_trigger *trig,
struct lis3l02dq_state *st = trig->private_data;
int ret = 0;
u8 t;
- __lis3l02dq_write_data_ready_config(&st->help.indio_dev->dev,
- &iio_event_data_rdy_trig,
- state);
- if (st == false) {
+ __lis3l02dq_write_data_ready_config(&st->help.indio_dev->dev, state);
+ if (state == false) {
/* possible quirk with handler currently worked around
by ensuring the work queue is empty */
flush_scheduled_work();
--
1.7.3.4

2011-03-31 14:56:32

by Jonathan Cameron

[permalink] [raw]
Subject: [PATCH 09/21] staging:iio: Push interrupt setup down into the drivers for event lines.

It is much easier to do in driver, and the core does not add much.
Note all drivers will have to be updated with this patch.
None currently are.
---
drivers/staging/iio/iio.h | 3 +++
drivers/staging/iio/industrialio-core.c | 17 +++--------------
2 files changed, 6 insertions(+), 14 deletions(-)

diff --git a/drivers/staging/iio/iio.h b/drivers/staging/iio/iio.h
index 1496f0b..341724f 100644
--- a/drivers/staging/iio/iio.h
+++ b/drivers/staging/iio/iio.h
@@ -12,6 +12,7 @@

#include <linux/device.h>
#include <linux/cdev.h>
+#include <linux/irq.h>
#include "sysfs.h"
#include "chrdev.h"

@@ -328,6 +329,8 @@ void iio_unregister_interrupt_line(struct iio_dev *dev_info,
int line_number);


+/* temporarily exported to allow moving of interrupt requesting into drivers */
+irqreturn_t iio_interrupt_handler(int irq, void *_int_info);

/**
* iio_push_event() - try to add event to the list for userspace reading
diff --git a/drivers/staging/iio/industrialio-core.c b/drivers/staging/iio/industrialio-core.c
index 5b7b5df..7cee697 100644
--- a/drivers/staging/iio/industrialio-core.c
+++ b/drivers/staging/iio/industrialio-core.c
@@ -118,7 +118,7 @@ int iio_push_event(struct iio_dev *dev_info,
EXPORT_SYMBOL(iio_push_event);

/* Generic interrupt line interrupt handler */
-static irqreturn_t iio_interrupt_handler(int irq, void *_int_info)
+irqreturn_t iio_interrupt_handler(int irq, void *_int_info)
{
struct iio_interrupt *int_info = _int_info;
struct iio_dev *dev_info = int_info->dev_info;
@@ -141,6 +141,7 @@ static irqreturn_t iio_interrupt_handler(int irq, void *_int_info)

return IRQ_HANDLED;
}
+EXPORT_SYMBOL(iio_interrupt_handler);

static struct iio_interrupt *iio_allocate_interrupt(void)
{
@@ -159,7 +160,7 @@ int iio_register_interrupt_line(unsigned int irq,
unsigned long type,
const char *name)
{
- int ret;
+ int ret = 0;

dev_info->interrupts[line_number] = iio_allocate_interrupt();
if (dev_info->interrupts[line_number] == NULL) {
@@ -170,16 +171,6 @@ int iio_register_interrupt_line(unsigned int irq,
dev_info->interrupts[line_number]->irq = irq;
dev_info->interrupts[line_number]->dev_info = dev_info;

- /* Possibly only request on demand?
- * Can see this may complicate the handling of interrupts.
- * However, with this approach we might end up handling lots of
- * events no-one cares about.*/
- ret = request_irq(irq,
- &iio_interrupt_handler,
- type,
- name,
- dev_info->interrupts[line_number]);
-
error_ret:
return ret;
}
@@ -199,8 +190,6 @@ void iio_unregister_interrupt_line(struct iio_dev *dev_info, int line_number)
{
/* make sure the interrupt handlers are all done */
flush_scheduled_work();
- free_irq(dev_info->interrupts[line_number]->irq,
- dev_info->interrupts[line_number]);
kfree(dev_info->interrupts[line_number]);
}
EXPORT_SYMBOL(iio_unregister_interrupt_line);
--
1.7.3.4

2011-03-31 14:56:54

by Jonathan Cameron

[permalink] [raw]
Subject: [PATCH 03/21] staging:iio:max1363 - experimental move to channel_spec registration.

---
drivers/staging/iio/adc/max1363.h | 8 +-
drivers/staging/iio/adc/max1363_core.c | 1277 ++++++++++++--------------------
drivers/staging/iio/adc/max1363_ring.c | 1 -
3 files changed, 478 insertions(+), 808 deletions(-)

diff --git a/drivers/staging/iio/adc/max1363.h b/drivers/staging/iio/adc/max1363.h
index 6a8687f..b5a3dcd 100644
--- a/drivers/staging/iio/adc/max1363.h
+++ b/drivers/staging/iio/adc/max1363.h
@@ -152,24 +152,24 @@ enum max1363_modes {
/**
* struct max1363_chip_info - chip specifc information
* @name: indentification string for chip
- * @num_inputs: number of physical inputs on chip
* @bits: accuracy of the adc in bits
* @int_vref_mv: the internal reference voltage
* @monitor_mode: whether the chip supports monitor interrupts
* @mode_list: array of available scan modes
* @num_modes: the number of scan modes available
* @default_mode: the scan mode in which the chip starts up
+ * @channel: channel specification
+ * @num_channels: number of channels
*/
struct max1363_chip_info {
- u8 num_inputs;
u8 bits;
u16 int_vref_mv;
bool monitor_mode;
const enum max1363_modes *mode_list;
int num_modes;
enum max1363_modes default_mode;
- struct attribute_group *dev_attrs;
- struct attribute_group *scan_attrs;
+ struct iio_chan_spec *channels;
+ int num_channels;
};

/**
diff --git a/drivers/staging/iio/adc/max1363_core.c b/drivers/staging/iio/adc/max1363_core.c
index a060b3d..ce4727e 100644
--- a/drivers/staging/iio/adc/max1363_core.c
+++ b/drivers/staging/iio/adc/max1363_core.c
@@ -42,39 +42,6 @@
/* Here we claim all are 16 bits. This currently does no harm and saves
* us a lot of scan element listings */

-#define MAX1363_SCAN_EL(number) \
- IIO_SCAN_EL_C(in##number, number, 0, NULL);
-#define MAX1363_SCAN_EL_D(p, n, number) \
- IIO_SCAN_NAMED_EL_C(in##p##m##in##n, in##p-in##n, number, 0, NULL);
-
-static MAX1363_SCAN_EL(0);
-static MAX1363_SCAN_EL(1);
-static MAX1363_SCAN_EL(2);
-static MAX1363_SCAN_EL(3);
-static MAX1363_SCAN_EL(4);
-static MAX1363_SCAN_EL(5);
-static MAX1363_SCAN_EL(6);
-static MAX1363_SCAN_EL(7);
-static MAX1363_SCAN_EL(8);
-static MAX1363_SCAN_EL(9);
-static MAX1363_SCAN_EL(10);
-static MAX1363_SCAN_EL(11);
-static MAX1363_SCAN_EL_D(0, 1, 12);
-static MAX1363_SCAN_EL_D(2, 3, 13);
-static MAX1363_SCAN_EL_D(4, 5, 14);
-static MAX1363_SCAN_EL_D(6, 7, 15);
-static MAX1363_SCAN_EL_D(8, 9, 16);
-static MAX1363_SCAN_EL_D(10, 11, 17);
-static MAX1363_SCAN_EL_D(1, 0, 18);
-static MAX1363_SCAN_EL_D(3, 2, 19);
-static MAX1363_SCAN_EL_D(5, 4, 20);
-static MAX1363_SCAN_EL_D(7, 6, 21);
-static MAX1363_SCAN_EL_D(9, 8, 22);
-static MAX1363_SCAN_EL_D(11, 10, 23);
-
-static IIO_SCAN_EL_TIMESTAMP(24);
-static IIO_CONST_ATTR_SCAN_EL_TYPE(timestamp, s, 64, 64);
-
static const struct max1363_mode max1363_mode_table[] = {
/* All of the single channel options first */
MAX1363_MODE_SINGLE(0, 1 << 0),
@@ -150,58 +117,6 @@ const struct max1363_mode
return NULL;
}

-static ssize_t max1363_show_precision_u(struct device *dev,
- struct device_attribute *attr,
- char *buf)
-{
- struct iio_ring_buffer *ring = dev_get_drvdata(dev);
- struct max1363_state *st = iio_priv(ring->indio_dev);
- return sprintf(buf, "u%d/16\n", st->chip_info->bits);
-}
-
-static ssize_t max1363_show_precision_s(struct device *dev,
- struct device_attribute *attr,
- char *buf)
-{
- struct iio_ring_buffer *ring = dev_get_drvdata(dev);
- struct max1363_state *st = iio_priv(ring->indio_dev);
- return sprintf(buf, "s%d/16\n", st->chip_info->bits);
-}
-
-#define MAX1363_SCAN_TYPE(n) \
- DEVICE_ATTR(in##n##_type, S_IRUGO, \
- max1363_show_precision_u, NULL);
-#define MAX1363_SCAN_TYPE_D(p, n) \
- struct device_attribute dev_attr_in##p##m##in##n##_type = \
- __ATTR(in##p-in##n##_type, S_IRUGO, \
- max1363_show_precision_s, NULL);
-
-static MAX1363_SCAN_TYPE(0);
-static MAX1363_SCAN_TYPE(1);
-static MAX1363_SCAN_TYPE(2);
-static MAX1363_SCAN_TYPE(3);
-static MAX1363_SCAN_TYPE(4);
-static MAX1363_SCAN_TYPE(5);
-static MAX1363_SCAN_TYPE(6);
-static MAX1363_SCAN_TYPE(7);
-static MAX1363_SCAN_TYPE(8);
-static MAX1363_SCAN_TYPE(9);
-static MAX1363_SCAN_TYPE(10);
-static MAX1363_SCAN_TYPE(11);
-
-static MAX1363_SCAN_TYPE_D(0, 1);
-static MAX1363_SCAN_TYPE_D(2, 3);
-static MAX1363_SCAN_TYPE_D(4, 5);
-static MAX1363_SCAN_TYPE_D(6, 7);
-static MAX1363_SCAN_TYPE_D(8, 9);
-static MAX1363_SCAN_TYPE_D(10, 11);
-static MAX1363_SCAN_TYPE_D(1, 0);
-static MAX1363_SCAN_TYPE_D(3, 2);
-static MAX1363_SCAN_TYPE_D(5, 4);
-static MAX1363_SCAN_TYPE_D(7, 6);
-static MAX1363_SCAN_TYPE_D(9, 8);
-static MAX1363_SCAN_TYPE_D(11, 10);
-
static int max1363_write_basic_config(struct i2c_client *client,
unsigned char d1,
unsigned char d2)
@@ -232,18 +147,17 @@ int max1363_set_scan_mode(struct max1363_state *st)
st->configbyte);
}

-static ssize_t max1363_read_single_channel(struct device *dev,
- struct device_attribute *attr,
- char *buf)
+static int max1363_read_single_chan(struct iio_dev *indio_dev,
+ struct iio_chan_spec *chan,
+ int *val,
+ long m)
{
- struct iio_dev *indio_dev = dev_get_drvdata(dev);
- struct max1363_state *st = iio_priv(indio_dev);
- struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
- struct i2c_client *client = st->client;
- int ret = 0, len = 0;
- s32 data ;
+ int ret = 0;
+ s32 data;
char rxbuf[2];
long mask;
+ struct max1363_state *st = iio_priv(indio_dev);
+ struct i2c_client *client = st->client;

mutex_lock(&indio_dev->mlock);
/*
@@ -258,7 +172,7 @@ static ssize_t max1363_read_single_channel(struct device *dev,

/* If ring buffer capture is occuring, query the buffer */
if (iio_ring_enabled(indio_dev)) {
- mask = max1363_mode_table[this_attr->address].modemask;
+ mask = max1363_mode_table[chan->address].modemask;
data = max1363_single_channel_from_ring(mask, st);
if (data < 0) {
ret = data;
@@ -267,10 +181,10 @@ static ssize_t max1363_read_single_channel(struct device *dev,
} else {
/* Check to see if current scan mode is correct */
if (st->current_mode !=
- &max1363_mode_table[this_attr->address]) {
+ &max1363_mode_table[chan->address]) {
/* Update scan mode if needed */
st->current_mode
- = &max1363_mode_table[this_attr->address];
+ = &max1363_mode_table[chan->address];
ret = max1363_set_scan_mode(st);
if (ret)
goto error_ret;
@@ -282,7 +196,6 @@ static ssize_t max1363_read_single_channel(struct device *dev,
ret = data;
goto error_ret;
}
-
data = (s32)(rxbuf[1]) | ((s32)(rxbuf[0] & 0x0F)) << 8;
} else {
/* Get reading */
@@ -294,60 +207,38 @@ static ssize_t max1363_read_single_channel(struct device *dev,
data = rxbuf[0];
}
}
- /* Pretty print the result */
- len = sprintf(buf, "%u\n", data);
-
+ *val = data;
error_ret:
mutex_unlock(&indio_dev->mlock);
- return ret ? ret : len;
+ return ret;
+
}

-/* 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)
+static int max1363_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec *chan,
+ int *val,
+ long m)
{
- /* Driver currently only support internal vref */
- struct max1363_state *st = iio_priv(dev_get_drvdata(dev));
- /* Corresponds to Vref / 2^(bits) */
+ struct max1363_state *st = iio_priv(indio_dev);

- 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);
+ switch (m) {
+ case 0:
+ return max1363_read_single_chan(indio_dev, chan, val, m);
+ break;
+ case (1 << IIO_CHAN_INFO_SCALE_SHARED):
+ if ((1 << (st->chip_info->bits + 1)) >
+ st->chip_info->int_vref_mv)
+ *val = 500000;
+ else
+ *val = (1000000*st->chip_info->int_vref_mv)
+ >> st->chip_info->bits;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 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,
char *buf)
@@ -366,17 +257,119 @@ static const enum max1363_modes max1363_mode_list[] = {
d0m1to2m3, d1m0to3m2,
};

+static int max1363_int_th(struct iio_dev *indio_dev,
+ int index,
+ s64 timestamp,
+ int not_test)
+{
+ struct max1363_state *st = iio_priv(indio_dev);
+
+ st->last_timestamp = timestamp;
+ schedule_work(&st->thresh_work);
+ return 0;
+}
+
+IIO_EVENT_SH(max1363_thresh, max1363_int_th);
+
+#define MAX1363_EV_M \
+ (IIO_EV_BIT(IIO_EV_TYPE_THRESH, IIO_EV_DIR_RISING) \
+ | IIO_EV_BIT(IIO_EV_TYPE_THRESH, IIO_EV_DIR_FALLING))
+#define MAX1363_INFO_MASK (1 << IIO_CHAN_INFO_SCALE_SHARED)
+
+static struct iio_chan_spec max1363_channels[] = {
+ IIO_CHAN_EV(IIO_IN, 0, MAX1363_INFO_MASK, _s0, 0,
+ IIO_ST('u', 12, 16, 0),
+ MAX1363_EV_M, &iio_event_max1363_thresh),
+ IIO_CHAN_EV(IIO_IN, 1, MAX1363_INFO_MASK,
+ _s1, 1, IIO_ST('u', 12, 16, 0),
+ MAX1363_EV_M, &iio_event_max1363_thresh),
+ IIO_CHAN_EV(IIO_IN, 2, MAX1363_INFO_MASK,
+ _s2, 2, IIO_ST('u', 12, 16, 0),
+ MAX1363_EV_M, &iio_event_max1363_thresh),
+ IIO_CHAN_EV(IIO_IN, 3, MAX1363_INFO_MASK,
+ _s3, 3, IIO_ST('u', 12, 16, 0),
+ MAX1363_EV_M, &iio_event_max1363_thresh),
+ IIO_CHAN_COMPOUND_EV(IIO_IN_DIFF, 0, 1, MAX1363_INFO_MASK,
+ d0m1, 4, IIO_ST('s', 12, 16, 0),
+ MAX1363_EV_M, &iio_event_max1363_thresh),
+ IIO_CHAN_COMPOUND_EV(IIO_IN_DIFF, 2, 3, MAX1363_INFO_MASK,
+ d2m3, 5, IIO_ST('s', 12, 16, 0),
+ MAX1363_EV_M, &iio_event_max1363_thresh),
+ IIO_CHAN_COMPOUND_EV(IIO_IN_DIFF, 1, 0, MAX1363_INFO_MASK,
+ d1m0, 6, IIO_ST('s', 12, 16, 0),
+ MAX1363_EV_M, &iio_event_max1363_thresh),
+ IIO_CHAN_COMPOUND_EV(IIO_IN_DIFF, 3, 2, MAX1363_INFO_MASK,
+ d3m2, 7, IIO_ST('s', 12, 16, 0),
+ MAX1363_EV_M, &iio_event_max1363_thresh),
+ IIO_CHAN_SOFT_TIMESTAMP(8)
+};
+
+static struct iio_chan_spec max1236_channels[] = {
+ IIO_CHAN(IIO_IN, 0, MAX1363_INFO_MASK, _s0, 0, IIO_ST('u', 12, 16, 0)),
+ IIO_CHAN(IIO_IN, 1, MAX1363_INFO_MASK, _s1, 1, IIO_ST('u', 12, 16, 0)),
+ IIO_CHAN(IIO_IN, 2, MAX1363_INFO_MASK, _s2, 2, IIO_ST('u', 12, 16, 0)),
+ IIO_CHAN(IIO_IN, 3, MAX1363_INFO_MASK, _s3, 3, IIO_ST('u', 12, 16, 0)),
+ IIO_CHAN_COMPOUND(IIO_IN_DIFF, 0, 1, MAX1363_INFO_MASK,
+ d0m1, 4, IIO_ST('s', 12, 16, 0)),
+ IIO_CHAN_COMPOUND(IIO_IN_DIFF, 2, 3, MAX1363_INFO_MASK,
+ d2m3, 5, IIO_ST('s', 12, 16, 0)),
+ IIO_CHAN_COMPOUND(IIO_IN_DIFF, 1, 0, MAX1363_INFO_MASK,
+ d1m0, 6, IIO_ST('s', 12, 16, 0)),
+ IIO_CHAN_COMPOUND(IIO_IN_DIFF, 3, 2, MAX1363_INFO_MASK,
+ d3m2, 7, IIO_ST('s', 12, 16, 0)),
+ IIO_CHAN_SOFT_TIMESTAMP(8)
+};
+
+static struct iio_chan_spec max1361_channels[] = {
+ IIO_CHAN(IIO_IN, 0, MAX1363_INFO_MASK, _s0, 0, IIO_ST('u', 10, 16, 0)),
+ IIO_CHAN(IIO_IN, 1, MAX1363_INFO_MASK, _s1, 1, IIO_ST('u', 10, 16, 0)),
+ IIO_CHAN(IIO_IN, 2, MAX1363_INFO_MASK, _s2, 2, IIO_ST('u', 10, 16, 0)),
+ IIO_CHAN(IIO_IN, 3, MAX1363_INFO_MASK, _s3, 3, IIO_ST('u', 10, 16, 0)),
+ IIO_CHAN_COMPOUND(IIO_IN_DIFF, 0, 1, MAX1363_INFO_MASK,
+ d0m1, 4, IIO_ST('s', 10, 16, 0)),
+ IIO_CHAN_COMPOUND(IIO_IN_DIFF, 2, 3, MAX1363_INFO_MASK,
+ d2m3, 5, IIO_ST('s', 10, 16, 0)),
+ IIO_CHAN_COMPOUND(IIO_IN_DIFF, 1, 0, MAX1363_INFO_MASK,
+ d1m0, 6, IIO_ST('s', 10, 16, 0)),
+ IIO_CHAN_COMPOUND(IIO_IN_DIFF, 3, 2, MAX1363_INFO_MASK,
+ d3m2, 7, IIO_ST('s', 10, 16, 0)),
+ IIO_CHAN_SOFT_TIMESTAMP(8)
+};
+
+static struct iio_chan_spec max1136_channels[] = {
+ IIO_CHAN(IIO_IN, 0, MAX1363_INFO_MASK, _s0, 0, IIO_ST('u', 10, 16, 0)),
+ IIO_CHAN(IIO_IN, 1, MAX1363_INFO_MASK, _s1, 1, IIO_ST('u', 10, 16, 0)),
+ IIO_CHAN(IIO_IN, 2, MAX1363_INFO_MASK, _s2, 2, IIO_ST('u', 10, 16, 0)),
+ IIO_CHAN(IIO_IN, 3, MAX1363_INFO_MASK, _s3, 3, IIO_ST('u', 10, 16, 0)),
+ IIO_CHAN_COMPOUND(IIO_IN_DIFF, 0, 1, MAX1363_INFO_MASK,
+ d0m1, 4, IIO_ST('s', 10, 16, 0)),
+ IIO_CHAN_COMPOUND(IIO_IN_DIFF, 2, 3, MAX1363_INFO_MASK,
+ d2m3, 5, IIO_ST('s', 10, 16, 0)),
+ IIO_CHAN_COMPOUND(IIO_IN_DIFF, 1, 0, MAX1363_INFO_MASK,
+ d1m0, 6, IIO_ST('s', 10, 16, 0)),
+ IIO_CHAN_COMPOUND(IIO_IN_DIFF, 3, 2, MAX1363_INFO_MASK,
+ d3m2, 7, IIO_ST('s', 10, 16, 0)),
+ IIO_CHAN_SOFT_TIMESTAMP(8)
+};
+
+static struct iio_chan_spec max1036_channels[] = {
+ IIO_CHAN(IIO_IN, 0, MAX1363_INFO_MASK, _s0, 0, IIO_ST('u', 8, 8, 0)),
+ IIO_CHAN(IIO_IN, 1, MAX1363_INFO_MASK, _s1, 1, IIO_ST('u', 8, 8, 0)),
+ IIO_CHAN(IIO_IN, 2, MAX1363_INFO_MASK, _s2, 2, IIO_ST('u', 8, 8, 0)),
+ IIO_CHAN(IIO_IN, 3, MAX1363_INFO_MASK, _s3, 3, IIO_ST('u', 8, 8, 0)),
+ IIO_CHAN_COMPOUND(IIO_IN_DIFF, 0, 1, MAX1363_INFO_MASK,
+ d0m1, 4, IIO_ST('s', 8, 8, 0)),
+ IIO_CHAN_COMPOUND(IIO_IN_DIFF, 2, 3, MAX1363_INFO_MASK,
+ d2m3, 5, IIO_ST('s', 8, 8, 0)),
+ IIO_CHAN_COMPOUND(IIO_IN_DIFF, 1, 0, MAX1363_INFO_MASK,
+ d1m0, 6, IIO_ST('s', 8, 8, 0)),
+ IIO_CHAN_COMPOUND(IIO_IN_DIFF, 3, 2, MAX1363_INFO_MASK,
+ d3m2, 7, IIO_ST('s', 8, 8, 0)),
+ IIO_CHAN_SOFT_TIMESTAMP(8)
+};
+
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
};

@@ -384,34 +377,6 @@ static struct attribute_group max1363_dev_attr_group = {
.attrs = max1363_device_attrs,
};

-static struct attribute *max1363_scan_el_attrs[] = {
- &iio_scan_el_in0.dev_attr.attr, &dev_attr_in0_type.attr,
- &iio_const_attr_in0_index.dev_attr.attr,
- &iio_scan_el_in1.dev_attr.attr, &dev_attr_in1_type.attr,
- &iio_const_attr_in1_index.dev_attr.attr,
- &iio_scan_el_in2.dev_attr.attr, &dev_attr_in2_type.attr,
- &iio_const_attr_in2_index.dev_attr.attr,
- &iio_scan_el_in3.dev_attr.attr, &dev_attr_in3_type.attr,
- &iio_const_attr_in3_index.dev_attr.attr,
- &iio_scan_el_in0min1.dev_attr.attr, &dev_attr_in0min1_type.attr,
- &iio_const_attr_in0min1_index.dev_attr.attr,
- &iio_scan_el_in2min3.dev_attr.attr, &dev_attr_in2min3_type.attr,
- &iio_const_attr_in2min3_index.dev_attr.attr,
- &iio_scan_el_in1min0.dev_attr.attr, &dev_attr_in1min0_type.attr,
- &iio_const_attr_in1min0_index.dev_attr.attr,
- &iio_scan_el_in3min2.dev_attr.attr, &dev_attr_in3min2_type.attr,
- &iio_const_attr_in3min2_index.dev_attr.attr,
- &iio_const_attr_timestamp_index.dev_attr.attr,
- &iio_scan_el_timestamp.dev_attr.attr,
- &iio_const_attr_timestamp_type.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,
@@ -434,101 +399,130 @@ static const enum max1363_modes max1238_mode_list[] = {
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 iio_chan_spec max1038_channels[] = {
+ IIO_CHAN(IIO_IN, 0, MAX1363_INFO_MASK, _s0, 0, IIO_ST('u', 8, 8, 0)),
+ IIO_CHAN(IIO_IN, 1, MAX1363_INFO_MASK, _s0, 1, IIO_ST('u', 8, 8, 0)),
+ IIO_CHAN(IIO_IN, 2, MAX1363_INFO_MASK, _s0, 2, IIO_ST('u', 8, 8, 0)),
+ IIO_CHAN(IIO_IN, 3, MAX1363_INFO_MASK, _s0, 3, IIO_ST('u', 8, 8, 0)),
+ IIO_CHAN(IIO_IN, 4, MAX1363_INFO_MASK, _s0, 4, IIO_ST('u', 8, 8, 0)),
+ IIO_CHAN(IIO_IN, 5, MAX1363_INFO_MASK, _s0, 5, IIO_ST('u', 8, 8, 0)),
+ IIO_CHAN(IIO_IN, 6, MAX1363_INFO_MASK, _s0, 6, IIO_ST('u', 8, 8, 0)),
+ IIO_CHAN(IIO_IN, 7, MAX1363_INFO_MASK, _s0, 7, IIO_ST('u', 8, 8, 0)),
+ IIO_CHAN(IIO_IN, 8, MAX1363_INFO_MASK, _s0, 8, IIO_ST('u', 8, 8, 0)),
+ IIO_CHAN(IIO_IN, 9, MAX1363_INFO_MASK, _s0, 9, IIO_ST('u', 8, 8, 0)),
+ IIO_CHAN(IIO_IN, 10, MAX1363_INFO_MASK, _s0, 10, IIO_ST('u', 8, 8, 0)),
+ IIO_CHAN(IIO_IN, 11, MAX1363_INFO_MASK, _s0, 11, IIO_ST('u', 8, 8, 0)),
+ IIO_CHAN_COMPOUND(IIO_IN_DIFF, 0, 1, MAX1363_INFO_MASK,
+ d0m1, 12, IIO_ST('s', 8, 8, 0)),
+ IIO_CHAN_COMPOUND(IIO_IN_DIFF, 2, 3, MAX1363_INFO_MASK,
+ d2m3, 13, IIO_ST('s', 8, 8, 0)),
+ IIO_CHAN_COMPOUND(IIO_IN_DIFF, 4, 5, MAX1363_INFO_MASK,
+ d4m5, 14, IIO_ST('s', 8, 8, 0)),
+ IIO_CHAN_COMPOUND(IIO_IN_DIFF, 6, 7, MAX1363_INFO_MASK,
+ d6m7, 15, IIO_ST('s', 8, 8, 0)),
+ IIO_CHAN_COMPOUND(IIO_IN_DIFF, 8, 9, MAX1363_INFO_MASK,
+ d8m9, 16, IIO_ST('s', 8, 8, 0)),
+ IIO_CHAN_COMPOUND(IIO_IN_DIFF, 10, 11, MAX1363_INFO_MASK,
+ d10m11, 17, IIO_ST('s', 8, 8, 0)),
+ IIO_CHAN_COMPOUND(IIO_IN_DIFF, 1, 0, MAX1363_INFO_MASK,
+ d1m0, 18, IIO_ST('s', 8, 8, 0)),
+ IIO_CHAN_COMPOUND(IIO_IN_DIFF, 3, 2, MAX1363_INFO_MASK,
+ d3m2, 19, IIO_ST('s', 8, 8, 0)),
+ IIO_CHAN_COMPOUND(IIO_IN_DIFF, 5, 4, MAX1363_INFO_MASK,
+ d5m4, 20, IIO_ST('s', 8, 8, 0)),
+ IIO_CHAN_COMPOUND(IIO_IN_DIFF, 7, 6, MAX1363_INFO_MASK,
+ d7m6, 21, IIO_ST('s', 8, 8, 0)),
+ IIO_CHAN_COMPOUND(IIO_IN_DIFF, 9, 8, MAX1363_INFO_MASK,
+ d9m8, 22, IIO_ST('s', 8, 8, 0)),
+ IIO_CHAN_COMPOUND(IIO_IN_DIFF, 11, 10, MAX1363_INFO_MASK,
+ d11m10, 23, IIO_ST('s', 8, 8, 0)),
+ IIO_CHAN_SOFT_TIMESTAMP(24)
};

-static struct attribute_group max1238_dev_attr_group = {
- .attrs = max1238_device_attrs,
-};
-
-static struct attribute *max1238_scan_el_attrs[] = {
- &iio_scan_el_in0.dev_attr.attr, &dev_attr_in0_type.attr,
- &iio_const_attr_in0_index.dev_attr.attr,
- &iio_scan_el_in1.dev_attr.attr, &dev_attr_in1_type.attr,
- &iio_const_attr_in1_index.dev_attr.attr,
- &iio_scan_el_in2.dev_attr.attr, &dev_attr_in2_type.attr,
- &iio_const_attr_in2_index.dev_attr.attr,
- &iio_scan_el_in3.dev_attr.attr, &dev_attr_in3_type.attr,
- &iio_const_attr_in3_index.dev_attr.attr,
- &iio_scan_el_in4.dev_attr.attr, &dev_attr_in4_type.attr,
- &iio_const_attr_in4_index.dev_attr.attr,
- &iio_scan_el_in5.dev_attr.attr, &dev_attr_in5_type.attr,
- &iio_const_attr_in5_index.dev_attr.attr,
- &iio_scan_el_in6.dev_attr.attr, &dev_attr_in6_type.attr,
- &iio_const_attr_in6_index.dev_attr.attr,
- &iio_scan_el_in7.dev_attr.attr, &dev_attr_in7_type.attr,
- &iio_const_attr_in7_index.dev_attr.attr,
- &iio_scan_el_in8.dev_attr.attr, &dev_attr_in8_type.attr,
- &iio_const_attr_in8_index.dev_attr.attr,
- &iio_scan_el_in9.dev_attr.attr, &dev_attr_in9_type.attr,
- &iio_const_attr_in9_index.dev_attr.attr,
- &iio_scan_el_in10.dev_attr.attr, &dev_attr_in10_type.attr,
- &iio_const_attr_in10_index.dev_attr.attr,
- &iio_scan_el_in11.dev_attr.attr, &dev_attr_in11_type.attr,
- &iio_const_attr_in11_index.dev_attr.attr,
- &iio_scan_el_in0min1.dev_attr.attr, &dev_attr_in0min1_type.attr,
- &iio_const_attr_in0min1_index.dev_attr.attr,
- &iio_scan_el_in2min3.dev_attr.attr, &dev_attr_in2min3_type.attr,
- &iio_const_attr_in2min3_index.dev_attr.attr,
- &iio_scan_el_in4min5.dev_attr.attr, &dev_attr_in4min5_type.attr,
- &iio_const_attr_in4min5_index.dev_attr.attr,
- &iio_scan_el_in6min7.dev_attr.attr, &dev_attr_in6min7_type.attr,
- &iio_const_attr_in6min7_index.dev_attr.attr,
- &iio_scan_el_in8min9.dev_attr.attr, &dev_attr_in8min9_type.attr,
- &iio_const_attr_in8min9_index.dev_attr.attr,
- &iio_scan_el_in10min11.dev_attr.attr, &dev_attr_in10min11_type.attr,
- &iio_const_attr_in10min11_index.dev_attr.attr,
- &iio_scan_el_in1min0.dev_attr.attr, &dev_attr_in1min0_type.attr,
- &iio_const_attr_in1min0_index.dev_attr.attr,
- &iio_scan_el_in3min2.dev_attr.attr, &dev_attr_in3min2_type.attr,
- &iio_const_attr_in3min2_index.dev_attr.attr,
- &iio_scan_el_in5min4.dev_attr.attr, &dev_attr_in5min4_type.attr,
- &iio_const_attr_in5min4_index.dev_attr.attr,
- &iio_scan_el_in7min6.dev_attr.attr, &dev_attr_in7min6_type.attr,
- &iio_const_attr_in7min6_index.dev_attr.attr,
- &iio_scan_el_in9min8.dev_attr.attr, &dev_attr_in9min8_type.attr,
- &iio_const_attr_in9min8_index.dev_attr.attr,
- &iio_scan_el_in11min10.dev_attr.attr, &dev_attr_in11min10_type.attr,
- &iio_const_attr_in11min10_index.dev_attr.attr,
- &iio_const_attr_timestamp_index.dev_attr.attr,
- &iio_scan_el_timestamp.dev_attr.attr,
- &iio_const_attr_timestamp_type.dev_attr.attr,
- NULL,
+static struct iio_chan_spec max1138_channels[] = {
+ IIO_CHAN(IIO_IN, 0, MAX1363_INFO_MASK, _s0, 0, IIO_ST('u', 10, 16, 0)),
+ IIO_CHAN(IIO_IN, 1, MAX1363_INFO_MASK, _s0, 1, IIO_ST('u', 10, 16, 0)),
+ IIO_CHAN(IIO_IN, 2, MAX1363_INFO_MASK, _s0, 2, IIO_ST('u', 10, 16, 0)),
+ IIO_CHAN(IIO_IN, 3, MAX1363_INFO_MASK, _s0, 3, IIO_ST('u', 10, 16, 0)),
+ IIO_CHAN(IIO_IN, 4, MAX1363_INFO_MASK, _s0, 4, IIO_ST('u', 10, 16, 0)),
+ IIO_CHAN(IIO_IN, 5, MAX1363_INFO_MASK, _s0, 5, IIO_ST('u', 10, 16, 0)),
+ IIO_CHAN(IIO_IN, 6, MAX1363_INFO_MASK, _s0, 6, IIO_ST('u', 10, 16, 0)),
+ IIO_CHAN(IIO_IN, 7, MAX1363_INFO_MASK, _s0, 7, IIO_ST('u', 10, 16, 0)),
+ IIO_CHAN(IIO_IN, 8, MAX1363_INFO_MASK, _s0, 8, IIO_ST('u', 10, 16, 0)),
+ IIO_CHAN(IIO_IN, 9, MAX1363_INFO_MASK, _s0, 9, IIO_ST('u', 10, 16, 0)),
+ IIO_CHAN(IIO_IN, 10, MAX1363_INFO_MASK, _s0, 10,
+ IIO_ST('u', 10, 16, 0)),
+ IIO_CHAN(IIO_IN, 11, MAX1363_INFO_MASK, _s0, 11,
+ IIO_ST('u', 10, 16, 0)),
+ IIO_CHAN_COMPOUND(IIO_IN_DIFF, 0, 1, MAX1363_INFO_MASK,
+ d0m1, 12, IIO_ST('s', 10, 16, 0)),
+ IIO_CHAN_COMPOUND(IIO_IN_DIFF, 2, 3, MAX1363_INFO_MASK,
+ d2m3, 13, IIO_ST('s', 10, 16, 0)),
+ IIO_CHAN_COMPOUND(IIO_IN_DIFF, 4, 5, MAX1363_INFO_MASK,
+ d4m5, 14, IIO_ST('s', 10, 16, 0)),
+ IIO_CHAN_COMPOUND(IIO_IN_DIFF, 6, 7, MAX1363_INFO_MASK,
+ d6m7, 15, IIO_ST('s', 10, 16, 0)),
+ IIO_CHAN_COMPOUND(IIO_IN_DIFF, 8, 9, MAX1363_INFO_MASK,
+ d8m9, 16, IIO_ST('s', 10, 16, 0)),
+ IIO_CHAN_COMPOUND(IIO_IN_DIFF, 10, 11, MAX1363_INFO_MASK,
+ d10m11, 17, IIO_ST('s', 10, 16, 0)),
+ IIO_CHAN_COMPOUND(IIO_IN_DIFF, 1, 0, MAX1363_INFO_MASK,
+ d1m0, 18, IIO_ST('s', 10, 16, 0)),
+ IIO_CHAN_COMPOUND(IIO_IN_DIFF, 3, 2, MAX1363_INFO_MASK,
+ d3m2, 19, IIO_ST('s', 10, 16, 0)),
+ IIO_CHAN_COMPOUND(IIO_IN_DIFF, 5, 4, MAX1363_INFO_MASK,
+ d5m4, 20, IIO_ST('s', 10, 16, 0)),
+ IIO_CHAN_COMPOUND(IIO_IN_DIFF, 7, 6, MAX1363_INFO_MASK,
+ d7m6, 21, IIO_ST('s', 10, 16, 0)),
+ IIO_CHAN_COMPOUND(IIO_IN_DIFF, 9, 8, MAX1363_INFO_MASK,
+ d9m8, 22, IIO_ST('s', 10, 16, 0)),
+ IIO_CHAN_COMPOUND(IIO_IN_DIFF, 11, 10, MAX1363_INFO_MASK,
+ d11m10, 23, IIO_ST('s', 10, 16, 0)),
+ IIO_CHAN_SOFT_TIMESTAMP(24)
};

-static struct attribute_group max1238_scan_el_group = {
- .name = "scan_elements",
- .attrs = max1238_scan_el_attrs,
+static struct iio_chan_spec max1238_channels[] = {
+ IIO_CHAN(IIO_IN, 0, MAX1363_INFO_MASK, _s0, 0, IIO_ST('u', 12, 16, 0)),
+ IIO_CHAN(IIO_IN, 1, MAX1363_INFO_MASK, _s0, 1, IIO_ST('u', 12, 16, 0)),
+ IIO_CHAN(IIO_IN, 2, MAX1363_INFO_MASK, _s0, 2, IIO_ST('u', 12, 16, 0)),
+ IIO_CHAN(IIO_IN, 3, MAX1363_INFO_MASK, _s0, 3, IIO_ST('u', 12, 16, 0)),
+ IIO_CHAN(IIO_IN, 4, MAX1363_INFO_MASK, _s0, 4, IIO_ST('u', 12, 16, 0)),
+ IIO_CHAN(IIO_IN, 5, MAX1363_INFO_MASK, _s0, 5, IIO_ST('u', 12, 16, 0)),
+ IIO_CHAN(IIO_IN, 6, MAX1363_INFO_MASK, _s0, 6, IIO_ST('u', 12, 16, 0)),
+ IIO_CHAN(IIO_IN, 7, MAX1363_INFO_MASK, _s0, 7, IIO_ST('u', 12, 16, 0)),
+ IIO_CHAN(IIO_IN, 8, MAX1363_INFO_MASK, _s0, 8, IIO_ST('u', 12, 16, 0)),
+ IIO_CHAN(IIO_IN, 9, MAX1363_INFO_MASK, _s0, 9, IIO_ST('u', 12, 16, 0)),
+ IIO_CHAN(IIO_IN, 10, MAX1363_INFO_MASK, _s0,
+ 10, IIO_ST('u', 12, 16, 0)),
+ IIO_CHAN(IIO_IN, 11, MAX1363_INFO_MASK, _s0,
+ 11, IIO_ST('u', 12, 16, 0)),
+ IIO_CHAN_COMPOUND(IIO_IN_DIFF, 0, 1, MAX1363_INFO_MASK,
+ d0m1, 12, IIO_ST('s', 12, 16, 0)),
+ IIO_CHAN_COMPOUND(IIO_IN_DIFF, 2, 3, MAX1363_INFO_MASK,
+ d2m3, 13, IIO_ST('s', 12, 16, 0)),
+ IIO_CHAN_COMPOUND(IIO_IN_DIFF, 4, 5, MAX1363_INFO_MASK,
+ d4m5, 14, IIO_ST('s', 12, 16, 0)),
+ IIO_CHAN_COMPOUND(IIO_IN_DIFF, 6, 7, MAX1363_INFO_MASK,
+ d6m7, 15, IIO_ST('s', 12, 16, 0)),
+ IIO_CHAN_COMPOUND(IIO_IN_DIFF, 8, 9, MAX1363_INFO_MASK,
+ d8m9, 16, IIO_ST('s', 12, 16, 0)),
+ IIO_CHAN_COMPOUND(IIO_IN_DIFF, 10, 11, MAX1363_INFO_MASK,
+ d10m11, 17, IIO_ST('s', 12, 16, 0)),
+ IIO_CHAN_COMPOUND(IIO_IN_DIFF, 1, 0, MAX1363_INFO_MASK,
+ d1m0, 18, IIO_ST('s', 12, 16, 0)),
+ IIO_CHAN_COMPOUND(IIO_IN_DIFF, 3, 2, MAX1363_INFO_MASK,
+ d3m2, 19, IIO_ST('s', 12, 16, 0)),
+ IIO_CHAN_COMPOUND(IIO_IN_DIFF, 5, 4, MAX1363_INFO_MASK,
+ d5m4, 20, IIO_ST('s', 12, 16, 0)),
+ IIO_CHAN_COMPOUND(IIO_IN_DIFF, 7, 6, MAX1363_INFO_MASK,
+ d7m6, 21, IIO_ST('s', 12, 16, 0)),
+ IIO_CHAN_COMPOUND(IIO_IN_DIFF, 9, 8, MAX1363_INFO_MASK,
+ d9m8, 22, IIO_ST('s', 12, 16, 0)),
+ IIO_CHAN_COMPOUND(IIO_IN_DIFF, 11, 10, MAX1363_INFO_MASK,
+ d11m10, 23, IIO_ST('s', 12, 16, 0)),
+ IIO_CHAN_SOFT_TIMESTAMP(24)
};

-
static const enum max1363_modes max11607_mode_list[] = {
_s0, _s1, _s2, _s3,
s0to1, s0to2, s0to3,
@@ -547,74 +541,88 @@ static const enum max1363_modes max11608_mode_list[] = {
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 iio_chan_spec max11602_channels[] = {
+ IIO_CHAN(IIO_IN, 0, MAX1363_INFO_MASK, _s0, 0, IIO_ST('u', 8, 8, 0)),
+ IIO_CHAN(IIO_IN, 1, MAX1363_INFO_MASK, _s0, 1, IIO_ST('u', 8, 8, 0)),
+ IIO_CHAN(IIO_IN, 2, MAX1363_INFO_MASK, _s0, 2, IIO_ST('u', 8, 8, 0)),
+ IIO_CHAN(IIO_IN, 3, MAX1363_INFO_MASK, _s0, 3, IIO_ST('u', 8, 8, 0)),
+ IIO_CHAN(IIO_IN, 4, MAX1363_INFO_MASK, _s0, 4, IIO_ST('u', 8, 8, 0)),
+ IIO_CHAN(IIO_IN, 5, MAX1363_INFO_MASK, _s0, 5, IIO_ST('u', 8, 8, 0)),
+ IIO_CHAN(IIO_IN, 6, MAX1363_INFO_MASK, _s0, 6, IIO_ST('u', 8, 8, 0)),
+ IIO_CHAN(IIO_IN, 7, MAX1363_INFO_MASK, _s0, 7, IIO_ST('u', 8, 8, 0)),
+ IIO_CHAN_COMPOUND(IIO_IN_DIFF, 0, 1, MAX1363_INFO_MASK,
+ d0m1, 8, IIO_ST('s', 8, 8, 0)),
+ IIO_CHAN_COMPOUND(IIO_IN_DIFF, 2, 3, MAX1363_INFO_MASK,
+ d2m3, 9, IIO_ST('s', 8, 8, 0)),
+ IIO_CHAN_COMPOUND(IIO_IN_DIFF, 4, 5, MAX1363_INFO_MASK,
+ d4m5, 10, IIO_ST('s', 8, 8, 0)),
+ IIO_CHAN_COMPOUND(IIO_IN_DIFF, 6, 7, MAX1363_INFO_MASK,
+ d6m7, 11, IIO_ST('s', 8, 8, 0)),
+ IIO_CHAN_COMPOUND(IIO_IN_DIFF, 1, 0, MAX1363_INFO_MASK,
+ d1m0, 12, IIO_ST('s', 8, 8, 0)),
+ IIO_CHAN_COMPOUND(IIO_IN_DIFF, 3, 2, MAX1363_INFO_MASK,
+ d3m2, 13, IIO_ST('s', 8, 8, 0)),
+ IIO_CHAN_COMPOUND(IIO_IN_DIFF, 5, 4, MAX1363_INFO_MASK,
+ d5m4, 14, IIO_ST('s', 8, 8, 0)),
+ IIO_CHAN_COMPOUND(IIO_IN_DIFF, 7, 6, MAX1363_INFO_MASK,
+ d7m6, 15, IIO_ST('s', 8, 8, 0)),
+ IIO_CHAN_SOFT_TIMESTAMP(16)
};

-static struct attribute *max11608_scan_el_attrs[] = {
- &iio_scan_el_in0.dev_attr.attr, &dev_attr_in0_type.attr,
- &iio_const_attr_in0_index.dev_attr.attr,
- &iio_scan_el_in1.dev_attr.attr, &dev_attr_in1_type.attr,
- &iio_const_attr_in1_index.dev_attr.attr,
- &iio_scan_el_in2.dev_attr.attr, &dev_attr_in2_type.attr,
- &iio_const_attr_in2_index.dev_attr.attr,
- &iio_scan_el_in3.dev_attr.attr, &dev_attr_in3_type.attr,
- &iio_const_attr_in3_index.dev_attr.attr,
- &iio_scan_el_in4.dev_attr.attr, &dev_attr_in4_type.attr,
- &iio_const_attr_in4_index.dev_attr.attr,
- &iio_scan_el_in5.dev_attr.attr, &dev_attr_in5_type.attr,
- &iio_const_attr_in5_index.dev_attr.attr,
- &iio_scan_el_in6.dev_attr.attr, &dev_attr_in6_type.attr,
- &iio_const_attr_in6_index.dev_attr.attr,
- &iio_scan_el_in7.dev_attr.attr, &dev_attr_in7_type.attr,
- &iio_const_attr_in7_index.dev_attr.attr,
- &iio_scan_el_in0min1.dev_attr.attr, &dev_attr_in0min1_type.attr,
- &iio_const_attr_in0min1_index.dev_attr.attr,
- &iio_scan_el_in2min3.dev_attr.attr, &dev_attr_in2min3_type.attr,
- &iio_const_attr_in2min3_index.dev_attr.attr,
- &iio_scan_el_in4min5.dev_attr.attr, &dev_attr_in4min5_type.attr,
- &iio_const_attr_in4min5_index.dev_attr.attr,
- &iio_scan_el_in6min7.dev_attr.attr, &dev_attr_in6min7_type.attr,
- &iio_const_attr_in6min7_index.dev_attr.attr,
- &iio_scan_el_in1min0.dev_attr.attr, &dev_attr_in1min0_type.attr,
- &iio_const_attr_in1min0_index.dev_attr.attr,
- &iio_scan_el_in3min2.dev_attr.attr, &dev_attr_in3min2_type.attr,
- &iio_const_attr_in3min2_index.dev_attr.attr,
- &iio_scan_el_in5min4.dev_attr.attr, &dev_attr_in5min4_type.attr,
- &iio_const_attr_in5min4_index.dev_attr.attr,
- &iio_scan_el_in7min6.dev_attr.attr, &dev_attr_in7min6_type.attr,
- &iio_const_attr_in7min6_index.dev_attr.attr,
- &iio_const_attr_timestamp_index.dev_attr.attr,
- &iio_scan_el_timestamp.dev_attr.attr,
- &iio_const_attr_timestamp_type.dev_attr.attr,
- NULL
+static struct iio_chan_spec max11608_channels[] = {
+ IIO_CHAN(IIO_IN, 0, MAX1363_INFO_MASK, _s0, 0, IIO_ST('u', 10, 16, 0)),
+ IIO_CHAN(IIO_IN, 1, MAX1363_INFO_MASK, _s0, 1, IIO_ST('u', 10, 16, 0)),
+ IIO_CHAN(IIO_IN, 2, MAX1363_INFO_MASK, _s0, 2, IIO_ST('u', 10, 16, 0)),
+ IIO_CHAN(IIO_IN, 3, MAX1363_INFO_MASK, _s0, 3, IIO_ST('u', 10, 16, 0)),
+ IIO_CHAN(IIO_IN, 4, MAX1363_INFO_MASK, _s0, 4, IIO_ST('u', 10, 16, 0)),
+ IIO_CHAN(IIO_IN, 5, MAX1363_INFO_MASK, _s0, 5, IIO_ST('u', 10, 16, 0)),
+ IIO_CHAN(IIO_IN, 6, MAX1363_INFO_MASK, _s0, 6, IIO_ST('u', 10, 16, 0)),
+ IIO_CHAN(IIO_IN, 7, MAX1363_INFO_MASK, _s0, 7, IIO_ST('u', 10, 16, 0)),
+ IIO_CHAN_COMPOUND(IIO_IN_DIFF, 0, 1, MAX1363_INFO_MASK,
+ d0m1, 8, IIO_ST('s', 10, 16, 0)),
+ IIO_CHAN_COMPOUND(IIO_IN_DIFF, 2, 3, MAX1363_INFO_MASK,
+ d2m3, 9, IIO_ST('s', 10, 16, 0)),
+ IIO_CHAN_COMPOUND(IIO_IN_DIFF, 4, 5, MAX1363_INFO_MASK,
+ d4m5, 10, IIO_ST('s', 10, 16, 0)),
+ IIO_CHAN_COMPOUND(IIO_IN_DIFF, 6, 7, MAX1363_INFO_MASK,
+ d6m7, 11, IIO_ST('s', 10, 16, 0)),
+ IIO_CHAN_COMPOUND(IIO_IN_DIFF, 1, 0, MAX1363_INFO_MASK,
+ d1m0, 12, IIO_ST('s', 10, 16, 0)),
+ IIO_CHAN_COMPOUND(IIO_IN_DIFF, 3, 2, MAX1363_INFO_MASK,
+ d3m2, 13, IIO_ST('s', 10, 16, 0)),
+ IIO_CHAN_COMPOUND(IIO_IN_DIFF, 5, 4, MAX1363_INFO_MASK,
+ d5m4, 14, IIO_ST('s', 10, 16, 0)),
+ IIO_CHAN_COMPOUND(IIO_IN_DIFF, 7, 6, MAX1363_INFO_MASK,
+ d7m6, 15, IIO_ST('s', 10, 16, 0)),
+ IIO_CHAN_SOFT_TIMESTAMP(16)
};

-static struct attribute_group max11608_scan_el_group = {
- .name = "scan_elements",
- .attrs = max11608_scan_el_attrs,
+static struct iio_chan_spec max11614_channels[] = {
+ IIO_CHAN(IIO_IN, 0, MAX1363_INFO_MASK, _s0, 0, IIO_ST('u', 12, 16, 0)),
+ IIO_CHAN(IIO_IN, 1, MAX1363_INFO_MASK, _s0, 1, IIO_ST('u', 12, 16, 0)),
+ IIO_CHAN(IIO_IN, 2, MAX1363_INFO_MASK, _s0, 2, IIO_ST('u', 12, 16, 0)),
+ IIO_CHAN(IIO_IN, 3, MAX1363_INFO_MASK, _s0, 3, IIO_ST('u', 12, 16, 0)),
+ IIO_CHAN(IIO_IN, 4, MAX1363_INFO_MASK, _s0, 4, IIO_ST('u', 12, 16, 0)),
+ IIO_CHAN(IIO_IN, 5, MAX1363_INFO_MASK, _s0, 5, IIO_ST('u', 12, 16, 0)),
+ IIO_CHAN(IIO_IN, 6, MAX1363_INFO_MASK, _s0, 6, IIO_ST('u', 12, 16, 0)),
+ IIO_CHAN(IIO_IN, 7, MAX1363_INFO_MASK, _s0, 7, IIO_ST('u', 12, 16, 0)),
+ IIO_CHAN_COMPOUND(IIO_IN_DIFF, 0, 1, MAX1363_INFO_MASK,
+ d0m1, 8, IIO_ST('s', 12, 16, 0)),
+ IIO_CHAN_COMPOUND(IIO_IN_DIFF, 2, 3, MAX1363_INFO_MASK,
+ d2m3, 9, IIO_ST('s', 12, 16, 0)),
+ IIO_CHAN_COMPOUND(IIO_IN_DIFF, 4, 5, MAX1363_INFO_MASK,
+ d4m5, 10, IIO_ST('s', 12, 16, 0)),
+ IIO_CHAN_COMPOUND(IIO_IN_DIFF, 6, 7, MAX1363_INFO_MASK,
+ d6m7, 11, IIO_ST('s', 12, 16, 0)),
+ IIO_CHAN_COMPOUND(IIO_IN_DIFF, 1, 0, MAX1363_INFO_MASK,
+ d1m0, 12, IIO_ST('s', 12, 16, 0)),
+ IIO_CHAN_COMPOUND(IIO_IN_DIFF, 3, 2, MAX1363_INFO_MASK,
+ d3m2, 13, IIO_ST('s', 12, 16, 0)),
+ IIO_CHAN_COMPOUND(IIO_IN_DIFF, 5, 4, MAX1363_INFO_MASK,
+ d5m4, 14, IIO_ST('s', 12, 16, 0)),
+ IIO_CHAN_COMPOUND(IIO_IN_DIFF, 7, 6, MAX1363_INFO_MASK,
+ d7m6, 15, IIO_ST('s', 12, 16, 0)),
+ IIO_CHAN_SOFT_TIMESTAMP(16)
};

enum { max1361,
@@ -656,348 +664,314 @@ enum { max1361,
/* max1363 and max1368 tested - rest from data sheet */
static const struct max1363_chip_info max1363_chip_info_tbl[] = {
[max1361] = {
- .num_inputs = 4,
.bits = 10,
.int_vref_mv = 2048,
.monitor_mode = 1,
.mode_list = max1363_mode_list,
.num_modes = ARRAY_SIZE(max1363_mode_list),
.default_mode = s0to3,
- .dev_attrs = &max1363_dev_attr_group,
- .scan_attrs = &max1363_scan_el_group,
+ .channels = max1361_channels,
+ .num_channels = ARRAY_SIZE(max1361_channels),
},
[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,
+ .channels = max1361_channels,
+ .num_channels = ARRAY_SIZE(max1361_channels),
},
[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,
+ .channels = max1363_channels,
+ .num_channels = ARRAY_SIZE(max1363_channels),
},
[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,
+ .channels = max1363_channels,
+ .num_channels = ARRAY_SIZE(max1363_channels),
},
[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,
+ .channels = max1036_channels,
+ .num_channels = ARRAY_SIZE(max1036_channels),
},
[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,
+ .channels = max1036_channels,
+ .num_channels = ARRAY_SIZE(max1036_channels),
},
[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,
+ .channels = max1038_channels,
+ .num_channels = ARRAY_SIZE(max1038_channels),
},
[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,
+ .channels = max1038_channels,
+ .num_channels = ARRAY_SIZE(max1038_channels),
},
[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,
+ .channels = max1136_channels,
+ .num_channels = ARRAY_SIZE(max1136_channels),
},
[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,
+ .channels = max1136_channels,
+ .num_channels = ARRAY_SIZE(max1136_channels),
},
[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,
+ .channels = max1138_channels,
+ .num_channels = ARRAY_SIZE(max1138_channels),
},
[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,
+ .channels = max1138_channels,
+ .num_channels = ARRAY_SIZE(max1138_channels),
},
[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,
+ .channels = max1236_channels,
+ .num_channels = ARRAY_SIZE(max1236_channels),
},
[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,
+ .channels = max1236_channels,
+ .num_channels = ARRAY_SIZE(max1236_channels),
},
[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,
+ .channels = max1238_channels,
+ .num_channels = ARRAY_SIZE(max1238_channels),
},
[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,
+ .channels = max1238_channels,
+ .num_channels = ARRAY_SIZE(max1238_channels),
},
[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,
+ .channels = max1036_channels,
+ .num_channels = ARRAY_SIZE(max1036_channels),
},
[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,
+ .channels = max1036_channels,
+ .num_channels = ARRAY_SIZE(max1036_channels),
},
[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,
+ .channels = max11602_channels,
+ .num_channels = ARRAY_SIZE(max11602_channels),
},
[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,
+ .channels = max11602_channels,
+ .num_channels = ARRAY_SIZE(max11602_channels),
},
[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,
+ .channels = max1238_channels,
+ .num_channels = ARRAY_SIZE(max1238_channels),
},
[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,
+ .channels = max1238_channels,
+ .num_channels = ARRAY_SIZE(max1238_channels),
},
[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,
+ .channels = max1136_channels,
+ .num_channels = ARRAY_SIZE(max1136_channels),
},
[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,
+ .channels = max1136_channels,
+ .num_channels = ARRAY_SIZE(max1136_channels),
},
[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,
+ .channels = max11608_channels,
+ .num_channels = ARRAY_SIZE(max11608_channels),
},
[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,
+ .channels = max11608_channels,
+ .num_channels = ARRAY_SIZE(max11608_channels),
},
[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,
+ .channels = max1238_channels,
+ .num_channels = ARRAY_SIZE(max1238_channels),
},
[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,
+ .channels = max1238_channels,
+ .num_channels = ARRAY_SIZE(max1238_channels),
},
[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,
+ .channels = max1363_channels,
+ .num_channels = ARRAY_SIZE(max1363_channels),
},
[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,
+ .channels = max1363_channels,
+ .num_channels = ARRAY_SIZE(max1363_channels),
},
[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,
+ .channels = max11614_channels,
+ .num_channels = ARRAY_SIZE(max11614_channels),
},
[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,
+ .channels = max11614_channels,
+ .num_channels = ARRAY_SIZE(max11614_channels),
},
[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,
+ .channels = max1238_channels,
+ .num_channels = ARRAY_SIZE(max1238_channels),
},
[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,
+ .channels = max1238_channels,
+ .num_channels = ARRAY_SIZE(max1238_channels),
}
};

@@ -1048,51 +1022,24 @@ static IIO_DEV_ATTR_SAMP_FREQ(S_IRUGO | S_IWUSR,
static IIO_CONST_ATTR(sampling_frequency_available,
"133000 665000 33300 16600 8300 4200 2000 1000");

-static ssize_t max1363_show_thresh(struct device *dev,
- struct device_attribute *attr,
- char *buf,
- bool high)
+static int max1363_read_thresh(struct iio_dev *indio_dev,
+ int event_code,
+ int *val)
{
- struct iio_dev *indio_dev = dev_get_drvdata(dev);
struct max1363_state *st = iio_priv(indio_dev);
- struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
-
- if (high)
- return sprintf(buf, "%d\n",
- st->thresh_high[this_attr->address]);
+ if (IIO_EVENT_CODE_EXTRACT_DIR(event_code) == IIO_EV_DIR_FALLING)
+ *val = st->thresh_low[IIO_EVENT_CODE_EXTRACT_NUM(event_code)];
else
- return sprintf(buf, "%d\n",
- st->thresh_low[this_attr->address & 0x7]);
-}
-
-static ssize_t max1363_show_thresh_low(struct device *dev,
- struct device_attribute *attr,
- char *buf)
-{
- return max1363_show_thresh(dev, attr, buf, false);
-}
-
-static ssize_t max1363_show_thresh_high(struct device *dev,
- struct device_attribute *attr,
- char *buf)
-{
- return max1363_show_thresh(dev, attr, buf, true);
+ *val = st->thresh_high[IIO_EVENT_CODE_EXTRACT_NUM(event_code)];
+ return 0;
}

-static ssize_t max1363_store_thresh_unsigned(struct device *dev,
- struct device_attribute *attr,
- const char *buf,
- size_t len,
- bool high)
+static int max1363_write_thresh(struct iio_dev *indio_dev,
+ int event_code,
+ int val)
{
- struct max1363_state *st = iio_priv(dev_get_drvdata(dev));
- struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
- unsigned long val;
- int ret;
-
- ret = strict_strtoul(buf, 10, &val);
- if (ret)
- return -EINVAL;
+ struct max1363_state *st = iio_priv(indio_dev);
+ /* make it handle signed correctly as well */
switch (st->chip_info->bits) {
case 10:
if (val > 0x3FF)
@@ -1104,154 +1051,15 @@ static ssize_t max1363_store_thresh_unsigned(struct device *dev,
break;
}

- switch (high) {
- case 1:
- st->thresh_high[this_attr->address] = val;
+ switch (IIO_EVENT_CODE_EXTRACT_DIR(event_code)) {
+ case IIO_EV_DIR_FALLING:
+ st->thresh_low[IIO_EVENT_CODE_EXTRACT_NUM(event_code)] = val;
break;
- case 0:
- st->thresh_low[this_attr->address & 0x7] = val;
+ case IIO_EV_DIR_RISING:
+ st->thresh_high[IIO_EVENT_CODE_EXTRACT_NUM(event_code)] = val;
break;
}

- return len;
-}
-
-static ssize_t max1363_store_thresh_high_unsigned(struct device *dev,
- struct device_attribute *attr,
- const char *buf,
- size_t len)
-{
- return max1363_store_thresh_unsigned(dev, attr, buf, len, true);
-}
-
-static ssize_t max1363_store_thresh_low_unsigned(struct device *dev,
- struct device_attribute *attr,
- const char *buf,
- size_t len)
-{
- return max1363_store_thresh_unsigned(dev, attr, buf, len, false);
-}
-
-static ssize_t max1363_store_thresh_signed(struct device *dev,
- struct device_attribute *attr,
- const char *buf,
- size_t len,
- bool high)
-{
- struct max1363_state *st = iio_priv(dev_get_drvdata(dev));
- struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
- long val;
- int ret;
-
- ret = strict_strtol(buf, 10, &val);
- if (ret)
- return -EINVAL;
- switch (st->chip_info->bits) {
- case 10:
- if (val < -512 || val > 511)
- return -EINVAL;
- break;
- case 12:
- if (val < -2048 || val > 2047)
- return -EINVAL;
- break;
- }
-
- switch (high) {
- case 1:
- st->thresh_high[this_attr->address] = val;
- break;
- case 0:
- st->thresh_low[this_attr->address & 0x7] = val;
- break;
- }
-
- return len;
-}
-
-static ssize_t max1363_store_thresh_high_signed(struct device *dev,
- struct device_attribute *attr,
- const char *buf,
- size_t len)
-{
- return max1363_store_thresh_signed(dev, attr, buf, len, true);
-}
-
-static ssize_t max1363_store_thresh_low_signed(struct device *dev,
- struct device_attribute *attr,
- const char *buf,
- size_t len)
-{
- return max1363_store_thresh_signed(dev, attr, buf, len, false);
-}
-
-static IIO_DEVICE_ATTR(in0_thresh_high_value, S_IRUGO | S_IWUSR,
- max1363_show_thresh_high,
- max1363_store_thresh_high_unsigned, 0);
-static IIO_DEVICE_ATTR(in0_thresh_low_value, S_IRUGO | S_IWUSR,
- max1363_show_thresh_low,
- max1363_store_thresh_low_unsigned, 0);
-static IIO_DEVICE_ATTR(in1_thresh_high_value, S_IRUGO | S_IWUSR,
- max1363_show_thresh_high,
- max1363_store_thresh_high_unsigned, 1);
-static IIO_DEVICE_ATTR(in1_thresh_low_value, S_IRUGO | S_IWUSR,
- max1363_show_thresh_low,
- max1363_store_thresh_low_unsigned, 1);
-static IIO_DEVICE_ATTR(in2_thresh_high_value, S_IRUGO | S_IWUSR,
- max1363_show_thresh_high,
- max1363_store_thresh_high_unsigned, 2);
-static IIO_DEVICE_ATTR(in2_thresh_low_value, S_IRUGO | S_IWUSR,
- max1363_show_thresh_low,
- max1363_store_thresh_low_unsigned, 2);
-static IIO_DEVICE_ATTR(in3_thresh_high_value, S_IRUGO | S_IWUSR,
- max1363_show_thresh_high,
- max1363_store_thresh_high_unsigned, 3);
-static IIO_DEVICE_ATTR(in3_thresh_low_value, S_IRUGO | S_IWUSR,
- max1363_show_thresh_low,
- max1363_store_thresh_low_unsigned, 3);
-
-static IIO_DEVICE_ATTR_NAMED(in0min1_thresh_high_value,
- in0-in1_thresh_high_value,
- S_IRUGO | S_IWUSR, max1363_show_thresh_high,
- max1363_store_thresh_high_signed, 4);
-static IIO_DEVICE_ATTR_NAMED(in0min1_thresh_low_value,
- in0-in1_thresh_low_value,
- S_IRUGO | S_IWUSR, max1363_show_thresh_low,
- max1363_store_thresh_low_signed, 4);
-static IIO_DEVICE_ATTR_NAMED(in2min3_thresh_high_value,
- in2-in3_thresh_high_value,
- S_IRUGO | S_IWUSR, max1363_show_thresh_high,
- max1363_store_thresh_high_signed, 5);
-static IIO_DEVICE_ATTR_NAMED(in2min3_thresh_low_value,
- in2-in3_thresh_low_value,
- S_IRUGO | S_IWUSR, max1363_show_thresh_low,
- max1363_store_thresh_low_signed, 5);
-static IIO_DEVICE_ATTR_NAMED(in1min0_thresh_high_value,
- in1-in0_thresh_high_value,
- S_IRUGO | S_IWUSR, max1363_show_thresh_high,
- max1363_store_thresh_high_signed, 6);
-static IIO_DEVICE_ATTR_NAMED(in1min0_thresh_low_value,
- in1-in0_thresh_low_value,
- S_IRUGO | S_IWUSR, max1363_show_thresh_low,
- max1363_store_thresh_low_signed, 6);
-static IIO_DEVICE_ATTR_NAMED(in3min2_thresh_high_value,
- in3-in2_thresh_high_value,
- S_IRUGO | S_IWUSR, max1363_show_thresh_high,
- max1363_store_thresh_high_signed, 7);
-static IIO_DEVICE_ATTR_NAMED(in3min2_thresh_low_value,
- in3-in2_thresh_low_value,
- S_IRUGO | S_IWUSR, max1363_show_thresh_low,
- max1363_store_thresh_low_signed, 7);
-
-static int max1363_int_th(struct iio_dev *indio_dev,
- int index,
- s64 timestamp,
- int not_test)
-{
- struct max1363_state *st = iio_priv(indio_dev);
-
- st->last_timestamp = timestamp;
- schedule_work(&st->thresh_work);
return 0;
}

@@ -1265,6 +1073,7 @@ static void max1363_thresh_handler_bh(struct work_struct *work_s)
MAX1363_MON_INT_ENABLE | (st->monitor_speed << 1) | 0xF0 };

i2c_master_recv(st->client, &rx, 1);
+ /* todo - begging for use of for_each_set_bit */
if (rx & (1 << 0))
iio_push_event(indio_dev, 0,
IIO_EVENT_CODE_IN_LOW_THRESH(3),
@@ -1301,23 +1110,21 @@ static void max1363_thresh_handler_bh(struct work_struct *work_s)
i2c_master_send(st->client, tx, 2);
}

-static ssize_t max1363_read_interrupt_config(struct device *dev,
- struct device_attribute *attr,
- char *buf)
+static int max1363_read_event_config(struct iio_dev *indio_dev,
+ int event_code)
{
- struct iio_dev *indio_dev = dev_get_drvdata(dev);
struct max1363_state *st = iio_priv(indio_dev);
- struct iio_event_attr *this_attr = to_iio_event_attr(attr);
- int val;

+ int val;
+ int number = IIO_EVENT_CODE_EXTRACT_NUM(event_code);
mutex_lock(&indio_dev->mlock);
- if (this_attr->mask & 0x8)
- val = (1 << (this_attr->mask & 0x7)) & st->mask_low;
+ if (IIO_EVENT_CODE_EXTRACT_DIR(event_code) == IIO_EV_DIR_FALLING)
+ val = (1 << number) & st->mask_low;
else
- val = (1 << this_attr->mask) & st->mask_high;
+ val = (1 << number) & st->mask_high;
mutex_unlock(&indio_dev->mlock);

- return sprintf(buf, "%d\n", !!val);
+ return val;
}

static int max1363_monitor_mode_update(struct max1363_state *st, int enabled)
@@ -1434,6 +1241,7 @@ error_ret:
* To keep this managable we always use one of 3 scan modes.
* Scan 0...3, 0-1,2-3 and 1-0,3-2
*/
+
static inline int __max1363_check_event_mask(int thismask, int checkmask)
{
int ret = 0;
@@ -1454,206 +1262,61 @@ error_ret:
return ret;
}

-static ssize_t max1363_write_interrupt_config(struct device *dev,
- struct device_attribute *attr,
- const char *buf,
- size_t len)
+static int max1363_write_event_config(struct iio_dev *indio_dev,
+ int event_code,
+ struct iio_event_handler_list *listel,
+ int state)
{
- struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ int ret = 0;
struct max1363_state *st = iio_priv(indio_dev);
- struct iio_event_attr *this_attr = to_iio_event_attr(attr);
- unsigned long val;
- int ret;
u16 unifiedmask;
- ret = strict_strtoul(buf, 10, &val);
- if (ret)
- return -EINVAL;
+ int number = IIO_EVENT_CODE_EXTRACT_NUM(event_code);
+
mutex_lock(&indio_dev->mlock);
unifiedmask = st->mask_low | st->mask_high;
- if (this_attr->mask & 0x08) {
- /* If we are disabling no need to test */
- if (val == 0)
- st->mask_low &= ~(1 << (this_attr->mask & 0x7));
+ if (IIO_EVENT_CODE_EXTRACT_DIR(event_code) == IIO_EV_DIR_FALLING) {
+
+ if (state == 0)
+ st->mask_low &= ~(1 << number);
else {
- ret = __max1363_check_event_mask(this_attr->mask & 0x7,
- unifiedmask);
+ ret = __max1363_check_event_mask((1 << number),
+ unifiedmask);
if (ret)
goto error_ret;
- st->mask_low |= (1 << (this_attr->mask & 0x7));
+ st->mask_low |= (1 << number);
}
} else {
- if (val == 0)
- st->mask_high &= ~(1 << (this_attr->mask));
+ if (state == 0)
+ st->mask_high &= ~(1 << number);
else {
- ret = __max1363_check_event_mask(this_attr->mask,
- unifiedmask);
+ ret = __max1363_check_event_mask((1 << number),
+ unifiedmask);
if (ret)
goto error_ret;
- st->mask_high |= (1 << this_attr->mask);
+ st->mask_high |= (1 << number);
}
}
if (st->monitor_on && !st->mask_high && !st->mask_low)
- iio_remove_event_from_list(this_attr->listel,
- &indio_dev->interrupts[0]->ev_list);
- if (!st->monitor_on && val)
- iio_add_event_to_list(this_attr->listel,
- &indio_dev->interrupts[0]->ev_list);
+ iio_remove_event_from_list(listel,
+ &indio_dev->interrupts[0]->ev_list);
+ if (!st->monitor_on && state)
+ iio_add_event_to_list(listel,
+ &indio_dev->interrupts[0]->ev_list);

max1363_monitor_mode_update(st, !!(st->mask_high | st->mask_low));
error_ret:
mutex_unlock(&indio_dev->mlock);

- return len;
+ return ret;
}

-IIO_EVENT_SH(max1363_thresh, max1363_int_th);
-
-#define MAX1363_HIGH_THRESH(a) a
-#define MAX1363_LOW_THRESH(a) (a | 0x8)
-
-IIO_EVENT_ATTR_SH(in0_thresh_high_en,
- iio_event_max1363_thresh,
- max1363_read_interrupt_config,
- max1363_write_interrupt_config,
- MAX1363_HIGH_THRESH(0));
-
-IIO_EVENT_ATTR_SH(in0_thresh_low_en,
- iio_event_max1363_thresh,
- max1363_read_interrupt_config,
- max1363_write_interrupt_config,
- MAX1363_LOW_THRESH(0));
-
-IIO_EVENT_ATTR_SH(in1_thresh_high_en,
- iio_event_max1363_thresh,
- max1363_read_interrupt_config,
- max1363_write_interrupt_config,
- MAX1363_HIGH_THRESH(1));
-
-IIO_EVENT_ATTR_SH(in1_thresh_low_en,
- iio_event_max1363_thresh,
- max1363_read_interrupt_config,
- max1363_write_interrupt_config,
- MAX1363_LOW_THRESH(1));
-
-IIO_EVENT_ATTR_SH(in2_thresh_high_en,
- iio_event_max1363_thresh,
- max1363_read_interrupt_config,
- max1363_write_interrupt_config,
- MAX1363_HIGH_THRESH(2));
-
-IIO_EVENT_ATTR_SH(in2_thresh_low_en,
- iio_event_max1363_thresh,
- max1363_read_interrupt_config,
- max1363_write_interrupt_config,
- MAX1363_LOW_THRESH(2));
-
-IIO_EVENT_ATTR_SH(in3_thresh_high_en,
- iio_event_max1363_thresh,
- max1363_read_interrupt_config,
- max1363_write_interrupt_config,
- MAX1363_HIGH_THRESH(3));
-
-IIO_EVENT_ATTR_SH(in3_thresh_low_en,
- iio_event_max1363_thresh,
- max1363_read_interrupt_config,
- max1363_write_interrupt_config,
- MAX1363_LOW_THRESH(3));
-
-IIO_EVENT_ATTR_NAMED_SH(in0min1_thresh_high_en,
- in0-in1_thresh_high_en,
- iio_event_max1363_thresh,
- max1363_read_interrupt_config,
- max1363_write_interrupt_config,
- MAX1363_HIGH_THRESH(4));
-
-IIO_EVENT_ATTR_NAMED_SH(in0min1_thresh_low_en,
- in0-in1_thresh_low_en,
- iio_event_max1363_thresh,
- max1363_read_interrupt_config,
- max1363_write_interrupt_config,
- MAX1363_LOW_THRESH(4));
-
-IIO_EVENT_ATTR_NAMED_SH(in3min2_thresh_high_en,
- in3-in2_thresh_high_en,
- iio_event_max1363_thresh,
- max1363_read_interrupt_config,
- max1363_write_interrupt_config,
- MAX1363_HIGH_THRESH(5));
-
-IIO_EVENT_ATTR_NAMED_SH(in3min2_thresh_low_en,
- in3-in2_thresh_low_en,
- iio_event_max1363_thresh,
- max1363_read_interrupt_config,
- max1363_write_interrupt_config,
- MAX1363_LOW_THRESH(5));
-
-IIO_EVENT_ATTR_NAMED_SH(in1min0_thresh_high_en,
- in1-in0_thresh_high_en,
- iio_event_max1363_thresh,
- max1363_read_interrupt_config,
- max1363_write_interrupt_config,
- MAX1363_HIGH_THRESH(6));
-
-IIO_EVENT_ATTR_NAMED_SH(in1min0_thresh_low_en,
- in1-in0_thresh_low_en,
- iio_event_max1363_thresh,
- max1363_read_interrupt_config,
- max1363_write_interrupt_config,
- MAX1363_LOW_THRESH(6));
-
-IIO_EVENT_ATTR_NAMED_SH(in2min3_thresh_high_en,
- in2-in3_thresh_high_en,
- iio_event_max1363_thresh,
- max1363_read_interrupt_config,
- max1363_write_interrupt_config,
- MAX1363_HIGH_THRESH(7));
-
-IIO_EVENT_ATTR_NAMED_SH(in2min3_thresh_low_en,
- in2-in3_thresh_low_en,
- iio_event_max1363_thresh,
- max1363_read_interrupt_config,
- max1363_write_interrupt_config,
- MAX1363_LOW_THRESH(7));
-
/*
* As with scan_elements, only certain sets of these can
* be combined.
*/
static struct attribute *max1363_event_attributes[] = {
- &iio_dev_attr_in0_thresh_high_value.dev_attr.attr,
- &iio_dev_attr_in0_thresh_low_value.dev_attr.attr,
- &iio_dev_attr_in1_thresh_high_value.dev_attr.attr,
- &iio_dev_attr_in1_thresh_low_value.dev_attr.attr,
- &iio_dev_attr_in2_thresh_high_value.dev_attr.attr,
- &iio_dev_attr_in2_thresh_low_value.dev_attr.attr,
- &iio_dev_attr_in3_thresh_high_value.dev_attr.attr,
- &iio_dev_attr_in3_thresh_low_value.dev_attr.attr,
- &iio_dev_attr_in0min1_thresh_high_value.dev_attr.attr,
- &iio_dev_attr_in0min1_thresh_low_value.dev_attr.attr,
- &iio_dev_attr_in2min3_thresh_high_value.dev_attr.attr,
- &iio_dev_attr_in2min3_thresh_low_value.dev_attr.attr,
- &iio_dev_attr_in1min0_thresh_high_value.dev_attr.attr,
- &iio_dev_attr_in1min0_thresh_low_value.dev_attr.attr,
- &iio_dev_attr_in3min2_thresh_high_value.dev_attr.attr,
- &iio_dev_attr_in3min2_thresh_low_value.dev_attr.attr,
&iio_dev_attr_sampling_frequency.dev_attr.attr,
&iio_const_attr_sampling_frequency_available.dev_attr.attr,
- &iio_event_attr_in0_thresh_high_en.dev_attr.attr,
- &iio_event_attr_in0_thresh_low_en.dev_attr.attr,
- &iio_event_attr_in1_thresh_high_en.dev_attr.attr,
- &iio_event_attr_in1_thresh_low_en.dev_attr.attr,
- &iio_event_attr_in2_thresh_high_en.dev_attr.attr,
- &iio_event_attr_in2_thresh_low_en.dev_attr.attr,
- &iio_event_attr_in3_thresh_high_en.dev_attr.attr,
- &iio_event_attr_in3_thresh_low_en.dev_attr.attr,
- &iio_event_attr_in0min1_thresh_high_en.dev_attr.attr,
- &iio_event_attr_in0min1_thresh_low_en.dev_attr.attr,
- &iio_event_attr_in3min2_thresh_high_en.dev_attr.attr,
- &iio_event_attr_in3min2_thresh_low_en.dev_attr.attr,
- &iio_event_attr_in1min0_thresh_high_en.dev_attr.attr,
- &iio_event_attr_in1min0_thresh_low_en.dev_attr.attr,
- &iio_event_attr_in2min3_thresh_high_en.dev_attr.attr,
- &iio_event_attr_in2min3_thresh_low_en.dev_attr.attr,
NULL,
};

@@ -1721,8 +1384,14 @@ static int __devinit max1363_probe(struct i2c_client *client,
.modemask;
/* Estabilish that the iio_dev is a child of the i2c device */
indio_dev->dev.parent = &client->dev;
- indio_dev->attrs = st->chip_info->dev_attrs;
-
+ indio_dev->attrs = &max1363_dev_attr_group;
+ indio_dev->read_event_value = &max1363_read_thresh;
+ indio_dev->write_event_value = &max1363_write_thresh;
+ indio_dev->read_event_config = &max1363_read_event_config;
+ indio_dev->write_event_config = &max1363_write_event_config;
+ indio_dev->channels = st->chip_info->channels;
+ indio_dev->num_channels = st->chip_info->num_channels;
+ indio_dev->read_raw = &max1363_read_raw;
/* Todo: this shouldn't be here. */
indio_dev->driver_module = THIS_MODULE;
indio_dev->modes = INDIO_DIRECT_MODE;
@@ -1744,7 +1413,9 @@ static int __devinit max1363_probe(struct i2c_client *client,
if (ret)
goto error_cleanup_ring;
regdone = 1;
- ret = iio_ring_buffer_register(indio_dev->ring, 0);
+ ret = iio_ring_buffer_register_ex(indio_dev->ring, 0,
+ st->chip_info->channels,
+ st->chip_info->num_channels);
if (ret)
goto error_cleanup_ring;

@@ -1754,9 +1425,9 @@ static int __devinit max1363_probe(struct i2c_client *client,
0,
IRQF_TRIGGER_RISING,
client->name);
+
if (ret)
goto error_uninit_ring;
-
INIT_WORK(&st->thresh_work, max1363_thresh_handler_bh);
}

diff --git a/drivers/staging/iio/adc/max1363_ring.c b/drivers/staging/iio/adc/max1363_ring.c
index 944ff7a..caebe44 100644
--- a/drivers/staging/iio/adc/max1363_ring.c
+++ b/drivers/staging/iio/adc/max1363_ring.c
@@ -199,7 +199,6 @@ int max1363_register_ring_funcs_and_init(struct iio_dev *indio_dev)
goto error_deallocate_sw_rb;

/* Ring buffer functions - here trigger setup related */
- indio_dev->ring->scan_el_attrs = st->chip_info->scan_attrs;
indio_dev->ring->postenable = &iio_triggered_ring_postenable;
indio_dev->ring->preenable = &max1363_ring_preenable;
indio_dev->ring->predisable = &iio_triggered_ring_predisable;
--
1.7.3.4

2011-03-31 14:56:53

by Jonathan Cameron

[permalink] [raw]
Subject: [PATCH 08/21] staging:iio:lis3l02dq: General cleanup

As Arnd observed, things are clearner if we pass iio_dev into read and write fucntions.

Now uses st for lis3l02dq_state everywhere.

Other bits of trivial tidying.

Signed-off-by: Jonathan Cameron <[email protected]>
---
drivers/staging/iio/accel/lis3l02dq.h | 4 +-
drivers/staging/iio/accel/lis3l02dq_core.c | 116 ++++++++++++----------------
drivers/staging/iio/accel/lis3l02dq_ring.c | 64 ++++++++-------
3 files changed, 84 insertions(+), 100 deletions(-)

diff --git a/drivers/staging/iio/accel/lis3l02dq.h b/drivers/staging/iio/accel/lis3l02dq.h
index 1d90b7d..5925405 100644
--- a/drivers/staging/iio/accel/lis3l02dq.h
+++ b/drivers/staging/iio/accel/lis3l02dq.h
@@ -173,11 +173,11 @@ struct lis3l02dq_state {
#define lis3l02dq_h_to_s(_h) \
container_of(_h, struct lis3l02dq_state, help)

-int lis3l02dq_spi_read_reg_8(struct device *dev,
+int lis3l02dq_spi_read_reg_8(struct iio_dev *indio_dev,
u8 reg_address,
u8 *val);

-int lis3l02dq_spi_write_reg_8(struct device *dev,
+int lis3l02dq_spi_write_reg_8(struct iio_dev *indio_dev,
u8 reg_address,
u8 *val);

diff --git a/drivers/staging/iio/accel/lis3l02dq_core.c b/drivers/staging/iio/accel/lis3l02dq_core.c
index 5d22116..42f4d9b 100644
--- a/drivers/staging/iio/accel/lis3l02dq_core.c
+++ b/drivers/staging/iio/accel/lis3l02dq_core.c
@@ -39,9 +39,17 @@
* This means that use cannot be made of spi_write etc.
*/

-static int __lis3l02dq_spi_read_reg_8(struct lis3l02dq_state *st,
- u8 reg_address, u8 *val)
+/**
+ * lis3l02dq_spi_read_reg_8() - read single byte from a single register
+ * @indio_dev: iio_dev for this actual device
+ * @reg_address: the address of the register to be read
+ * @val: pass back the resulting value
+ **/
+int lis3l02dq_spi_read_reg_8(struct iio_dev *indio_dev,
+ u8 reg_address, u8 *val)
{
+ struct iio_sw_ring_helper_state *h = iio_dev_get_devdata(indio_dev);
+ struct lis3l02dq_state *st = lis3l02dq_h_to_s(h);
struct spi_message msg;
int ret;
struct spi_transfer xfer = {
@@ -49,7 +57,6 @@ static int __lis3l02dq_spi_read_reg_8(struct lis3l02dq_state *st,
.rx_buf = st->rx,
.bits_per_word = 8,
.len = 2,
- .cs_change = 1,
};

mutex_lock(&st->buf_lock);
@@ -64,50 +71,26 @@ static int __lis3l02dq_spi_read_reg_8(struct lis3l02dq_state *st,

return ret;
}
-/**
- * lis3l02dq_spi_read_reg_8() - read single byte from a single register
- * @dev: device asosciated with child of actual device (iio_dev or iio_trig)
- * @reg_address: the address of the register to be read
- * @val: pass back the resulting value
- **/
-int lis3l02dq_spi_read_reg_8(struct device *dev, u8 reg_address, u8 *val)
-{
- struct iio_dev *indio_dev = dev_get_drvdata(dev);
- struct iio_sw_ring_helper_state *h = iio_dev_get_devdata(indio_dev);
- struct lis3l02dq_state *st = lis3l02dq_h_to_s(h);
- return __lis3l02dq_spi_read_reg_8(st, reg_address, val);
-}

/**
* lis3l02dq_spi_write_reg_8() - write single byte to a register
- * @dev: device associated with child of actual device (iio_dev or iio_trig)
+ * @indio_dev: iio_dev for this device
* @reg_address: the address of the register to be writen
* @val: the value to write
**/
-int lis3l02dq_spi_write_reg_8(struct device *dev,
+int lis3l02dq_spi_write_reg_8(struct iio_dev *indio_dev,
u8 reg_address,
u8 *val)
{
int ret;
- struct spi_message msg;
- struct iio_dev *indio_dev = dev_get_drvdata(dev);
struct iio_sw_ring_helper_state *h
= iio_dev_get_devdata(indio_dev);
struct lis3l02dq_state *st = lis3l02dq_h_to_s(h);
- struct spi_transfer xfer = {
- .tx_buf = st->tx,
- .bits_per_word = 8,
- .len = 2,
- .cs_change = 1,
- };

mutex_lock(&st->buf_lock);
st->tx[0] = LIS3L02DQ_WRITE_REG(reg_address);
st->tx[1] = *val;
-
- spi_message_init(&msg);
- spi_message_add_tail(&xfer, &msg);
- ret = spi_sync(st->us, &msg);
+ ret = spi_write(st->us, st->tx, 2);
mutex_unlock(&st->buf_lock);

return ret;
@@ -115,18 +98,17 @@ int lis3l02dq_spi_write_reg_8(struct device *dev,

/**
* lisl302dq_spi_write_reg_s16() - write 2 bytes to a pair of registers
- * @dev: device associated with child of actual device (iio_dev or iio_trig)
- * @reg_address: the address of the lower of the two registers. Second register
- * is assumed to have address one greater.
- * @val: value to be written
+ * @indio_dev: iio_dev for this device
+ * @lower_reg_address: the address of the lower of the two registers.
+ * Second register is assumed to have address one greater.
+ * @value: value to be written
**/
-static int lis3l02dq_spi_write_reg_s16(struct device *dev,
+static int lis3l02dq_spi_write_reg_s16(struct iio_dev *indio_dev,
u8 lower_reg_address,
s16 value)
{
int ret;
struct spi_message msg;
- struct iio_dev *indio_dev = dev_get_drvdata(dev);
struct iio_sw_ring_helper_state *h
= iio_dev_get_devdata(indio_dev);
struct lis3l02dq_state *st = lis3l02dq_h_to_s(h);
@@ -139,7 +121,6 @@ static int lis3l02dq_spi_write_reg_s16(struct device *dev,
.tx_buf = st->tx + 2,
.bits_per_word = 8,
.len = 2,
- .cs_change = 1,
},
};

@@ -158,10 +139,14 @@ static int lis3l02dq_spi_write_reg_s16(struct device *dev,
return ret;
}

-static int lis3l02dq_read_16bit_s(struct lis3l02dq_state *st,
+static int lis3l02dq_read_reg_s16(struct iio_dev *indio_dev,
u8 lower_reg_address,
int *val)
{
+ struct iio_sw_ring_helper_state *h
+ = iio_dev_get_devdata(indio_dev);
+ struct lis3l02dq_state *st = lis3l02dq_h_to_s(h);
+
struct spi_message msg;
int ret;
s16 tempval;
@@ -176,7 +161,6 @@ static int lis3l02dq_read_16bit_s(struct lis3l02dq_state *st,
.rx_buf = st->rx + 2,
.bits_per_word = 8,
.len = 2,
- .cs_change = 0,
},
};

@@ -224,11 +208,7 @@ static int lis3l02dq_read_thresh(struct iio_dev *indio_dev,
int e,
int *val)
{
- struct iio_sw_ring_helper_state *h
- = iio_dev_get_devdata(indio_dev);
- struct lis3l02dq_state *st = lis3l02dq_h_to_s(h);
-
- return lis3l02dq_read_16bit_s(st, LIS3L02DQ_REG_THS_L_ADDR, val);
+ return lis3l02dq_read_reg_s16(indio_dev, LIS3L02DQ_REG_THS_L_ADDR, val);
}

static int lis3l02dq_write_thresh(struct iio_dev *indio_dev,
@@ -236,7 +216,7 @@ static int lis3l02dq_write_thresh(struct iio_dev *indio_dev,
int val)
{
u16 value = val;
- return lis3l02dq_spi_write_reg_s16(&indio_dev->dev,
+ return lis3l02dq_spi_write_reg_s16(indio_dev,
LIS3L02DQ_REG_THS_L_ADDR,
value);
}
@@ -249,10 +229,8 @@ static int lis3l02dq_read_raw(struct iio_dev *indio_dev,
u8 utemp;
s8 stemp;
ssize_t ret = 0;
- struct iio_sw_ring_helper_state *h
- = iio_dev_get_devdata(indio_dev);
- struct lis3l02dq_state *st = lis3l02dq_h_to_s(h);
u8 reg;
+
switch (mask) {
case 0:
/* Take the iio_dev status lock */
@@ -273,7 +251,7 @@ static int lis3l02dq_read_raw(struct iio_dev *indio_dev,
break;
case (1 << IIO_CHAN_INFO_CALIBSCALE_SEPARATE):
reg = lis3l02dq_axis_map[LIS3L02DQ_GAIN][chan->address];
- ret = __lis3l02dq_spi_read_reg_8(st, reg, &utemp);
+ ret = lis3l02dq_spi_read_reg_8(indio_dev, reg, &utemp);
if (ret)
goto error_ret;
/* to match with what previous code does */
@@ -282,7 +260,7 @@ static int lis3l02dq_read_raw(struct iio_dev *indio_dev,

case (1 << IIO_CHAN_INFO_CALIBBIAS_SEPARATE):
reg = lis3l02dq_axis_map[LIS3L02DQ_BIAS][chan->address];
- ret = __lis3l02dq_spi_read_reg_8(st, reg, (u8 *)&stemp);
+ ret = lis3l02dq_spi_read_reg_8(indio_dev, reg, (u8 *)&stemp);
/* to match with what previous code does */
*val = 1000000*stemp;
break;
@@ -296,9 +274,10 @@ static ssize_t lis3l02dq_read_frequency(struct device *dev,
struct device_attribute *attr,
char *buf)
{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
int ret, len = 0;
s8 t;
- ret = lis3l02dq_spi_read_reg_8(dev,
+ ret = lis3l02dq_spi_read_reg_8(indio_dev,
LIS3L02DQ_REG_CTRL_1_ADDR,
(u8 *)&t);
if (ret)
@@ -336,7 +315,7 @@ static ssize_t lis3l02dq_write_frequency(struct device *dev,
return ret;

mutex_lock(&indio_dev->mlock);
- ret = lis3l02dq_spi_read_reg_8(dev,
+ ret = lis3l02dq_spi_read_reg_8(indio_dev,
LIS3L02DQ_REG_CTRL_1_ADDR,
&t);
if (ret)
@@ -361,7 +340,7 @@ static ssize_t lis3l02dq_write_frequency(struct device *dev,
goto error_ret_mutex;
};

- ret = lis3l02dq_spi_write_reg_8(dev,
+ ret = lis3l02dq_spi_write_reg_8(indio_dev,
LIS3L02DQ_REG_CTRL_1_ADDR,
&t);

@@ -382,7 +361,7 @@ static int lis3l02dq_initial_setup(struct lis3l02dq_state *st)

val = LIS3L02DQ_DEFAULT_CTRL1;
/* Write suitable defaults to ctrl1 */
- ret = lis3l02dq_spi_write_reg_8(&st->help.indio_dev->dev,
+ ret = lis3l02dq_spi_write_reg_8(st->help.indio_dev,
LIS3L02DQ_REG_CTRL_1_ADDR,
&val);
if (ret) {
@@ -390,7 +369,7 @@ static int lis3l02dq_initial_setup(struct lis3l02dq_state *st)
goto err_ret;
}
/* Repeat as sometimes doesn't work first time?*/
- ret = lis3l02dq_spi_write_reg_8(&st->help.indio_dev->dev,
+ ret = lis3l02dq_spi_write_reg_8(st->help.indio_dev,
LIS3L02DQ_REG_CTRL_1_ADDR,
&val);
if (ret) {
@@ -400,17 +379,18 @@ static int lis3l02dq_initial_setup(struct lis3l02dq_state *st)

/* Read back to check this has worked acts as loose test of correct
* chip */
- ret = lis3l02dq_spi_read_reg_8(&st->help.indio_dev->dev,
+ ret = lis3l02dq_spi_read_reg_8(st->help.indio_dev,
LIS3L02DQ_REG_CTRL_1_ADDR,
&valtest);
if (ret || (valtest != val)) {
- dev_err(&st->help.indio_dev->dev, "device not playing ball");
+ dev_err(&st->help.indio_dev->dev,
+ "device not playing ball %d %d\n", valtest, val);
ret = -EINVAL;
goto err_ret;
}

val = LIS3L02DQ_DEFAULT_CTRL2;
- ret = lis3l02dq_spi_write_reg_8(&st->help.indio_dev->dev,
+ ret = lis3l02dq_spi_write_reg_8(st->help.indio_dev,
LIS3L02DQ_REG_CTRL_2_ADDR,
&val);
if (ret) {
@@ -419,7 +399,7 @@ static int lis3l02dq_initial_setup(struct lis3l02dq_state *st)
}

val = LIS3L02DQ_REG_WAKE_UP_CFG_LATCH_SRC;
- ret = lis3l02dq_spi_write_reg_8(&st->help.indio_dev->dev,
+ ret = lis3l02dq_spi_write_reg_8(st->help.indio_dev,
LIS3L02DQ_REG_WAKE_UP_CFG_ADDR,
&val);
if (ret)
@@ -486,7 +466,7 @@ static ssize_t lis3l02dq_read_event_config(struct iio_dev *indio_dev,
u8 mask = (1 << (IIO_EVENT_CODE_EXTRACT_MODIFIER(event_code)*2 +
(IIO_EVENT_CODE_EXTRACT_DIR(event_code) ==
IIO_EV_DIR_RISING)));
- ret = lis3l02dq_spi_read_reg_8(&indio_dev->dev,
+ ret = lis3l02dq_spi_read_reg_8(indio_dev,
LIS3L02DQ_REG_WAKE_UP_CFG_ADDR,
&val);
if (ret < 0)
@@ -510,12 +490,12 @@ static int lis3l02dq_write_event_config(struct iio_dev *indio_dev,

mutex_lock(&indio_dev->mlock);
/* read current control */
- ret = lis3l02dq_spi_read_reg_8(&indio_dev->dev,
+ ret = lis3l02dq_spi_read_reg_8(indio_dev,
LIS3L02DQ_REG_CTRL_2_ADDR,
&control);
if (ret)
goto error_ret;
- ret = lis3l02dq_spi_read_reg_8(&indio_dev->dev,
+ ret = lis3l02dq_spi_read_reg_8(indio_dev,
LIS3L02DQ_REG_WAKE_UP_CFG_ADDR,
&val);
if (ret < 0)
@@ -535,7 +515,7 @@ static int lis3l02dq_write_event_config(struct iio_dev *indio_dev,
&indio_dev->interrupts[0]->ev_list);
}
if (changed) {
- ret = lis3l02dq_spi_write_reg_8(&indio_dev->dev,
+ ret = lis3l02dq_spi_write_reg_8(indio_dev,
LIS3L02DQ_REG_WAKE_UP_CFG_ADDR,
&val);
if (ret)
@@ -543,7 +523,7 @@ static int lis3l02dq_write_event_config(struct iio_dev *indio_dev,
control = list_el->refcount ?
(control | LIS3L02DQ_REG_CTRL_2_ENABLE_INTERRUPT) :
(control & ~LIS3L02DQ_REG_CTRL_2_ENABLE_INTERRUPT);
- ret = lis3l02dq_spi_write_reg_8(&indio_dev->dev,
+ ret = lis3l02dq_spi_write_reg_8(indio_dev,
LIS3L02DQ_REG_CTRL_2_ADDR,
&control);
}
@@ -564,7 +544,7 @@ static void lis3l02dq_thresh_handler_bh_no_check(struct work_struct *work_s)

u8 t;

- lis3l02dq_spi_read_reg_8(&st->help.indio_dev->dev,
+ lis3l02dq_spi_read_reg_8(st->help.indio_dev,
LIS3L02DQ_REG_WAKE_UP_SRC_ADDR,
&t);

@@ -624,7 +604,7 @@ static void lis3l02dq_thresh_handler_bh_no_check(struct work_struct *work_s)
/* reenable the irq */
enable_irq(st->us->irq);
/* Ack and allow for new interrupts */
- lis3l02dq_spi_read_reg_8(&st->help.indio_dev->dev,
+ lis3l02dq_spi_read_reg_8(st->help.indio_dev,
LIS3L02DQ_REG_WAKE_UP_ACK_ADDR,
&t);

@@ -763,7 +743,7 @@ static int lis3l02dq_stop_device(struct iio_dev *indio_dev)
u8 val = 0;

mutex_lock(&indio_dev->mlock);
- ret = lis3l02dq_spi_write_reg_8(&indio_dev->dev,
+ ret = lis3l02dq_spi_write_reg_8(indio_dev,
LIS3L02DQ_REG_CTRL_1_ADDR,
&val);
if (ret) {
@@ -771,7 +751,7 @@ static int lis3l02dq_stop_device(struct iio_dev *indio_dev)
goto err_ret;
}

- ret = lis3l02dq_spi_write_reg_8(&indio_dev->dev,
+ ret = lis3l02dq_spi_write_reg_8(indio_dev,
LIS3L02DQ_REG_CTRL_2_ADDR,
&val);
if (ret)
diff --git a/drivers/staging/iio/accel/lis3l02dq_ring.c b/drivers/staging/iio/accel/lis3l02dq_ring.c
index c6c4762..1a4b3fc 100644
--- a/drivers/staging/iio/accel/lis3l02dq_ring.c
+++ b/drivers/staging/iio/accel/lis3l02dq_ring.c
@@ -206,7 +206,7 @@ __lis3l02dq_write_data_ready_config(struct device *dev,
struct iio_dev *indio_dev = dev_get_drvdata(dev);

/* Get the current event mask register */
- ret = lis3l02dq_spi_read_reg_8(dev,
+ ret = lis3l02dq_spi_read_reg_8(indio_dev,
LIS3L02DQ_REG_CTRL_2_ADDR,
&valold);
if (ret)
@@ -220,12 +220,12 @@ __lis3l02dq_write_data_ready_config(struct device *dev,

valold &= ~LIS3L02DQ_REG_CTRL_2_ENABLE_DATA_READY_GENERATION;
/* The double write is to overcome a hardware bug?*/
- ret = lis3l02dq_spi_write_reg_8(dev,
+ ret = lis3l02dq_spi_write_reg_8(indio_dev,
LIS3L02DQ_REG_CTRL_2_ADDR,
&valold);
if (ret)
goto error_ret;
- ret = lis3l02dq_spi_write_reg_8(dev,
+ ret = lis3l02dq_spi_write_reg_8(indio_dev,
LIS3L02DQ_REG_CTRL_2_ADDR,
&valold);
if (ret)
@@ -240,7 +240,7 @@ __lis3l02dq_write_data_ready_config(struct device *dev,
/* if not set, enable requested */
valold |= LIS3L02DQ_REG_CTRL_2_ENABLE_DATA_READY_GENERATION;
iio_add_event_to_list(list, &indio_dev->interrupts[0]->ev_list);
- ret = lis3l02dq_spi_write_reg_8(dev,
+ ret = lis3l02dq_spi_write_reg_8(indio_dev,
LIS3L02DQ_REG_CTRL_2_ADDR,
&valold);
if (ret)
@@ -268,14 +268,14 @@ static int lis3l02dq_data_rdy_trigger_set_state(struct iio_trigger *trig,
__lis3l02dq_write_data_ready_config(&st->help.indio_dev->dev,
&iio_event_data_rdy_trig,
state);
- if (state == false) {
+ if (st == false) {
/* possible quirk with handler currently worked around
by ensuring the work queue is empty */
flush_scheduled_work();
/* Clear any outstanding ready events */
ret = lis3l02dq_read_all(st, NULL);
}
- lis3l02dq_spi_read_reg_8(&st->help.indio_dev->dev,
+ lis3l02dq_spi_read_reg_8(st->help.indio_dev,
LIS3L02DQ_REG_WAKE_UP_SRC_ADDR,
&t);
return ret;
@@ -328,47 +328,51 @@ static int lis3l02dq_trig_try_reen(struct iio_trigger *trig)
int lis3l02dq_probe_trigger(struct iio_dev *indio_dev)
{
int ret;
- struct lis3l02dq_state *state = indio_dev->dev_data;
+ struct iio_sw_ring_helper_state *h
+ = iio_dev_get_devdata(indio_dev);
+ struct lis3l02dq_state *st = lis3l02dq_h_to_s(h);

- state->trig = iio_allocate_trigger();
- if (!state->trig)
+ st->trig = iio_allocate_trigger();
+ if (!st->trig)
return -ENOMEM;

- state->trig->name = kasprintf(GFP_KERNEL,
- "lis3l02dq-dev%d",
- indio_dev->id);
- if (!state->trig->name) {
+ st->trig->name = kasprintf(GFP_KERNEL,
+ "lis3l02dq-dev%d",
+ indio_dev->id);
+ if (!st->trig->name) {
ret = -ENOMEM;
goto error_free_trig;
}

- state->trig->dev.parent = &state->us->dev;
- state->trig->owner = THIS_MODULE;
- state->trig->private_data = state;
- state->trig->set_trigger_state = &lis3l02dq_data_rdy_trigger_set_state;
- state->trig->try_reenable = &lis3l02dq_trig_try_reen;
- state->trig->control_attrs = &lis3l02dq_trigger_attr_group;
- ret = iio_trigger_register(state->trig);
+ st->trig->dev.parent = &st->us->dev;
+ st->trig->owner = THIS_MODULE;
+ st->trig->private_data = st;
+ st->trig->set_trigger_state = &lis3l02dq_data_rdy_trigger_set_state;
+ st->trig->try_reenable = &lis3l02dq_trig_try_reen;
+ st->trig->control_attrs = &lis3l02dq_trigger_attr_group;
+ ret = iio_trigger_register(st->trig);
if (ret)
goto error_free_trig_name;

return 0;

error_free_trig_name:
- kfree(state->trig->name);
+ kfree(st->trig->name);
error_free_trig:
- iio_free_trigger(state->trig);
+ iio_free_trigger(st->trig);

return ret;
}

void lis3l02dq_remove_trigger(struct iio_dev *indio_dev)
{
- struct lis3l02dq_state *state = indio_dev->dev_data;
+ struct iio_sw_ring_helper_state *h
+ = iio_dev_get_devdata(indio_dev);
+ struct lis3l02dq_state *st = lis3l02dq_h_to_s(h);

- iio_trigger_unregister(state->trig);
- kfree(state->trig->name);
- iio_free_trigger(state->trig);
+ iio_trigger_unregister(st->trig);
+ kfree(st->trig->name);
+ iio_free_trigger(st->trig);
}

void lis3l02dq_unconfigure_ring(struct iio_dev *indio_dev)
@@ -384,7 +388,7 @@ static int lis3l02dq_ring_postenable(struct iio_dev *indio_dev)
int ret;
bool oneenabled = false;

- ret = lis3l02dq_spi_read_reg_8(&indio_dev->dev,
+ ret = lis3l02dq_spi_read_reg_8(indio_dev,
LIS3L02DQ_REG_CTRL_1_ADDR,
&t);
if (ret)
@@ -408,7 +412,7 @@ static int lis3l02dq_ring_postenable(struct iio_dev *indio_dev)

if (!oneenabled) /* what happens in this case is unknown */
return -EINVAL;
- ret = lis3l02dq_spi_write_reg_8(&indio_dev->dev,
+ ret = lis3l02dq_spi_write_reg_8(indio_dev,
LIS3L02DQ_REG_CTRL_1_ADDR,
&t);
if (ret)
@@ -429,7 +433,7 @@ static int lis3l02dq_ring_predisable(struct iio_dev *indio_dev)
if (ret)
goto error_ret;

- ret = lis3l02dq_spi_read_reg_8(&indio_dev->dev,
+ ret = lis3l02dq_spi_read_reg_8(indio_dev,
LIS3L02DQ_REG_CTRL_1_ADDR,
&t);
if (ret)
@@ -438,7 +442,7 @@ static int lis3l02dq_ring_predisable(struct iio_dev *indio_dev)
LIS3L02DQ_REG_CTRL_1_AXES_Y_ENABLE |
LIS3L02DQ_REG_CTRL_1_AXES_Z_ENABLE;

- ret = lis3l02dq_spi_write_reg_8(&indio_dev->dev,
+ ret = lis3l02dq_spi_write_reg_8(indio_dev,
LIS3L02DQ_REG_CTRL_1_ADDR,
&t);

--
1.7.3.4

2011-03-31 14:57:29

by Jonathan Cameron

[permalink] [raw]
Subject: [PATCH 02/21] staging:iio:lis3l02dq - experimental move to new channel_spec approach.

---
drivers/staging/iio/accel/lis3l02dq.h | 13 +-
drivers/staging/iio/accel/lis3l02dq_core.c | 541 +++++++++++-----------------
drivers/staging/iio/accel/lis3l02dq_ring.c | 227 +++++-------
3 files changed, 306 insertions(+), 475 deletions(-)

diff --git a/drivers/staging/iio/accel/lis3l02dq.h b/drivers/staging/iio/accel/lis3l02dq.h
index 579b3a2..1d90b7d 100644
--- a/drivers/staging/iio/accel/lis3l02dq.h
+++ b/drivers/staging/iio/accel/lis3l02dq.h
@@ -188,9 +188,9 @@ int lis3l02dq_spi_write_reg_8(struct device *dev,
void lis3l02dq_remove_trigger(struct iio_dev *indio_dev);
int lis3l02dq_probe_trigger(struct iio_dev *indio_dev);

-ssize_t lis3l02dq_read_accel_from_ring(struct device *dev,
- struct device_attribute *attr,
- char *buf);
+ssize_t lis3l02dq_read_accel_from_ring(struct iio_ring_buffer *ring,
+ int index,
+ int *val);


int lis3l02dq_configure_ring(struct iio_dev *indio_dev);
@@ -215,11 +215,10 @@ static inline int lis3l02dq_probe_trigger(struct iio_dev *indio_dev)
{
return 0;
}
-
static inline ssize_t
-lis3l02dq_read_accel_from_ring(struct device *dev,
- struct device_attribute *attr,
- char *buf)
+lis3l02dq_read_accel_from_ring(struct iio_ring_buffer *ring,
+ int index,
+ int *val)
{
return 0;
}
diff --git a/drivers/staging/iio/accel/lis3l02dq_core.c b/drivers/staging/iio/accel/lis3l02dq_core.c
index b5d6711..5d22116 100644
--- a/drivers/staging/iio/accel/lis3l02dq_core.c
+++ b/drivers/staging/iio/accel/lis3l02dq_core.c
@@ -39,20 +39,11 @@
* This means that use cannot be made of spi_write etc.
*/

-/**
- * lis3l02dq_spi_read_reg_8() - read single byte from a single register
- * @dev: device asosciated with child of actual device (iio_dev or iio_trig)
- * @reg_address: the address of the register to be read
- * @val: pass back the resulting value
- **/
-int lis3l02dq_spi_read_reg_8(struct device *dev, u8 reg_address, u8 *val)
+static int __lis3l02dq_spi_read_reg_8(struct lis3l02dq_state *st,
+ u8 reg_address, u8 *val)
{
- int ret;
struct spi_message msg;
- struct iio_dev *indio_dev = dev_get_drvdata(dev);
- struct iio_sw_ring_helper_state *h = iio_dev_get_devdata(indio_dev);
- struct lis3l02dq_state *st = lis3l02dq_h_to_s(h);
-
+ int ret;
struct spi_transfer xfer = {
.tx_buf = st->tx,
.rx_buf = st->rx,
@@ -73,6 +64,19 @@ int lis3l02dq_spi_read_reg_8(struct device *dev, u8 reg_address, u8 *val)

return ret;
}
+/**
+ * lis3l02dq_spi_read_reg_8() - read single byte from a single register
+ * @dev: device asosciated with child of actual device (iio_dev or iio_trig)
+ * @reg_address: the address of the register to be read
+ * @val: pass back the resulting value
+ **/
+int lis3l02dq_spi_read_reg_8(struct device *dev, u8 reg_address, u8 *val)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct iio_sw_ring_helper_state *h = iio_dev_get_devdata(indio_dev);
+ struct lis3l02dq_state *st = lis3l02dq_h_to_s(h);
+ return __lis3l02dq_spi_read_reg_8(st, reg_address, val);
+}

/**
* lis3l02dq_spi_write_reg_8() - write single byte to a register
@@ -154,23 +158,13 @@ static int lis3l02dq_spi_write_reg_s16(struct device *dev,
return ret;
}

-/**
- * lisl302dq_spi_read_reg_s16() - write 2 bytes to a pair of registers
- * @dev: device associated with child of actual device (iio_dev or iio_trig)
- * @reg_address: the address of the lower of the two registers. Second register
- * is assumed to have address one greater.
- * @val: somewhere to pass back the value read
- **/
-static int lis3l02dq_spi_read_reg_s16(struct device *dev,
- u8 lower_reg_address,
- s16 *val)
+static int lis3l02dq_read_16bit_s(struct lis3l02dq_state *st,
+ u8 lower_reg_address,
+ int *val)
{
struct spi_message msg;
- struct iio_dev *indio_dev = dev_get_drvdata(dev);
- struct iio_sw_ring_helper_state *h
- = iio_dev_get_devdata(indio_dev);
- struct lis3l02dq_state *st = lis3l02dq_h_to_s(h);
int ret;
+ s16 tempval;
struct spi_transfer xfers[] = { {
.tx_buf = st->tx,
.rx_buf = st->rx,
@@ -182,15 +176,14 @@ static int lis3l02dq_spi_read_reg_s16(struct device *dev,
.rx_buf = st->rx + 2,
.bits_per_word = 8,
.len = 2,
- .cs_change = 1,
-
+ .cs_change = 0,
},
};

mutex_lock(&st->buf_lock);
st->tx[0] = LIS3L02DQ_READ_REG(lower_reg_address);
st->tx[1] = 0;
- st->tx[2] = LIS3L02DQ_READ_REG(lower_reg_address+1);
+ st->tx[2] = LIS3L02DQ_READ_REG(lower_reg_address + 1);
st->tx[3] = 0;

spi_message_init(&msg);
@@ -201,135 +194,102 @@ static int lis3l02dq_spi_read_reg_s16(struct device *dev,
dev_err(&st->us->dev, "problem when reading 16 bit register");
goto error_ret;
}
- *val = (s16)(st->rx[1]) | ((s16)(st->rx[3]) << 8);
+ tempval = (s16)(st->rx[1]) | ((s16)(st->rx[3]) << 8);

+ *val = tempval;
error_ret:
mutex_unlock(&st->buf_lock);
return ret;
}

-/**
- * lis3l02dq_read_signed() - attribute function used for 8 bit signed values
- * @dev: the child device associated with the iio_dev or iio_trigger
- * @attr: the attribute being processed
- * @buf: buffer into which put the output string
- **/
-static ssize_t lis3l02dq_read_signed(struct device *dev,
- struct device_attribute *attr,
- char *buf)
-{
- int ret;
- s8 val;
- struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
-
- ret = lis3l02dq_spi_read_reg_8(dev, this_attr->address, (u8 *)&val);
-
- return ret ? ret : sprintf(buf, "%d\n", val);
-}
-
-static ssize_t lis3l02dq_read_unsigned(struct device *dev,
- struct device_attribute *attr,
- char *buf)
-{
- int ret;
- u8 val;
- struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
-
- ret = lis3l02dq_spi_read_reg_8(dev, this_attr->address, &val);
-
- return ret ? ret : sprintf(buf, "%d\n", val);
-}
-
-static ssize_t lis3l02dq_write_signed(struct device *dev,
- struct device_attribute *attr,
- const char *buf,
- size_t len)
-{
- long valin;
- s8 val;
- int ret;
- struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
-
- ret = strict_strtol(buf, 10, &valin);
- if (ret)
- goto error_ret;
- val = valin;
- ret = lis3l02dq_spi_write_reg_8(dev, this_attr->address, (u8 *)&val);
-
-error_ret:
- return ret ? ret : len;
-}
-
-static ssize_t lis3l02dq_write_unsigned(struct device *dev,
- struct device_attribute *attr,
- const char *buf,
- size_t len)
-{
- int ret;
- ulong valin;
- u8 val;
- struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
-
- ret = strict_strtoul(buf, 10, &valin);
- if (ret)
- goto err_ret;
- val = valin;
- ret = lis3l02dq_spi_write_reg_8(dev, this_attr->address, &val);
+enum lis3l02dq_rm_ind {
+ LIS3L02DQ_ACCEL,
+ LIS3L02DQ_GAIN,
+ LIS3L02DQ_BIAS,
+};

-err_ret:
- return ret ? ret : len;
-}
+static u8 lis3l02dq_axis_map[3][3] = {
+ [LIS3L02DQ_ACCEL] = { LIS3L02DQ_REG_OUT_X_L_ADDR,
+ LIS3L02DQ_REG_OUT_Y_L_ADDR,
+ LIS3L02DQ_REG_OUT_Z_L_ADDR },
+ [LIS3L02DQ_GAIN] = { LIS3L02DQ_REG_GAIN_X_ADDR,
+ LIS3L02DQ_REG_GAIN_Y_ADDR,
+ LIS3L02DQ_REG_GAIN_Z_ADDR },
+ [LIS3L02DQ_BIAS] = { LIS3L02DQ_REG_OFFSET_X_ADDR,
+ LIS3L02DQ_REG_OFFSET_Y_ADDR,
+ LIS3L02DQ_REG_OFFSET_Z_ADDR }
+};

-static ssize_t lis3l02dq_read_16bit_signed(struct device *dev,
- struct device_attribute *attr,
- char *buf)
+static int lis3l02dq_read_thresh(struct iio_dev *indio_dev,
+ int e,
+ int *val)
{
- int ret;
- s16 val = 0;
- struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
-
- ret = lis3l02dq_spi_read_reg_s16(dev, this_attr->address, &val);
-
- if (ret)
- return ret;
+ struct iio_sw_ring_helper_state *h
+ = iio_dev_get_devdata(indio_dev);
+ struct lis3l02dq_state *st = lis3l02dq_h_to_s(h);

- return sprintf(buf, "%d\n", val);
+ return lis3l02dq_read_16bit_s(st, LIS3L02DQ_REG_THS_L_ADDR, val);
}

-static ssize_t lis3l02dq_read_accel(struct device *dev,
- struct device_attribute *attr,
- char *buf)
+static int lis3l02dq_write_thresh(struct iio_dev *indio_dev,
+ int event_code,
+ int val)
{
- struct iio_dev *indio_dev = dev_get_drvdata(dev);
- ssize_t ret;
-
- /* Take the iio_dev status lock */
- mutex_lock(&indio_dev->mlock);
- if (indio_dev->currentmode == INDIO_RING_TRIGGERED)
- ret = lis3l02dq_read_accel_from_ring(dev, attr, buf);
- else
- ret = lis3l02dq_read_16bit_signed(dev, attr, buf);
- mutex_unlock(&indio_dev->mlock);
-
- return ret;
+ u16 value = val;
+ return lis3l02dq_spi_write_reg_s16(&indio_dev->dev,
+ LIS3L02DQ_REG_THS_L_ADDR,
+ value);
}

-static ssize_t lis3l02dq_write_16bit_signed(struct device *dev,
- struct device_attribute *attr,
- const char *buf,
- size_t len)
+static int lis3l02dq_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec *chan,
+ int *val,
+ long mask)
{
- struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
- int ret;
- long val;
+ u8 utemp;
+ s8 stemp;
+ ssize_t ret = 0;
+ struct iio_sw_ring_helper_state *h
+ = iio_dev_get_devdata(indio_dev);
+ struct lis3l02dq_state *st = lis3l02dq_h_to_s(h);
+ u8 reg;
+ switch (mask) {
+ case 0:
+ /* Take the iio_dev status lock */
+ mutex_lock(&indio_dev->mlock);
+ if (indio_dev->currentmode == INDIO_RING_TRIGGERED)
+ ret = lis3l02dq_read_accel_from_ring(indio_dev->ring,
+ chan->scan_index,
+ val);
+ else {
+ reg = lis3l02dq_axis_map
+ [LIS3L02DQ_ACCEL][chan->address];
+ ret = lis3l02dq_read_16bit_s(st, reg, val);
+ }
+ mutex_unlock(&indio_dev->mlock);
+ break;
+ case (1 << IIO_CHAN_INFO_SCALE_SHARED):
+ *val = 9580;
+ break;
+ case (1 << IIO_CHAN_INFO_CALIBSCALE_SEPARATE):
+ reg = lis3l02dq_axis_map[LIS3L02DQ_GAIN][chan->address];
+ ret = __lis3l02dq_spi_read_reg_8(st, reg, &utemp);
+ if (ret)
+ goto error_ret;
+ /* to match with what previous code does */
+ *val = 1000000*utemp;
+ break;

- ret = strict_strtol(buf, 10, &val);
- if (ret)
- goto error_ret;
- ret = lis3l02dq_spi_write_reg_s16(dev, this_attr->address, val);
+ case (1 << IIO_CHAN_INFO_CALIBBIAS_SEPARATE):
+ reg = lis3l02dq_axis_map[LIS3L02DQ_BIAS][chan->address];
+ ret = __lis3l02dq_spi_read_reg_8(st, reg, (u8 *)&stemp);
+ /* to match with what previous code does */
+ *val = 1000000*stemp;
+ break;

+ }
error_ret:
- return ret ? ret : len;
+ return ret;
}

static ssize_t lis3l02dq_read_frequency(struct device *dev,
@@ -469,159 +429,130 @@ err_ret:
return ret;
}

-#define LIS3L02DQ_SIGNED_ATTR(name, reg) \
- IIO_DEVICE_ATTR(name, \
- S_IWUSR | S_IRUGO, \
- lis3l02dq_read_signed, \
- lis3l02dq_write_signed, \
- reg);
-
-#define LIS3L02DQ_UNSIGNED_ATTR(name, reg) \
- IIO_DEVICE_ATTR(name, \
- S_IWUSR | S_IRUGO, \
- lis3l02dq_read_unsigned, \
- lis3l02dq_write_unsigned, \
- reg);
-
-static LIS3L02DQ_SIGNED_ATTR(accel_x_calibbias,
- LIS3L02DQ_REG_OFFSET_X_ADDR);
-static LIS3L02DQ_SIGNED_ATTR(accel_y_calibbias,
- LIS3L02DQ_REG_OFFSET_Y_ADDR);
-static LIS3L02DQ_SIGNED_ATTR(accel_z_calibbias,
- LIS3L02DQ_REG_OFFSET_Z_ADDR);
-
-static LIS3L02DQ_UNSIGNED_ATTR(accel_x_calibscale,
- LIS3L02DQ_REG_GAIN_X_ADDR);
-static LIS3L02DQ_UNSIGNED_ATTR(accel_y_calibscale,
- LIS3L02DQ_REG_GAIN_Y_ADDR);
-static LIS3L02DQ_UNSIGNED_ATTR(accel_z_calibscale,
- LIS3L02DQ_REG_GAIN_Z_ADDR);
-
-static IIO_DEVICE_ATTR(accel_raw_mag_value,
- S_IWUSR | S_IRUGO,
- lis3l02dq_read_16bit_signed,
- lis3l02dq_write_16bit_signed,
- LIS3L02DQ_REG_THS_L_ADDR);
-/* RFC The reading method for these will change depending on whether
- * ring buffer capture is in use. Is it worth making these take two
- * functions and let the core handle which to call, or leave as in this
- * driver where it is the drivers problem to manage this?
- */
-
-static IIO_DEV_ATTR_ACCEL_X(lis3l02dq_read_accel,
- LIS3L02DQ_REG_OUT_X_L_ADDR);
-
-static IIO_DEV_ATTR_ACCEL_Y(lis3l02dq_read_accel,
- LIS3L02DQ_REG_OUT_Y_L_ADDR);
-
-static IIO_DEV_ATTR_ACCEL_Z(lis3l02dq_read_accel,
- LIS3L02DQ_REG_OUT_Z_L_ADDR);
-
static IIO_DEV_ATTR_SAMP_FREQ(S_IWUSR | S_IRUGO,
lis3l02dq_read_frequency,
lis3l02dq_write_frequency);

static IIO_CONST_ATTR_SAMP_FREQ_AVAIL("280 560 1120 4480");

-static ssize_t lis3l02dq_read_interrupt_config(struct device *dev,
- struct device_attribute *attr,
- char *buf)
+static int lis3l02dq_thresh_handler_th(struct iio_dev *indio_dev,
+ int index,
+ s64 timestamp,
+ int no_test)
{
- int ret;
- s8 val;
- struct iio_event_attr *this_attr = to_iio_event_attr(attr);
+ struct iio_sw_ring_helper_state *h
+ = iio_dev_get_devdata(indio_dev);
+ struct lis3l02dq_state *st = lis3l02dq_h_to_s(h);

- ret = lis3l02dq_spi_read_reg_8(dev->parent,
- LIS3L02DQ_REG_WAKE_UP_CFG_ADDR,
- (u8 *)&val);
+ /* Stash the timestamp somewhere convenient for the bh */
+ st->thresh_timestamp = timestamp;
+ schedule_work(&st->work_thresh);

- return ret ? ret : sprintf(buf, "%d\n", !!(val & this_attr->mask));
+ return 0;
}

-static ssize_t lis3l02dq_write_interrupt_config(struct device *dev,
- struct device_attribute *attr,
- const char *buf,
- size_t len)
-{
- struct iio_event_attr *this_attr = to_iio_event_attr(attr);
- struct iio_dev *indio_dev = dev_get_drvdata(dev);
- int ret, currentlyset, changed = 0;
- u8 valold, controlold;
- bool val;
+/* A shared handler for a number of threshold types */
+IIO_EVENT_SH(threshold, &lis3l02dq_thresh_handler_th);

- val = !(buf[0] == '0');

- mutex_lock(&indio_dev->mlock);
- /* read current value */
- ret = lis3l02dq_spi_read_reg_8(dev->parent,
+#define LIS3L02DQ_INFO_MASK \
+ ((1 << IIO_CHAN_INFO_SCALE_SHARED) | \
+ (1 << IIO_CHAN_INFO_CALIBSCALE_SEPARATE) | \
+ (1 << IIO_CHAN_INFO_CALIBBIAS_SEPARATE))
+
+#define LIS3L02DQ_EVENT_MASK \
+ (IIO_EV_BIT(IIO_EV_TYPE_THRESH, IIO_EV_DIR_RISING) | \
+ IIO_EV_BIT(IIO_EV_TYPE_THRESH, IIO_EV_DIR_FALLING))
+
+static struct iio_chan_spec lis3l02dq_channels[] = {
+ IIO_CHAN_EV(IIO_ACCEL, 'x', LIS3L02DQ_INFO_MASK,
+ 0, 0, IIO_ST('s', 12, 16, 0),
+ LIS3L02DQ_EVENT_MASK, &iio_event_threshold),
+ IIO_CHAN_EV(IIO_ACCEL, 'y', LIS3L02DQ_INFO_MASK,
+ 1, 1, IIO_ST('s', 12, 16, 0),
+ LIS3L02DQ_EVENT_MASK, &iio_event_threshold),
+ IIO_CHAN(IIO_ACCEL, 'z', LIS3L02DQ_INFO_MASK,
+ 2, 2, IIO_ST('s', 12, 16, 0)),
+ IIO_CHAN_SOFT_TIMESTAMP(3)
+};
+
+
+static ssize_t lis3l02dq_read_event_config(struct iio_dev *indio_dev,
+ int event_code)
+{
+
+ u8 val;
+ int ret;
+ u8 mask = (1 << (IIO_EVENT_CODE_EXTRACT_MODIFIER(event_code)*2 +
+ (IIO_EVENT_CODE_EXTRACT_DIR(event_code) ==
+ IIO_EV_DIR_RISING)));
+ ret = lis3l02dq_spi_read_reg_8(&indio_dev->dev,
LIS3L02DQ_REG_WAKE_UP_CFG_ADDR,
- &valold);
- if (ret)
- goto error_mutex_unlock;
+ &val);
+ if (ret < 0)
+ return ret;
+
+ return !!(val & mask);
+}
+
+static int lis3l02dq_write_event_config(struct iio_dev *indio_dev,
+ int event_code,
+ struct iio_event_handler_list *list_el,
+ int state)
+{
+ int ret = 0;
+ u8 val, control;
+ u8 currentlyset;
+ bool changed = false;
+ u8 mask = (1 << (IIO_EVENT_CODE_EXTRACT_MODIFIER(event_code)*2 +
+ (IIO_EVENT_CODE_EXTRACT_DIR(event_code) ==
+ IIO_EV_DIR_RISING)));

+ mutex_lock(&indio_dev->mlock);
/* read current control */
- ret = lis3l02dq_spi_read_reg_8(dev,
+ ret = lis3l02dq_spi_read_reg_8(&indio_dev->dev,
LIS3L02DQ_REG_CTRL_2_ADDR,
- &controlold);
+ &control);
if (ret)
- goto error_mutex_unlock;
- currentlyset = !!(valold & this_attr->mask);
- if (val == false && currentlyset) {
- valold &= ~this_attr->mask;
- changed = 1;
- iio_remove_event_from_list(this_attr->listel,
- &indio_dev->interrupts[0]
- ->ev_list);
- } else if (val == true && !currentlyset) {
- changed = 1;
- valold |= this_attr->mask;
- iio_add_event_to_list(this_attr->listel,
- &indio_dev->interrupts[0]->ev_list);
+ goto error_ret;
+ ret = lis3l02dq_spi_read_reg_8(&indio_dev->dev,
+ LIS3L02DQ_REG_WAKE_UP_CFG_ADDR,
+ &val);
+ if (ret < 0)
+ goto error_ret;
+ currentlyset = val & mask;
+
+ if (!currentlyset && state) {
+ changed = true;
+ val |= mask;
+ iio_add_event_to_list(list_el,
+ &indio_dev->interrupts[0]->ev_list);
+
+ } else if (currentlyset && !state) {
+ changed = true;
+ val &= ~mask;
+ iio_remove_event_from_list(list_el,
+ &indio_dev->interrupts[0]->ev_list);
}
-
if (changed) {
- ret = lis3l02dq_spi_write_reg_8(dev,
+ ret = lis3l02dq_spi_write_reg_8(&indio_dev->dev,
LIS3L02DQ_REG_WAKE_UP_CFG_ADDR,
- &valold);
- if (ret)
- goto error_mutex_unlock;
- /* This always enables the interrupt, even if we've remove the
- * last thing using it. For this device we can use the reference
- * count on the handler to tell us if anyone wants the interrupt
- */
- controlold = this_attr->listel->refcount ?
- (controlold | LIS3L02DQ_REG_CTRL_2_ENABLE_INTERRUPT) :
- (controlold & ~LIS3L02DQ_REG_CTRL_2_ENABLE_INTERRUPT);
- ret = lis3l02dq_spi_write_reg_8(dev,
- LIS3L02DQ_REG_CTRL_2_ADDR,
- &controlold);
+ &val);
if (ret)
- goto error_mutex_unlock;
+ goto error_ret;
+ control = list_el->refcount ?
+ (control | LIS3L02DQ_REG_CTRL_2_ENABLE_INTERRUPT) :
+ (control & ~LIS3L02DQ_REG_CTRL_2_ENABLE_INTERRUPT);
+ ret = lis3l02dq_spi_write_reg_8(&indio_dev->dev,
+ LIS3L02DQ_REG_CTRL_2_ADDR,
+ &control);
}
-error_mutex_unlock:
- mutex_unlock(&indio_dev->mlock);
-
- return ret ? ret : len;
-}
-
-
-static int lis3l02dq_thresh_handler_th(struct iio_dev *indio_dev,
- int index,
- s64 timestamp,
- int no_test)
-{
- struct iio_sw_ring_helper_state *h
- = iio_dev_get_devdata(indio_dev);
- struct lis3l02dq_state *st = lis3l02dq_h_to_s(h);

- /* Stash the timestamp somewhere convenient for the bh */
- st->thresh_timestamp = timestamp;
- schedule_work(&st->work_thresh);
-
- return 0;
+error_ret:
+ mutex_unlock(&indio_dev->mlock);
+ return ret;
}

-
/* Unforunately it appears the interrupt won't clear unless you read from the
* src register.
*/
@@ -700,75 +631,9 @@ static void lis3l02dq_thresh_handler_bh_no_check(struct work_struct *work_s)
return;
}

-/* A shared handler for a number of threshold types */
-IIO_EVENT_SH(threshold, &lis3l02dq_thresh_handler_th);
-
-IIO_EVENT_ATTR_SH(accel_x_thresh_rising_en,
- iio_event_threshold,
- lis3l02dq_read_interrupt_config,
- lis3l02dq_write_interrupt_config,
- LIS3L02DQ_REG_WAKE_UP_CFG_INTERRUPT_X_HIGH);
-
-IIO_EVENT_ATTR_SH(accel_y_thresh_rising_en,
- iio_event_threshold,
- lis3l02dq_read_interrupt_config,
- lis3l02dq_write_interrupt_config,
- LIS3L02DQ_REG_WAKE_UP_CFG_INTERRUPT_Y_HIGH);
-
-IIO_EVENT_ATTR_SH(accel_z_thresh_rising_en,
- iio_event_threshold,
- lis3l02dq_read_interrupt_config,
- lis3l02dq_write_interrupt_config,
- LIS3L02DQ_REG_WAKE_UP_CFG_INTERRUPT_Z_HIGH);
-
-IIO_EVENT_ATTR_SH(accel_x_thresh_falling_en,
- iio_event_threshold,
- lis3l02dq_read_interrupt_config,
- lis3l02dq_write_interrupt_config,
- LIS3L02DQ_REG_WAKE_UP_CFG_INTERRUPT_X_LOW);
-
-IIO_EVENT_ATTR_SH(accel_y_thresh_falling_en,
- iio_event_threshold,
- lis3l02dq_read_interrupt_config,
- lis3l02dq_write_interrupt_config,
- LIS3L02DQ_REG_WAKE_UP_CFG_INTERRUPT_Y_LOW);
-
-IIO_EVENT_ATTR_SH(accel_z_thresh_falling_en,
- iio_event_threshold,
- lis3l02dq_read_interrupt_config,
- lis3l02dq_write_interrupt_config,
- LIS3L02DQ_REG_WAKE_UP_CFG_INTERRUPT_Z_LOW);
-
-
-static struct attribute *lis3l02dq_event_attributes[] = {
- &iio_event_attr_accel_x_thresh_rising_en.dev_attr.attr,
- &iio_event_attr_accel_y_thresh_rising_en.dev_attr.attr,
- &iio_event_attr_accel_z_thresh_rising_en.dev_attr.attr,
- &iio_event_attr_accel_x_thresh_falling_en.dev_attr.attr,
- &iio_event_attr_accel_y_thresh_falling_en.dev_attr.attr,
- &iio_event_attr_accel_z_thresh_falling_en.dev_attr.attr,
- &iio_dev_attr_accel_raw_mag_value.dev_attr.attr,
- NULL
-};
-
-static struct attribute_group lis3l02dq_event_attribute_group = {
- .attrs = lis3l02dq_event_attributes,
-};
-
static IIO_CONST_ATTR_NAME("lis3l02dq");
-static IIO_CONST_ATTR(accel_scale, "0.00958");

static struct attribute *lis3l02dq_attributes[] = {
- &iio_dev_attr_accel_x_calibbias.dev_attr.attr,
- &iio_dev_attr_accel_y_calibbias.dev_attr.attr,
- &iio_dev_attr_accel_z_calibbias.dev_attr.attr,
- &iio_dev_attr_accel_x_calibscale.dev_attr.attr,
- &iio_dev_attr_accel_y_calibscale.dev_attr.attr,
- &iio_dev_attr_accel_z_calibscale.dev_attr.attr,
- &iio_const_attr_accel_scale.dev_attr.attr,
- &iio_dev_attr_accel_x_raw.dev_attr.attr,
- &iio_dev_attr_accel_y_raw.dev_attr.attr,
- &iio_dev_attr_accel_z_raw.dev_attr.attr,
&iio_dev_attr_sampling_frequency.dev_attr.attr,
&iio_const_attr_sampling_frequency_available.dev_attr.attr,
&iio_const_attr_name.dev_attr.attr,
@@ -813,7 +678,13 @@ static int __devinit lis3l02dq_probe(struct spi_device *spi)

st->help.indio_dev->dev.parent = &spi->dev;
st->help.indio_dev->num_interrupt_lines = 1;
- st->help.indio_dev->event_attrs = &lis3l02dq_event_attribute_group;
+ st->help.indio_dev->channels = lis3l02dq_channels;
+ st->help.indio_dev->num_channels = ARRAY_SIZE(lis3l02dq_channels);
+ st->help.indio_dev->read_raw = &lis3l02dq_read_raw;
+ st->help.indio_dev->read_event_value = &lis3l02dq_read_thresh;
+ st->help.indio_dev->write_event_value = &lis3l02dq_write_thresh;
+ st->help.indio_dev->write_event_config = &lis3l02dq_write_event_config;
+ st->help.indio_dev->read_event_config = &lis3l02dq_read_event_config;
st->help.indio_dev->attrs = &lis3l02dq_attribute_group;
st->help.indio_dev->dev_data = (void *)(&st->help);
st->help.indio_dev->driver_module = THIS_MODULE;
@@ -828,7 +699,9 @@ static int __devinit lis3l02dq_probe(struct spi_device *spi)
goto error_unreg_ring_funcs;
regdone = 1;

- ret = iio_ring_buffer_register(st->help.indio_dev->ring, 0);
+ ret = iio_ring_buffer_register_ex(st->help.indio_dev->ring, 0,
+ lis3l02dq_channels,
+ ARRAY_SIZE(lis3l02dq_channels));
if (ret) {
printk(KERN_ERR "failed to initialize the ring\n");
goto error_unreg_ring_funcs;
diff --git a/drivers/staging/iio/accel/lis3l02dq_ring.c b/drivers/staging/iio/accel/lis3l02dq_ring.c
index 2c461a3..c6c4762 100644
--- a/drivers/staging/iio/accel/lis3l02dq_ring.c
+++ b/drivers/staging/iio/accel/lis3l02dq_ring.c
@@ -29,86 +29,6 @@ static inline u16 combine_8_to_16(u8 lower, u8 upper)
}

/**
- * lis3l02dq_scan_el_set_state() set whether a scan contains a given channel
- * @scan_el: associtate iio scan element attribute
- * @indio_dev: the device structure
- * @bool: desired state
- *
- * mlock already held when this is called.
- **/
-static int lis3l02dq_scan_el_set_state(struct iio_scan_el *scan_el,
- struct iio_dev *indio_dev,
- bool state)
-{
- u8 t, mask;
- int ret;
-
- ret = lis3l02dq_spi_read_reg_8(&indio_dev->dev,
- LIS3L02DQ_REG_CTRL_1_ADDR,
- &t);
- if (ret)
- goto error_ret;
- switch (scan_el->label) {
- case LIS3L02DQ_REG_OUT_X_L_ADDR:
- mask = LIS3L02DQ_REG_CTRL_1_AXES_X_ENABLE;
- break;
- case LIS3L02DQ_REG_OUT_Y_L_ADDR:
- mask = LIS3L02DQ_REG_CTRL_1_AXES_Y_ENABLE;
- break;
- case LIS3L02DQ_REG_OUT_Z_L_ADDR:
- mask = LIS3L02DQ_REG_CTRL_1_AXES_Z_ENABLE;
- break;
- default:
- ret = -EINVAL;
- goto error_ret;
- }
-
- if (!(mask & t) == state) {
- if (state)
- t |= mask;
- else
- t &= ~mask;
- ret = lis3l02dq_spi_write_reg_8(&indio_dev->dev,
- LIS3L02DQ_REG_CTRL_1_ADDR,
- &t);
- }
-error_ret:
- return ret;
-
-}
-static IIO_SCAN_EL_C(accel_x, 0,
- LIS3L02DQ_REG_OUT_X_L_ADDR,
- &lis3l02dq_scan_el_set_state);
-static IIO_SCAN_EL_C(accel_y, 1,
- LIS3L02DQ_REG_OUT_Y_L_ADDR,
- &lis3l02dq_scan_el_set_state);
-static IIO_SCAN_EL_C(accel_z, 2,
- LIS3L02DQ_REG_OUT_Z_L_ADDR,
- &lis3l02dq_scan_el_set_state);
-static IIO_CONST_ATTR_SCAN_EL_TYPE(accel, s, 12, 16);
-static IIO_SCAN_EL_TIMESTAMP(3);
-static IIO_CONST_ATTR_SCAN_EL_TYPE(timestamp, s, 64, 64);
-
-static struct attribute *lis3l02dq_scan_el_attrs[] = {
- &iio_scan_el_accel_x.dev_attr.attr,
- &iio_const_attr_accel_x_index.dev_attr.attr,
- &iio_scan_el_accel_y.dev_attr.attr,
- &iio_const_attr_accel_y_index.dev_attr.attr,
- &iio_scan_el_accel_z.dev_attr.attr,
- &iio_const_attr_accel_z_index.dev_attr.attr,
- &iio_const_attr_accel_type.dev_attr.attr,
- &iio_scan_el_timestamp.dev_attr.attr,
- &iio_const_attr_timestamp_index.dev_attr.attr,
- &iio_const_attr_timestamp_type.dev_attr.attr,
- NULL,
-};
-
-static struct attribute_group lis3l02dq_scan_el_group = {
- .attrs = lis3l02dq_scan_el_attrs,
- .name = "scan_elements",
-};
-
-/**
* lis3l02dq_poll_func_th() top half interrupt handler called by trigger
* @private_data: iio_dev
**/
@@ -151,58 +71,27 @@ IIO_EVENT_SH(data_rdy_trig, &lis3l02dq_data_rdy_trig_poll);
/**
* lis3l02dq_read_accel_from_ring() individual acceleration read from ring
**/
-ssize_t lis3l02dq_read_accel_from_ring(struct device *dev,
- struct device_attribute *attr,
- char *buf)
+ssize_t lis3l02dq_read_accel_from_ring(struct iio_ring_buffer *ring,
+ int index,
+ int *val)
{
- struct iio_scan_el *el = NULL;
- int ret, len = 0, i = 0;
- struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
- struct iio_dev *dev_info = dev_get_drvdata(dev);
- struct iio_ring_buffer *ring = dev_info->ring;
- struct attribute_group *scan_el_attrs = ring->scan_el_attrs;
+ int ret;
s16 *data;
+ if (!iio_scan_mask_query(ring, index))
+ return -EINVAL;

- while (scan_el_attrs->attrs[i]) {
- el = to_iio_scan_el((struct device_attribute *)
- (scan_el_attrs->attrs[i]));
- /* label is in fact the address */
- if (el->label == this_attr->address)
- break;
- i++;
- }
- if (!scan_el_attrs->attrs[i]) {
- ret = -EINVAL;
- goto error_ret;
- }
- /* If this element is in the scan mask */
- ret = iio_scan_mask_query(ring, el->number);
- if (ret < 0)
- goto error_ret;
- if (ret) {
- data = kmalloc(ring->access.get_bytes_per_datum(ring),
- GFP_KERNEL);
- if (data == NULL)
- return -ENOMEM;
- ret = ring->access.read_last(ring,
- (u8 *)data);
- if (ret)
- goto error_free_data;
- } else {
- ret = -EINVAL;
- goto error_ret;
- }
- len = iio_scan_mask_count_to_right(ring, el->number);
- if (len < 0) {
- ret = len;
+ data = kmalloc(ring->access.get_bytes_per_datum(ring),
+ GFP_KERNEL);
+ if (data == NULL)
+ return -ENOMEM;
+
+ ret = ring->access.read_last(ring, (u8 *)data);
+ if (ret)
goto error_free_data;
- }
- len = sprintf(buf, "ring %d\n", data[len]);
+ *val = data[iio_scan_mask_count_to_right(ring, index)];
error_free_data:
kfree(data);
-error_ret:
- return ret ? ret : len;
-
+ return ret;
}

static const u8 read_all_tx_array[] = {
@@ -234,7 +123,7 @@ static int lis3l02dq_read_all(struct lis3l02dq_state *st, u8 *rx_array)

mutex_lock(&st->buf_lock);

- for (i = 0; i < ARRAY_SIZE(read_all_tx_array)/4; i++) {
+ for (i = 0; i < ARRAY_SIZE(read_all_tx_array)/4; i++)
if (ring->scan_mask & (1 << i)) {
/* lower byte */
xfers[j].tx_buf = st->tx + 2*j;
@@ -258,7 +147,7 @@ static int lis3l02dq_read_all(struct lis3l02dq_state *st, u8 *rx_array)
xfers[j].cs_change = 1;
j++;
}
- }
+
/* After these are transmitted, the rx_buff should have
* values in alternate bytes
*/
@@ -488,6 +377,76 @@ void lis3l02dq_unconfigure_ring(struct iio_dev *indio_dev)
lis3l02dq_free_buf(indio_dev->ring);
}

+static int lis3l02dq_ring_postenable(struct iio_dev *indio_dev)
+{
+ /* Disable unwanted channels otherwise the interrupt will not clear */
+ u8 t;
+ int ret;
+ bool oneenabled = false;
+
+ ret = lis3l02dq_spi_read_reg_8(&indio_dev->dev,
+ LIS3L02DQ_REG_CTRL_1_ADDR,
+ &t);
+ if (ret)
+ goto error_ret;
+
+ if (iio_scan_mask_query(indio_dev->ring, 0)) {
+ t |= LIS3L02DQ_REG_CTRL_1_AXES_X_ENABLE;
+ oneenabled = true;
+ } else
+ t &= ~LIS3L02DQ_REG_CTRL_1_AXES_X_ENABLE;
+ if (iio_scan_mask_query(indio_dev->ring, 1)) {
+ t |= LIS3L02DQ_REG_CTRL_1_AXES_Y_ENABLE;
+ oneenabled = true;
+ } else
+ t &= ~LIS3L02DQ_REG_CTRL_1_AXES_Y_ENABLE;
+ if (iio_scan_mask_query(indio_dev->ring, 2)) {
+ t |= LIS3L02DQ_REG_CTRL_1_AXES_Z_ENABLE;
+ oneenabled = true;
+ } else
+ t &= ~LIS3L02DQ_REG_CTRL_1_AXES_Z_ENABLE;
+
+ if (!oneenabled) /* what happens in this case is unknown */
+ return -EINVAL;
+ ret = lis3l02dq_spi_write_reg_8(&indio_dev->dev,
+ LIS3L02DQ_REG_CTRL_1_ADDR,
+ &t);
+ if (ret)
+ goto error_ret;
+
+ return iio_triggered_ring_postenable(indio_dev);
+error_ret:
+ return ret;
+}
+
+/* Turn all channels on again */
+static int lis3l02dq_ring_predisable(struct iio_dev *indio_dev)
+{
+ u8 t;
+ int ret;
+
+ ret = iio_triggered_ring_predisable(indio_dev);
+ if (ret)
+ goto error_ret;
+
+ ret = lis3l02dq_spi_read_reg_8(&indio_dev->dev,
+ LIS3L02DQ_REG_CTRL_1_ADDR,
+ &t);
+ if (ret)
+ goto error_ret;
+ t |= LIS3L02DQ_REG_CTRL_1_AXES_X_ENABLE |
+ LIS3L02DQ_REG_CTRL_1_AXES_Y_ENABLE |
+ LIS3L02DQ_REG_CTRL_1_AXES_Z_ENABLE;
+
+ ret = lis3l02dq_spi_write_reg_8(&indio_dev->dev,
+ LIS3L02DQ_REG_CTRL_1_ADDR,
+ &t);
+
+error_ret:
+ return ret;
+}
+
+
int lis3l02dq_configure_ring(struct iio_dev *indio_dev)
{
int ret;
@@ -504,17 +463,17 @@ int lis3l02dq_configure_ring(struct iio_dev *indio_dev)
/* Effectively select the ring buffer implementation */
lis3l02dq_register_buf_funcs(&ring->access);
ring->bpe = 2;
- ring->scan_el_attrs = &lis3l02dq_scan_el_group;
+
ring->scan_timestamp = true;
ring->preenable = &iio_sw_ring_preenable;
- ring->postenable = &iio_triggered_ring_postenable;
- ring->predisable = &iio_triggered_ring_predisable;
+ ring->postenable = &lis3l02dq_ring_postenable;
+ ring->predisable = &lis3l02dq_ring_predisable;
ring->owner = THIS_MODULE;

/* Set default scan mode */
- iio_scan_mask_set(ring, iio_scan_el_accel_x.number);
- iio_scan_mask_set(ring, iio_scan_el_accel_y.number);
- iio_scan_mask_set(ring, iio_scan_el_accel_z.number);
+ iio_scan_mask_set(ring, 0);
+ iio_scan_mask_set(ring, 1);
+ iio_scan_mask_set(ring, 2);

ret = iio_alloc_pollfunc(indio_dev, NULL, &lis3l02dq_poll_func_th);
if (ret)
--
1.7.3.4

2011-03-31 14:57:25

by Jonathan Cameron

[permalink] [raw]
Subject: [PATCH 07/21] staging:iio: Buffer device flattening.

Given we now only have one device we don't need the extra layer any more.
Hence this patch removes it.

Signed-off-by: Jonathan Cameron <[email protected]>
---
drivers/staging/iio/industrialio-ring.c | 76 ++++++++++---------------------
drivers/staging/iio/ring_generic.h | 8 +---
drivers/staging/iio/ring_sw.c | 3 +-
3 files changed, 28 insertions(+), 59 deletions(-)

diff --git a/drivers/staging/iio/industrialio-ring.c b/drivers/staging/iio/industrialio-ring.c
index b503ea1..07d64bc 100644
--- a/drivers/staging/iio/industrialio-ring.c
+++ b/drivers/staging/iio/industrialio-ring.c
@@ -106,72 +106,60 @@ static const struct file_operations iio_ring_fileops = {
.llseek = noop_llseek,
};

-static void iio_ring_access_release(struct device *dev)
+void iio_ring_access_release(struct device *dev)
{
struct iio_ring_buffer *buf
- = access_dev_to_iio_ring_buffer(dev);
+ = container_of(dev, struct iio_ring_buffer, dev);
cdev_del(&buf->access_handler.chrdev);
iio_device_free_chrdev_minor(MINOR(dev->devt));
}
-
-static struct device_type iio_ring_access_type = {
- .release = iio_ring_access_release,
-};
+EXPORT_SYMBOL(iio_ring_access_release);

static inline int
-__iio_request_ring_buffer_access_chrdev(struct iio_ring_buffer *buf,
- int id,
+__iio_request_ring_buffer_chrdev(struct iio_ring_buffer *buf,
struct module *owner)
{
int ret, minor;

buf->access_handler.flags = 0;

- buf->access_dev.parent = &buf->dev;
- buf->access_dev.bus = &iio_bus_type;
- buf->access_dev.type = &iio_ring_access_type;
- device_initialize(&buf->access_dev);
+ buf->dev.bus = &iio_bus_type;
+ device_initialize(&buf->dev);

minor = iio_device_get_chrdev_minor();
if (minor < 0) {
ret = minor;
goto error_device_put;
}
- buf->access_dev.devt = MKDEV(MAJOR(iio_devt), minor);
-
-
- buf->access_id = id;
-
- dev_set_name(&buf->access_dev, "%s:access%d",
- dev_name(&buf->dev),
- buf->access_id);
- ret = device_add(&buf->access_dev);
+ buf->dev.devt = MKDEV(MAJOR(iio_devt), minor);
+ dev_set_name(&buf->dev, "%s:buffer%d",
+ dev_name(buf->dev.parent),
+ buf->id);
+ ret = device_add(&buf->dev);
if (ret < 0) {
- printk(KERN_ERR "failed to add the ring access dev\n");
+ printk(KERN_ERR "failed to add the ring dev\n");
goto error_device_put;
}
-
cdev_init(&buf->access_handler.chrdev, &iio_ring_fileops);
buf->access_handler.chrdev.owner = owner;
-
- ret = cdev_add(&buf->access_handler.chrdev, buf->access_dev.devt, 1);
+ ret = cdev_add(&buf->access_handler.chrdev, buf->dev.devt, 1);
if (ret) {
- printk(KERN_ERR "failed to allocate ring access chrdev\n");
+ printk(KERN_ERR "failed to allocate ring chrdev\n");
goto error_device_unregister;
}
return 0;

error_device_unregister:
- device_unregister(&buf->access_dev);
+ device_unregister(&buf->dev);
error_device_put:
- put_device(&buf->access_dev);
+ put_device(&buf->dev);

return ret;
}

-static void __iio_free_ring_buffer_access_chrdev(struct iio_ring_buffer *buf)
+static void __iio_free_ring_buffer_chrdev(struct iio_ring_buffer *buf)
{
- device_unregister(&buf->access_dev);
+ device_unregister(&buf->dev);
}

void iio_ring_buffer_init(struct iio_ring_buffer *ring,
@@ -344,36 +332,25 @@ int iio_ring_buffer_register_ex(struct iio_ring_buffer *ring, int id,

ring->id = id;

- dev_set_name(&ring->dev, "%s:buffer%d",
- dev_name(ring->dev.parent),
- ring->id);
- ret = device_add(&ring->dev);
- if (ret)
- goto error_ret;
-
- ret = __iio_request_ring_buffer_access_chrdev(ring,
- 0,
- ring->owner);
+ ret = __iio_request_ring_buffer_chrdev(ring, ring->owner);

if (ret)
- goto error_remove_device;
-
+ goto error_ret;
if (ring->scan_el_attrs) {
ret = sysfs_create_group(&ring->dev.kobj,
ring->scan_el_attrs);
if (ret) {
dev_err(&ring->dev,
"Failed to add sysfs scan elements\n");
- goto error_free_ring_buffer_access_chrdev;
+ goto error_free_ring_buffer_chrdev;
}
} else if (channels) {
ret = sysfs_create_group(&ring->dev.kobj,
&iio_scan_el_dummy_group);
if (ret)
- goto error_free_ring_buffer_access_chrdev;
+ goto error_free_ring_buffer_chrdev;
}

-
INIT_LIST_HEAD(&ring->scan_el_dev_attr_list);
INIT_LIST_HEAD(&ring->scan_el_en_attr_list);
if (channels) {
@@ -388,10 +365,8 @@ int iio_ring_buffer_register_ex(struct iio_ring_buffer *ring, int id,
return 0;
error_cleanup_dynamic:
__iio_ring_attr_cleanup(ring);
-error_free_ring_buffer_access_chrdev:
- __iio_free_ring_buffer_access_chrdev(ring);
-error_remove_device:
- device_del(&ring->dev);
+error_free_ring_buffer_chrdev:
+ __iio_free_ring_buffer_chrdev(ring);
error_ret:
return ret;
}
@@ -406,8 +381,7 @@ EXPORT_SYMBOL(iio_ring_buffer_register);
void iio_ring_buffer_unregister(struct iio_ring_buffer *ring)
{
__iio_ring_attr_cleanup(ring);
- __iio_free_ring_buffer_access_chrdev(ring);
- device_del(&ring->dev);
+ __iio_free_ring_buffer_chrdev(ring);
}
EXPORT_SYMBOL(iio_ring_buffer_unregister);

diff --git a/drivers/staging/iio/ring_generic.h b/drivers/staging/iio/ring_generic.h
index 44c7543..ba441d9 100644
--- a/drivers/staging/iio/ring_generic.h
+++ b/drivers/staging/iio/ring_generic.h
@@ -68,11 +68,9 @@ struct iio_ring_access_funcs {
/**
* struct iio_ring_buffer - general ring buffer structure
* @dev: ring buffer device struct
- * @access_dev: system device struct for the chrdev
* @indio_dev: industrial I/O device structure
* @owner: module that owns the ring buffer (for ref counting)
* @id: unique id number
- * @access_id: device id number
* @length: [DEVICE] number of datums in ring
* @bytes_per_datum: [DEVICE] size of individual datum including timestamp
* @bpe: [DEVICE] size of individual channel value
@@ -92,11 +90,9 @@ struct iio_ring_access_funcs {
**/
struct iio_ring_buffer {
struct device dev;
- struct device access_dev;
struct iio_dev *indio_dev;
struct module *owner;
int id;
- int access_id;
int length;
int bytes_per_datum;
int bpe;
@@ -398,8 +394,6 @@ static inline void iio_put_ring_buffer(struct iio_ring_buffer *ring)

#define to_iio_ring_buffer(d) \
container_of(d, struct iio_ring_buffer, dev)
-#define access_dev_to_iio_ring_buffer(d) \
- container_of(d, struct iio_ring_buffer, access_dev)

/**
* iio_ring_buffer_register() - register the buffer with IIO core
@@ -416,6 +410,8 @@ int iio_ring_buffer_register_ex(struct iio_ring_buffer *ring, int id,
struct iio_chan_spec *channels,
int num_channels);

+void iio_ring_access_release(struct device *dev);
+
/**
* iio_ring_buffer_unregister() - unregister the buffer from IIO core
* @ring: the buffer to be unregistered
diff --git a/drivers/staging/iio/ring_sw.c b/drivers/staging/iio/ring_sw.c
index a429a3d..5fbf5ff 100644
--- a/drivers/staging/iio/ring_sw.c
+++ b/drivers/staging/iio/ring_sw.c
@@ -375,6 +375,7 @@ EXPORT_SYMBOL(iio_mark_update_needed_sw_rb);
static void iio_sw_rb_release(struct device *dev)
{
struct iio_ring_buffer *r = to_iio_ring_buffer(dev);
+ iio_ring_access_release(&r->dev);
kfree(iio_to_sw_ring(r));
}

@@ -416,9 +417,7 @@ struct iio_ring_buffer *iio_sw_rb_allocate(struct iio_dev *indio_dev)
iio_ring_buffer_init(buf, indio_dev);
__iio_init_sw_ring_buffer(ring);
buf->dev.type = &iio_sw_ring_type;
- device_initialize(&buf->dev);
buf->dev.parent = &indio_dev->dev;
- buf->dev.bus = &iio_bus_type;
dev_set_drvdata(&buf->dev, (void *)buf);

return buf;
--
1.7.3.4

2011-03-31 14:57:27

by Jonathan Cameron

[permalink] [raw]
Subject: [PATCH 06/21] staging:iio: remove legacy event chrdev for the buffers

Signed-off-by: Jonathan Cameron <[email protected]>
---
drivers/staging/iio/industrialio-ring.c | 58 +------------------------------
drivers/staging/iio/ring_generic.h | 25 -------------
drivers/staging/iio/ring_sw.c | 5 ---
3 files changed, 1 insertions(+), 87 deletions(-)

diff --git a/drivers/staging/iio/industrialio-ring.c b/drivers/staging/iio/industrialio-ring.c
index 464fec2..b503ea1 100644
--- a/drivers/staging/iio/industrialio-ring.c
+++ b/drivers/staging/iio/industrialio-ring.c
@@ -23,16 +23,6 @@
#include "iio.h"
#include "ring_generic.h"

-int iio_push_ring_event(struct iio_ring_buffer *ring_buf,
- int event_code,
- s64 timestamp)
-{
- return __iio_push_event(&ring_buf->ev_int,
- event_code,
- timestamp);
-}
-EXPORT_SYMBOL(iio_push_ring_event);
-
/**
* iio_ring_open() - chrdev file open for ring buffer access
*
@@ -116,43 +106,6 @@ static const struct file_operations iio_ring_fileops = {
.llseek = noop_llseek,
};

-/**
- * __iio_request_ring_buffer_event_chrdev() - allocate ring event chrdev
- * @buf: ring buffer whose event chrdev we are allocating
- * @id: id of this ring buffer (typically 0)
- * @owner: the module who owns the ring buffer (for ref counting)
- * @dev: device with which the chrdev is associated
- **/
-static inline int
-__iio_request_ring_buffer_event_chrdev(struct iio_ring_buffer *buf,
- int id,
- struct module *owner,
- struct device *dev)
-{
- int ret;
-
- snprintf(buf->ev_int._name, sizeof(buf->ev_int._name),
- "%s:event%d",
- dev_name(&buf->dev),
- id);
- ret = iio_setup_ev_int(&(buf->ev_int),
- buf->ev_int._name,
- owner,
- dev);
- if (ret)
- goto error_ret;
- return 0;
-
-error_ret:
- return ret;
-}
-
-static inline void
-__iio_free_ring_buffer_event_chrdev(struct iio_ring_buffer *buf)
-{
- iio_free_ev_int(&(buf->ev_int));
-}
-
static void iio_ring_access_release(struct device *dev)
{
struct iio_ring_buffer *buf
@@ -227,7 +180,6 @@ void iio_ring_buffer_init(struct iio_ring_buffer *ring,
if (ring->access.mark_param_change)
ring->access.mark_param_change(ring);
ring->indio_dev = dev_info;
- ring->ev_int.private = ring;
ring->access_handler.private = ring;
init_waitqueue_head(&ring->pollq);
}
@@ -399,19 +351,12 @@ int iio_ring_buffer_register_ex(struct iio_ring_buffer *ring, int id,
if (ret)
goto error_ret;

- ret = __iio_request_ring_buffer_event_chrdev(ring,
- 0,
- ring->owner,
- &ring->dev);
- if (ret)
- goto error_remove_device;
-
ret = __iio_request_ring_buffer_access_chrdev(ring,
0,
ring->owner);

if (ret)
- goto error_ret;
+ goto error_remove_device;

if (ring->scan_el_attrs) {
ret = sysfs_create_group(&ring->dev.kobj,
@@ -462,7 +407,6 @@ void iio_ring_buffer_unregister(struct iio_ring_buffer *ring)
{
__iio_ring_attr_cleanup(ring);
__iio_free_ring_buffer_access_chrdev(ring);
- __iio_free_ring_buffer_event_chrdev(ring);
device_del(&ring->dev);
}
EXPORT_SYMBOL(iio_ring_buffer_unregister);
diff --git a/drivers/staging/iio/ring_generic.h b/drivers/staging/iio/ring_generic.h
index 37b5eda..44c7543 100644
--- a/drivers/staging/iio/ring_generic.h
+++ b/drivers/staging/iio/ring_generic.h
@@ -16,29 +16,6 @@
struct iio_ring_buffer;

/**
- * iio_push_ring_event() - ring buffer specific push to event chrdev
- * @ring_buf: ring buffer that is the event source
- * @event_code: event indentification code
- * @timestamp: time of event
- **/
-int iio_push_ring_event(struct iio_ring_buffer *ring_buf,
- int event_code,
- s64 timestamp);
-/**
- * iio_push_or_escallate_ring_event() - escalate or add as appropriate
- * @ring_buf: ring buffer that is the event source
- * @event_code: event indentification code
- * @timestamp: time of event
- *
- * Typical usecase is to escalate a 50% ring full to 75% full if noone has yet
- * read the first event. Clearly the 50% full is no longer of interest in
- * typical use case.
- **/
-int iio_push_or_escallate_ring_event(struct iio_ring_buffer *ring_buf,
- int event_code,
- s64 timestamp);
-
-/**
* struct iio_ring_access_funcs - access functions for ring buffers.
* @mark_in_use: reference counting, typically to prevent module removal
* @unmark_in_use: reduce reference count when no longer using ring buffer
@@ -106,7 +83,6 @@ struct iio_ring_access_funcs {
* @scan_mask: [INTERN] bitmask used in masking scan mode elements
* @scan_timestamp: [INTERN] does the scan mode include a timestamp
* @access_handler: [INTERN] chrdev access handling
- * @ev_int: [INTERN] chrdev interface for the event chrdev
* @access: [DRIVER] ring access functions associated with the
* implementation.
* @preenable: [DRIVER] function to run prior to marking ring enabled
@@ -130,7 +106,6 @@ struct iio_ring_buffer {
u32 scan_mask;
bool scan_timestamp;
struct iio_handler access_handler;
- struct iio_event_interface ev_int;
struct iio_ring_access_funcs access;
int (*preenable)(struct iio_dev *);
int (*postenable)(struct iio_dev *);
diff --git a/drivers/staging/iio/ring_sw.c b/drivers/staging/iio/ring_sw.c
index 53c6774..a429a3d 100644
--- a/drivers/staging/iio/ring_sw.c
+++ b/drivers/staging/iio/ring_sw.c
@@ -69,7 +69,6 @@ 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 */
@@ -133,10 +132,6 @@ static int iio_store_to_sw_ring(struct iio_sw_ring_buffer *ring,
if (ring->half_p == ring->data + ring->buf.length*ring->buf.bytes_per_datum)
ring->half_p = ring->data;
if (ring->half_p == ring->read_p) {
- code = IIO_EVENT_CODE_RING_50_FULL;
- ret = __iio_push_event(&ring->buf.ev_int,
- code,
- timestamp);
ring->buf.stufftoread = true;
wake_up_interruptible(&ring->buf.pollq);
}
--
1.7.3.4

2011-03-31 14:58:01

by Jonathan Cameron

[permalink] [raw]
Subject: [PATCH 01/21] staging:iio: allow channels to be set up using a table of iio_channel_spec structures.

V2: Various fixes - some thanks to Arnd.

Signed-off-by: Jonathan Cameron <[email protected]>
---
drivers/staging/iio/chrdev.h | 3 +
drivers/staging/iio/iio.h | 158 ++++++++
drivers/staging/iio/industrialio-core.c | 593 +++++++++++++++++++++++++++++--
drivers/staging/iio/industrialio-ring.c | 188 ++++++++++-
drivers/staging/iio/ring_generic.h | 20 +
drivers/staging/iio/sysfs.h | 31 ++-
6 files changed, 953 insertions(+), 40 deletions(-)

diff --git a/drivers/staging/iio/chrdev.h b/drivers/staging/iio/chrdev.h
index 98d1a2c..95b439e 100644
--- a/drivers/staging/iio/chrdev.h
+++ b/drivers/staging/iio/chrdev.h
@@ -91,6 +91,9 @@ struct iio_event_interface {
void *private;
char _name[35];
char _attrname[20];
+
+ struct list_head event_attr_list;
+ struct list_head dev_attr_list;
};

/**
diff --git a/drivers/staging/iio/iio.h b/drivers/staging/iio/iio.h
index bf84799..578d078 100644
--- a/drivers/staging/iio/iio.h
+++ b/drivers/staging/iio/iio.h
@@ -26,6 +26,139 @@

struct iio_dev;

+/* naughty temporary hack to match these against the event version
+ - need to flattern these together */
+enum iio_chan_type {
+ /* Need this here for now to support buffer events
+ * set to 0 to avoid changes to ring_generic.c */
+ IIO_BUFFER = 0,
+
+ /* real channel types */
+ IIO_IN,
+ IIO_ACCEL,
+ IIO_IN_DIFF,
+ IIO_GYRO,
+ IIO_MAGN,
+ IIO_LIGHT,
+ IIO_PROXIMITY,
+ IIO_TIMESTAMP,
+};
+
+/* Could add the raw attributes as well - allowing buffer only devices */
+enum iio_chan_info_enum {
+ IIO_CHAN_INFO_SCALE_SHARED,
+ IIO_CHAN_INFO_SCALE_SEPARATE,
+ IIO_CHAN_INFO_OFFSET_SHARED,
+ IIO_CHAN_INFO_OFFSET_SEPARATE,
+ IIO_CHAN_INFO_CALIBSCALE_SHARED,
+ IIO_CHAN_INFO_CALIBSCALE_SEPARATE,
+ IIO_CHAN_INFO_CALIBBIAS_SHARED,
+ IIO_CHAN_INFO_CALIBBIAS_SEPARATE
+};
+
+/**
+ * struct iio_chan_spec - specification of a single channel
+ * @type: what type of measurement is the channel making
+ * @channel: what number or name do we wish to asign the channel
+ * @channel2: if there is a second number for a differential
+ * channel then this is it.
+ * @address: driver specific identifier.
+ * @scan_index: monotonic index to give ordering in scans when read
+ * from a buffer.
+ * @scan_type: sign is 's' or 'u' to specify signed or unsigned
+ * realbits is the number of valid bits of data
+ * storage bits is realbits + padding
+ * shift tells you how much to shift right before masking
+ * out realbits.
+ * @info_mask: what information is to be exported about this channel.
+ * This includes calibbias, scale etc.
+ * @event_mask: what events can this channel produce.
+ * @shared_handler: single handler for the events registered.
+ */
+struct iio_chan_spec {
+ enum iio_chan_type type;
+ int channel;
+ int channel2;
+ unsigned long address;
+ int scan_index;
+ struct {
+ char sign;
+ u8 realbits;
+ u8 storagebits;
+ u8 shift;
+ } scan_type;
+ const long info_mask;
+ const long event_mask;
+ /* TODO: investigate pushing shared event handling out to
+ * the drivers */
+ struct iio_event_handler_list *shared_handler;
+};
+/* Meant for internal use only */
+void __iio_device_attr_deinit(struct device_attribute *dev_attr);
+int __iio_device_attr_init(struct device_attribute *dev_attr,
+ const char *postfix,
+ struct iio_chan_spec *chan,
+ ssize_t (*readfunc)(struct device *dev,
+ struct device_attribute *attr,
+ char *buf),
+ ssize_t (*writefunc)(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t len),
+ bool generic);
+#define IIO_ST(si, rb, sb, sh) \
+ { .sign = si, .realbits = rb, .storagebits = sb, .shift = sh }
+
+#define IIO_CHAN(_type, _chan, _inf_mask, _address, _si, _stype) \
+ { .type = _type, .channel = _chan, .info_mask = _inf_mask, \
+ .address = _address, \
+ .scan_index = _si, .scan_type = _stype }
+
+#define IIO_CHAN_EV(_type, _chan, _inf_mask, _address, _si, \
+ _stype, _event_mask, _shared_h) \
+ { .type = _type, \
+ .channel = _chan, \
+ .info_mask = _inf_mask, \
+ .address = _address, \
+ .scan_index = _si, .scan_type = _stype, \
+ .event_mask = _event_mask, \
+ .shared_handler = _shared_h }
+
+#define IIO_CHAN_COMPOUND(_type, _chan1, _chan2, _inf_mask, \
+ _address, _si, _stype) \
+ { .type = _type, .channel = _chan1, .channel2 = _chan2, \
+ .info_mask = _inf_mask, \
+ .address = _address, \
+ .scan_index = _si, .scan_type = _stype }
+
+#define IIO_CHAN_COMPOUND_EV(_type, _chan1, _chan2, _inf_mask, \
+ _address, _si, _stype, _event_mask, _shared_h) \
+ { .type = _type, \
+ .channel = _chan1, .channel2 = _chan2, \
+ .info_mask = _inf_mask, \
+ .address = _address, \
+ .scan_index = _si, .scan_type = _stype, \
+ .event_mask = _event_mask, \
+ .shared_handler = _shared_h}
+
+#define IIO_CHAN_SOFT_TIMESTAMP(_si) \
+ { .type = IIO_TIMESTAMP, .channel = -1, \
+ .scan_index = _si, .scan_type = IIO_ST('s', 64, 64, 0) }
+
+int __iio_add_chan_devattr(const char *postfix,
+ const char *group,
+ struct iio_chan_spec *chan,
+ ssize_t (*func)(struct device *dev,
+ struct device_attribute *attr,
+ char *buf),
+ ssize_t (*writefunc)(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t len),
+ int mask,
+ bool generic,
+ struct device *dev,
+ struct list_head *attr_list);
/**
* iio_get_time_ns() - utility function to get a time stamp for events etc
**/
@@ -116,6 +249,31 @@ struct iio_dev {
u32 *available_scan_masks;
struct iio_trigger *trig;
struct iio_poll_func *pollfunc;
+
+ struct iio_chan_spec *channels;
+ int num_channels;
+ struct list_head channel_attr_list;
+
+ char *name; /*device name - IMPLEMENT */
+ int (*read_raw)(struct iio_dev *indio_dev,
+ struct iio_chan_spec *chan,
+ int *val,
+ long mask);
+
+ int (*read_event_config)(struct iio_dev *indio_dev,
+ int event_code);
+
+ int (*write_event_config)(struct iio_dev *indio_dev,
+ int event_code,
+ struct iio_event_handler_list *listel,
+ int state);
+
+ int (*read_event_value)(struct iio_dev *indio_dev,
+ int event_code,
+ int *val);
+ int (*write_event_value)(struct iio_dev *indio_dev,
+ int event_code,
+ int val);
};

/**
diff --git a/drivers/staging/iio/industrialio-core.c b/drivers/staging/iio/industrialio-core.c
index 54a61a3..809588f 100644
--- a/drivers/staging/iio/industrialio-core.c
+++ b/drivers/staging/iio/industrialio-core.c
@@ -44,6 +44,28 @@ struct bus_type iio_bus_type = {
};
EXPORT_SYMBOL(iio_bus_type);

+static const char * const iio_chan_type_name_spec_shared[] = {
+ [IIO_TIMESTAMP] = "timestamp",
+ [IIO_ACCEL] = "accel",
+ [IIO_IN] = "in",
+ [IIO_IN_DIFF] = "in-in",
+};
+
+static const char * const iio_chan_type_name_spec[] = {
+ [IIO_TIMESTAMP] = "timestamp",
+ [IIO_ACCEL] = "accel_%c",
+ [IIO_IN] = "in%d",
+ [IIO_IN_DIFF] = "in%d-in%d",
+};
+
+/* relies on pairs of these shared then separate */
+static const char * const iio_chan_info_postfix[] = {
+ [IIO_CHAN_INFO_SCALE_SHARED/2] = "scale",
+ [IIO_CHAN_INFO_OFFSET_SHARED/2] = "offset",
+ [IIO_CHAN_INFO_CALIBSCALE_SHARED/2] = "calibscale",
+ [IIO_CHAN_INFO_CALIBBIAS_SHARED/2] = "calibbias",
+};
+
void __iio_change_event(struct iio_detected_event_list *ev,
int ev_code,
s64 timestamp)
@@ -488,24 +510,267 @@ static void __exit iio_exit(void)
bus_unregister(&iio_bus_type);
}

-static int iio_device_register_sysfs(struct iio_dev *dev_info)
+static ssize_t iio_read_single_channel(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
{
- int ret = 0;
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+ int val, ret;

- ret = sysfs_create_group(&dev_info->dev.kobj, dev_info->attrs);
- if (ret) {
- dev_err(dev_info->dev.parent,
- "Failed to register sysfs hooks\n");
+ ret = indio_dev->read_raw(indio_dev, this_attr->c, &val, 0);
+
+ return ret < 0 ? ret : sprintf(buf, "%d\n", val);
+}
+
+static ssize_t iio_read_channel_info(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);
+ int val;
+ int ret = indio_dev->read_raw(indio_dev, this_attr->c,
+ &val, this_attr->address);
+
+ if (ret < 0)
+ return ret;
+ else /* may want a longer type for val */
+ return sprintf(buf, "%d.%06ld\n", val/1000000,
+ abs(val)%1000000);
+}
+
+static ssize_t iio_write_channel_info(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t len)
+{
+ /* To be implemented - need to decide if we pass a number
+ on to the drivers or a string and let them do the handling
+ */
+ return len;
+}
+
+int __iio_device_attr_init(struct device_attribute *dev_attr,
+ const char *postfix,
+ struct iio_chan_spec *chan,
+ ssize_t (*readfunc)(struct device *dev,
+ struct device_attribute *attr,
+ char *buf),
+ ssize_t (*writefunc)(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t len),
+ bool generic)
+{
+ int ret;
+ char *name_format;
+ sysfs_attr_init(&dev_attr->attr);
+ if (generic)
+ name_format
+ = kasprintf(GFP_KERNEL, "%s_%s",
+ iio_chan_type_name_spec_shared[chan->type],
+ postfix);
+ else
+ name_format = kasprintf(GFP_KERNEL, "%s_%s",
+ iio_chan_type_name_spec[chan->type],
+ postfix);
+ if (name_format == NULL) {
+ ret = -ENOMEM;
+ goto error_ret;
+ }
+
+ dev_attr->attr.name = kasprintf(GFP_KERNEL,
+ name_format,
+ chan->channel,
+ chan->channel2);
+ if (dev_attr->attr.name == NULL) {
+ ret = -ENOMEM;
+ goto error_free_name_format;
+ }
+
+ if (readfunc) {
+ dev_attr->attr.mode |= S_IRUGO;
+ dev_attr->show = readfunc;
+ }
+
+ if (writefunc) {
+ dev_attr->attr.mode |= S_IWUSR;
+ dev_attr->store = writefunc;
+ }
+ kfree(name_format);
+
+ return 0;
+
+error_free_name_format:
+ kfree(name_format);
+error_ret:
+ return ret;
+}
+
+void __iio_device_attr_deinit(struct device_attribute *dev_attr)
+{
+ kfree(dev_attr->attr.name);
+}
+
+int __iio_add_chan_devattr(const char *postfix,
+ const char *group,
+ struct iio_chan_spec *chan,
+ ssize_t (*readfunc)(struct device *dev,
+ struct device_attribute *attr,
+ char *buf),
+ ssize_t (*writefunc)(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t len),
+ int mask,
+ bool generic,
+ struct device *dev,
+ struct list_head *attr_list)
+{
+ int ret;
+ struct iio_dev_attr *iio_attr, *t;
+
+ iio_attr = kzalloc(sizeof *iio_attr, GFP_KERNEL);
+ if (iio_attr == NULL) {
+ ret = -ENOMEM;
goto error_ret;
}
+ ret = __iio_device_attr_init(&iio_attr->dev_attr,
+ postfix, chan,
+ readfunc, writefunc, generic);
+ if (ret)
+ goto error_iio_dev_attr_free;
+ iio_attr->c = chan;
+ iio_attr->address = mask;
+ list_for_each_entry(t, attr_list, l)
+ if (strcmp(t->dev_attr.attr.name,
+ iio_attr->dev_attr.attr.name) == 0) {
+ if (!generic)
+ dev_err(dev, "tried to double register : %s\n",
+ t->dev_attr.attr.name);
+ ret = -EBUSY;
+ goto error_device_attr_deinit;
+ }
+
+ ret = sysfs_add_file_to_group(&dev->kobj,
+ &iio_attr->dev_attr.attr, group);
+ if (ret < 0)
+ goto error_device_attr_deinit;
+
+ list_add(&iio_attr->l, attr_list);
+
+ return 0;

+error_device_attr_deinit:
+ __iio_device_attr_deinit(&iio_attr->dev_attr);
+error_iio_dev_attr_free:
+ kfree(iio_attr);
error_ret:
return ret;
}

+static int iio_device_add_channel_sysfs(struct iio_dev *dev_info,
+ struct iio_chan_spec *chan)
+{
+ int ret, i;
+
+
+ if (chan->channel < 0)
+ return 0;
+
+ ret = __iio_add_chan_devattr("raw", NULL, chan,
+ &iio_read_single_channel,
+ NULL,
+ 0,
+ 0,
+ &dev_info->dev,
+ &dev_info->channel_attr_list);
+ if (ret)
+ goto error_ret;
+
+ for_each_set_bit(i, &chan->info_mask, sizeof(long)*8) {
+ ret = __iio_add_chan_devattr(iio_chan_info_postfix[i/2],
+ NULL, chan,
+ &iio_read_channel_info,
+ &iio_write_channel_info,
+ (1 << i),
+ !(i%2),
+ &dev_info->dev,
+ &dev_info->channel_attr_list);
+ if (ret == -EBUSY && (i%2 == 0)) {
+ ret = 0;
+ continue;
+ }
+ if (ret < 0)
+ goto error_ret;
+ }
+error_ret:
+ return ret;
+}
+
+static void iio_device_remove_and_free_read_attr(struct iio_dev *dev_info,
+ struct iio_dev_attr *p)
+{
+ sysfs_remove_file_from_group(&dev_info->dev.kobj,
+ &p->dev_attr.attr, NULL);
+ kfree(p->dev_attr.attr.name);
+ kfree(p);
+}
+
+static int iio_device_register_sysfs(struct iio_dev *dev_info)
+{
+ int i, ret = 0;
+ struct iio_dev_attr *p, *n;
+
+ if (dev_info->attrs) {
+ ret = sysfs_create_group(&dev_info->dev.kobj, dev_info->attrs);
+ if (ret) {
+ dev_err(dev_info->dev.parent,
+ "Failed to register sysfs hooks\n");
+ goto error_ret;
+ }
+ }
+
+ /*
+ * New channel registration method - relies on the fact a group does
+ * not need to be initialized if it is name is NULL.
+ */
+ INIT_LIST_HEAD(&dev_info->channel_attr_list);
+ if (dev_info->channels)
+ for (i = 0; i < dev_info->num_channels; i++) {
+ ret = iio_device_add_channel_sysfs(dev_info,
+ &dev_info
+ ->channels[i]);
+ if (ret < 0)
+ goto error_clear_attrs;
+ }
+
+ return 0;
+error_clear_attrs:
+ list_for_each_entry_safe(p, n,
+ &dev_info->channel_attr_list, l) {
+ list_del(&p->l);
+ iio_device_remove_and_free_read_attr(dev_info, p);
+ }
+ if (dev_info->attrs)
+ sysfs_remove_group(&dev_info->dev.kobj, dev_info->attrs);
+error_ret:
+ return ret;
+
+}
+
static void iio_device_unregister_sysfs(struct iio_dev *dev_info)
{
- sysfs_remove_group(&dev_info->dev.kobj, dev_info->attrs);
+
+ struct iio_dev_attr *p, *n;
+ list_for_each_entry_safe(p, n, &dev_info->channel_attr_list, l) {
+ list_del(&p->l);
+ iio_device_remove_and_free_read_attr(dev_info, p);
+ }
+
+ if (dev_info->attrs)
+ sysfs_remove_group(&dev_info->dev.kobj, dev_info->attrs);
}

/* Return a negative errno on failure */
@@ -552,8 +817,256 @@ static void iio_device_unregister_id(struct iio_dev *dev_info)
iio_free_ida_val(&iio_ida, dev_info->id);
}

+static const char * const iio_ev_type_text[] = {
+ [IIO_EV_TYPE_THRESH] = "thresh",
+ [IIO_EV_TYPE_MAG] = "mag",
+ [IIO_EV_TYPE_ROC] = "roc"
+};
+
+static const char * const iio_ev_dir_text[] = {
+ [IIO_EV_DIR_EITHER] = "either",
+ [IIO_EV_DIR_RISING] = "rising",
+ [IIO_EV_DIR_FALLING] = "falling"
+};
+
+static ssize_t iio_ev_state_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t len)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct iio_event_attr *this_attr = to_iio_event_attr(attr);
+ int ret;
+ unsigned long val;
+ ret = strict_strtoul(buf, 10, &val);
+ if (ret || val < 0 || val > 1)
+ return -EINVAL;
+
+ ret = indio_dev->write_event_config(indio_dev, this_attr->mask,
+ this_attr->listel,
+ val);
+ return (ret < 0) ? ret : len;
+}
+
+static ssize_t iio_ev_state_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct iio_event_attr *this_attr = to_iio_event_attr(attr);
+ int val = indio_dev->read_event_config(indio_dev, this_attr->mask);
+
+ if (val < 0)
+ return val;
+ else
+ return sprintf(buf, "%d\n", val);
+}
+
+static ssize_t iio_ev_value_show(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);
+ int val, ret;
+
+ ret = indio_dev->read_event_value(indio_dev,
+ this_attr->address, &val);
+ if (ret < 0)
+ return ret;
+
+ return sprintf(buf, "%d\n", val);
+}
+
+static ssize_t iio_ev_value_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t len)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+ unsigned long val;
+ int ret;
+
+ ret = strict_strtoul(buf, 10, &val);
+ if (ret)
+ return ret;
+
+ ret = indio_dev->write_event_value(indio_dev, this_attr->address,
+ val);
+ if (ret < 0)
+ return ret;
+
+ return len;
+}
+
+static int __iio_add_chan_event_attr(const char *postfix,
+ const char *group,
+ struct iio_chan_spec *chan,
+ unsigned int mask,
+ struct device *dev,
+ struct list_head *attr_list)
+{
+ char *name_format;
+ int ret;
+ struct iio_event_attr *iio_ev_attr;
+
+ iio_ev_attr = kzalloc(sizeof *iio_ev_attr, GFP_KERNEL);
+ if (iio_ev_attr == NULL) {
+ ret = -ENOMEM;
+ goto error_ret;
+ }
+
+ sysfs_attr_init(&iio_ev_attr->dev_attr.attr);
+
+ name_format = kasprintf(GFP_KERNEL, "%s_%s",
+ iio_chan_type_name_spec[chan->type],
+ postfix);
+ if (name_format == NULL) {
+ ret = -ENOMEM;
+ goto error_free_attr;
+ }
+
+ iio_ev_attr->dev_attr.attr.name = kasprintf(GFP_KERNEL,
+ name_format,
+ chan->channel,
+ chan->channel2);
+ if (iio_ev_attr->dev_attr.attr.name == NULL) {
+ ret = -ENOMEM;
+ goto error_free_name_format;
+ }
+
+ iio_ev_attr->dev_attr.attr.mode = S_IRUGO | S_IWUSR;
+ iio_ev_attr->dev_attr.show = &iio_ev_state_show;
+ iio_ev_attr->dev_attr.store = &iio_ev_state_store;
+ iio_ev_attr->mask = mask;
+ iio_ev_attr->listel = chan->shared_handler;
+ ret = sysfs_add_file_to_group(&dev->kobj,
+ &iio_ev_attr->dev_attr.attr,
+ group);
+ if (ret < 0)
+ goto error_free_name;
+ list_add(&iio_ev_attr->l, attr_list);
+ kfree(name_format);
+ return 0;
+
+error_free_name:
+ kfree(iio_ev_attr->dev_attr.attr.name);
+error_free_name_format:
+ kfree(name_format);
+error_free_attr:
+ kfree(iio_ev_attr);
+error_ret:
+ return ret;
+}
+
+
+static int iio_device_add_event_sysfs(struct iio_dev *dev_info,
+ struct iio_chan_spec *chan)
+{
+
+ int ret = 0, i, mask;
+ char *postfix;
+ if (!chan->event_mask)
+ return 0;
+
+ for_each_set_bit(i, &chan->event_mask, sizeof(chan->event_mask)*8) {
+ postfix = kasprintf(GFP_KERNEL, "%s_%s_en",
+ iio_ev_type_text[i/IIO_EV_TYPE_MAX],
+ iio_ev_dir_text[i%IIO_EV_TYPE_MAX]);
+ if (postfix == NULL) {
+ ret = -ENOMEM;
+ goto error_ret;
+ }
+ switch (chan->type) {
+ /* Switch this to a table at some point */
+ case IIO_IN:
+
+ mask = IIO_UNMOD_EVENT_CODE(chan->type, chan->channel,
+ i/IIO_EV_TYPE_MAX,
+ i%IIO_EV_TYPE_MAX);
+ break;
+ case IIO_ACCEL:
+ mask = IIO_MOD_EVENT_CODE(chan->type, 0, chan->channel,
+ i/IIO_EV_TYPE_MAX,
+ i%IIO_EV_TYPE_MAX);
+ break;
+ default:
+ printk("currently unhandled\n");
+ }
+ ret = __iio_add_chan_event_attr(postfix,
+ NULL,
+ chan,
+ mask,
+ /*HACK. - limits us to one
+ event interface - fix by
+ extending the bitmask - but
+ how far*/
+ &dev_info->event_interfaces[0]
+ .dev,
+ &dev_info->event_interfaces[0].
+ event_attr_list);
+ kfree(postfix);
+ if (ret)
+ goto error_ret;
+
+ postfix = kasprintf(GFP_KERNEL, "%s_%s_value",
+ iio_ev_type_text[i/IIO_EV_TYPE_MAX],
+ iio_ev_dir_text[i%IIO_EV_TYPE_MAX]);
+ if (postfix == NULL) {
+ ret = -ENOMEM;
+ goto error_ret;
+ }
+ ret = __iio_add_chan_devattr(postfix, NULL, chan,
+ iio_ev_value_show,
+ iio_ev_value_store,
+ mask,
+ 0,
+ &dev_info->event_interfaces[0]
+ .dev,
+ &dev_info->event_interfaces[0]
+ .dev_attr_list);
+ kfree(postfix);
+ if (ret)
+ goto error_ret;
+
+ }
+
+error_ret:
+ return ret;
+}
+
+static inline void __iio_remove_all_event_sysfs(struct iio_dev *dev_info,
+ const char *groupname,
+ int num)
+{
+ struct iio_dev_attr *p, *n;
+ struct iio_event_attr *q, *m;
+ list_for_each_entry_safe(p, n,
+ &dev_info->event_interfaces[num].
+ dev_attr_list, l) {
+ sysfs_remove_file_from_group(&dev_info
+ ->event_interfaces[num].dev.kobj,
+ &p->dev_attr.attr,
+ groupname);
+ kfree(p->dev_attr.attr.name);
+ kfree(p);
+ }
+ list_for_each_entry_safe(q, m,
+ &dev_info->event_interfaces[num].
+ event_attr_list, l) {
+ sysfs_remove_file_from_group(&dev_info
+ ->event_interfaces[num].dev.kobj,
+ &q->dev_attr.attr,
+ groupname);
+ kfree(q->dev_attr.attr.name);
+ kfree(q);
+ }
+}
+
static inline int __iio_add_event_config_attrs(struct iio_dev *dev_info, int i)
{
+ int j;
int ret;
/*p for adding, q for removing */
struct attribute **attrp, **attrq;
@@ -561,23 +1074,42 @@ static inline int __iio_add_event_config_attrs(struct iio_dev *dev_info, int i)
if (dev_info->event_conf_attrs && dev_info->event_conf_attrs[i].attrs) {
attrp = dev_info->event_conf_attrs[i].attrs;
while (*attrp) {
- ret = sysfs_add_file_to_group(&dev_info->dev.kobj,
+ ret = sysfs_add_file_to_group(&dev_info
+ ->event_interfaces[0]
+ .dev.kobj,
*attrp,
- dev_info
- ->event_attrs[i].name);
+ NULL);
if (ret)
goto error_ret;
attrp++;
}
}
+ INIT_LIST_HEAD(&dev_info->event_interfaces[0].event_attr_list);
+ INIT_LIST_HEAD(&dev_info->event_interfaces[0].dev_attr_list);
+ /* Dynically created from the channels array */
+ if (dev_info->channels) {
+ for (j = 0; j < dev_info->num_channels; j++) {
+ ret = iio_device_add_event_sysfs(dev_info,
+ &dev_info
+ ->channels[j]);
+ if (ret)
+ goto error_clear_attrs;
+ }
+ }
return 0;

+error_clear_attrs:
+ __iio_remove_all_event_sysfs(dev_info,
+ NULL,
+ i);
error_ret:
attrq = dev_info->event_conf_attrs[i].attrs;
while (attrq != attrp) {
- sysfs_remove_file_from_group(&dev_info->dev.kobj,
- *attrq,
- dev_info->event_attrs[i].name);
+ sysfs_remove_file_from_group(&dev_info
+ ->event_interfaces[0]
+ .dev.kobj,
+ *attrq,
+ NULL);
attrq++;
}

@@ -588,15 +1120,18 @@ static inline int __iio_remove_event_config_attrs(struct iio_dev *dev_info,
int i)
{
struct attribute **attrq;
-
+ __iio_remove_all_event_sysfs(dev_info,
+ NULL,
+ i);
if (dev_info->event_conf_attrs
&& dev_info->event_conf_attrs[i].attrs) {
attrq = dev_info->event_conf_attrs[i].attrs;
while (*attrq) {
- sysfs_remove_file_from_group(&dev_info->dev.kobj,
+ sysfs_remove_file_from_group(&dev_info
+ ->event_interfaces[0]
+ .dev.kobj,
*attrq,
- dev_info
- ->event_attrs[i].name);
+ NULL);
attrq++;
}
}
@@ -650,10 +1185,12 @@ static int iio_device_register_eventset(struct iio_dev *dev_info)

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 (dev_info->event_attrs != NULL)
+ ret = sysfs_create_group(&dev_info
+ ->event_interfaces[i]
+ .dev.kobj,
+ &dev_info->event_attrs[i]);

if (ret) {
dev_err(&dev_info->dev,
@@ -676,7 +1213,8 @@ 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
+ if (dev_info->event_attrs != NULL)
+ sysfs_remove_group(&dev_info
->event_interfaces[j].dev.kobj,
&dev_info->event_attrs[j]);
error_free_setup_ev_ints:
@@ -696,10 +1234,13 @@ 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
- ->event_interfaces[i].dev.kobj,
- &dev_info->event_attrs[i]);
+ for (i = 0; i < dev_info->num_interrupt_lines; i++) {
+ __iio_remove_event_config_attrs(dev_info, i);
+ if (dev_info->event_attrs != NULL)
+ sysfs_remove_group(&dev_info
+ ->event_interfaces[i].dev.kobj,
+ NULL);
+ }

for (i = 0; i < dev_info->num_interrupt_lines; i++)
iio_free_ev_int(&dev_info->event_interfaces[i]);
diff --git a/drivers/staging/iio/industrialio-ring.c b/drivers/staging/iio/industrialio-ring.c
index 3f6bee0..1dfbc6e 100644
--- a/drivers/staging/iio/industrialio-ring.c
+++ b/drivers/staging/iio/industrialio-ring.c
@@ -233,9 +233,162 @@ 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 id)
+static ssize_t iio_show_scan_index(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+ return sprintf(buf, "%u\n", this_attr->c->scan_index);
+}
+
+static ssize_t iio_show_fixed_type(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+ return sprintf(buf, "%c%d/%d>>%u\n",
+ this_attr->c->scan_type.sign,
+ this_attr->c->scan_type.realbits,
+ this_attr->c->scan_type.storagebits,
+ this_attr->c->scan_type.shift);
+}
+
+static int __iio_add_chan_scan_elattr(const char *postfix,
+ const char *group,
+ struct iio_chan_spec *chan,
+ struct device *dev,
+ struct list_head *attr_list)
{
int ret;
+ struct iio_scan_el *scan_el;
+
+ scan_el = kzalloc(sizeof *scan_el, GFP_KERNEL);
+ if (scan_el == NULL) {
+ ret = -ENOMEM;
+ goto error_ret;
+ }
+ if (chan->type != IIO_TIMESTAMP)
+ ret = __iio_device_attr_init(&scan_el->dev_attr, postfix, chan,
+ iio_scan_el_show,
+ iio_scan_el_store, 0);
+ else /*
+ * Timestamp handled separately because it simplifies a lot of
+ * drivers by ensuring they don't have to know its magic index
+ */
+ ret = __iio_device_attr_init(&scan_el->dev_attr, postfix, chan,
+ iio_scan_el_ts_show,
+ iio_scan_el_ts_store, 0);
+ if (ret)
+ goto error_free_scan_el;
+
+ scan_el->number = chan->scan_index;
+
+ ret = sysfs_add_file_to_group(&dev->kobj,
+ &scan_el->dev_attr.attr,
+ group);
+ if (ret < 0)
+ goto error_device_attr_deinit;
+
+ list_add(&scan_el->l, attr_list);
+
+ return 0;
+error_device_attr_deinit:
+ __iio_device_attr_deinit(&scan_el->dev_attr);
+error_free_scan_el:
+ kfree(scan_el);
+error_ret:
+ return ret;
+}
+
+static int iio_ring_add_channel_sysfs(struct iio_ring_buffer *ring,
+ struct iio_chan_spec *chan)
+{
+ int ret;
+
+ ret = __iio_add_chan_devattr("index", "scan_elements",
+ chan,
+ &iio_show_scan_index,
+ NULL,
+ 0,
+ 0,
+ &ring->dev,
+ &ring->scan_el_dev_attr_list);
+ if (ret)
+ goto error_ret;
+
+ ret = __iio_add_chan_devattr("type", "scan_elements",
+ chan,
+ &iio_show_fixed_type,
+ NULL,
+ 0,
+ 0,
+ &ring->dev,
+ &ring->scan_el_dev_attr_list);
+
+ if (ret)
+ goto error_ret;
+
+ ret = __iio_add_chan_scan_elattr("en", "scan_elements",
+ chan, &ring->dev,
+ &ring->scan_el_en_attr_list);
+
+error_ret:
+ return ret;
+}
+
+static void iio_ring_remove_and_free_scan_el_attr(struct iio_ring_buffer *ring,
+ struct iio_scan_el *p)
+{
+ sysfs_remove_file_from_group(&ring->dev.kobj,
+ &p->dev_attr.attr, "scan_elements");
+ kfree(p->dev_attr.attr.name);
+ kfree(p);
+}
+
+static void iio_ring_remove_and_free_scan_dev_attr(struct iio_ring_buffer *ring,
+ struct iio_dev_attr *p)
+{
+ sysfs_remove_file_from_group(&ring->dev.kobj,
+ &p->dev_attr.attr, "scan_elements");
+ kfree(p->dev_attr.attr.name);
+ kfree(p);
+}
+
+static struct attribute *iio_scan_el_dummy_attrs[] = {
+ NULL
+};
+
+static struct attribute_group iio_scan_el_dummy_group = {
+ .name = "scan_elements",
+ .attrs = iio_scan_el_dummy_attrs
+};
+
+static void __iio_ring_attr_cleanup(struct iio_ring_buffer *ring)
+{
+ struct iio_dev_attr *p, *n;
+ struct iio_scan_el *q, *m;
+ int anydynamic = !(list_empty(&ring->scan_el_dev_attr_list) &&
+ list_empty(&ring->scan_el_en_attr_list));
+ list_for_each_entry_safe(p, n,
+ &ring->scan_el_dev_attr_list, l)
+ iio_ring_remove_and_free_scan_dev_attr(ring, p);
+ list_for_each_entry_safe(q, m,
+ &ring->scan_el_en_attr_list, l)
+ iio_ring_remove_and_free_scan_el_attr(ring, q);
+
+ if (ring->scan_el_attrs)
+ sysfs_remove_group(&ring->dev.kobj,
+ ring->scan_el_attrs);
+ else if (anydynamic)
+ sysfs_remove_group(&ring->dev.kobj,
+ &iio_scan_el_dummy_group);
+}
+
+int iio_ring_buffer_register_ex(struct iio_ring_buffer *ring, int id,
+ struct iio_chan_spec *channels,
+ int num_channels)
+{
+ int ret, i;

ring->id = id;

@@ -268,9 +421,28 @@ int iio_ring_buffer_register(struct iio_ring_buffer *ring, int id)
"Failed to add sysfs scan elements\n");
goto error_free_ring_buffer_event_chrdev;
}
+ } else if (channels) {
+ ret = sysfs_create_group(&ring->dev.kobj,
+ &iio_scan_el_dummy_group);
+ if (ret)
+ goto error_free_ring_buffer_event_chrdev;
}

- return ret;
+
+ INIT_LIST_HEAD(&ring->scan_el_dev_attr_list);
+ INIT_LIST_HEAD(&ring->scan_el_en_attr_list);
+ if (channels) {
+ /* new magic */
+ for (i = 0; i < num_channels; i++) {
+ ret = iio_ring_add_channel_sysfs(ring, &channels[i]);
+ if (ret < 0)
+ goto error_cleanup_dynamic;
+ }
+ }
+
+ return 0;
+error_cleanup_dynamic:
+ __iio_ring_attr_cleanup(ring);
error_free_ring_buffer_event_chrdev:
__iio_free_ring_buffer_event_chrdev(ring);
error_remove_device:
@@ -278,14 +450,17 @@ error_remove_device:
error_ret:
return ret;
}
+EXPORT_SYMBOL(iio_ring_buffer_register_ex);
+
+int iio_ring_buffer_register(struct iio_ring_buffer *ring, int id)
+{
+ return iio_ring_buffer_register_ex(ring, id, NULL, 0);
+}
EXPORT_SYMBOL(iio_ring_buffer_register);

void iio_ring_buffer_unregister(struct iio_ring_buffer *ring)
{
- if (ring->scan_el_attrs)
- sysfs_remove_group(&ring->dev.kobj,
- ring->scan_el_attrs);
-
+ __iio_ring_attr_cleanup(ring);
__iio_free_ring_buffer_access_chrdev(ring);
__iio_free_ring_buffer_event_chrdev(ring);
device_del(&ring->dev);
@@ -540,4 +715,3 @@ error_ret:
return ret ? ret : len;
}
EXPORT_SYMBOL(iio_scan_el_ts_store);
-
diff --git a/drivers/staging/iio/ring_generic.h b/drivers/staging/iio/ring_generic.h
index bae2d6d..6c7e073 100644
--- a/drivers/staging/iio/ring_generic.h
+++ b/drivers/staging/iio/ring_generic.h
@@ -140,6 +140,8 @@ struct iio_ring_buffer {
int (*predisable)(struct iio_dev *);
int (*postdisable)(struct iio_dev *);

+ struct list_head scan_el_dev_attr_list;
+ struct list_head scan_el_en_attr_list;
};

/**
@@ -177,6 +179,7 @@ struct iio_scan_el {
struct device_attribute dev_attr;
unsigned int number;
unsigned int label;
+ struct list_head l;

int (*set_state)(struct iio_scan_el *scanel,
struct iio_dev *dev_info,
@@ -430,6 +433,14 @@ static inline void iio_put_ring_buffer(struct iio_ring_buffer *ring)
**/
int iio_ring_buffer_register(struct iio_ring_buffer *ring, int id);

+/** iio_ring_buffer_register_ex() - register the buffer with IIO core
+ * @ring: the buffer to be registered
+ * @id: the id of the buffer (typically 0)
+ **/
+int iio_ring_buffer_register_ex(struct iio_ring_buffer *ring, int id,
+ struct iio_chan_spec *channels,
+ int num_channels);
+
/**
* iio_ring_buffer_unregister() - unregister the buffer from IIO core
* @ring: the buffer to be unregistered
@@ -481,6 +492,15 @@ static inline int iio_ring_buffer_register(struct iio_ring_buffer *ring, int id)
{
return 0;
};
+
+static inline int iio_ring_buffer_register_ex(struct iio_ring_buffer *ring,
+ int id,
+ struct iio_chan_spec *channels,
+ int num_channels)
+{
+ return 0;
+}
+
static inline void iio_ring_buffer_unregister(struct iio_ring_buffer *ring)
{};

diff --git a/drivers/staging/iio/sysfs.h b/drivers/staging/iio/sysfs.h
index 24b74dd..1a2e46e 100644
--- a/drivers/staging/iio/sysfs.h
+++ b/drivers/staging/iio/sysfs.h
@@ -24,6 +24,7 @@ struct iio_event_attr {
struct device_attribute dev_attr;
int mask;
struct iio_event_handler_list *listel;
+ struct list_head l;
};

#define to_iio_event_attr(_dev_attr) \
@@ -34,11 +35,14 @@ struct iio_event_attr {
* @dev_attr: underlying device attribute
* @address: associated register address
* @val2: secondary attribute value
+ * @l: list head for maintaining list of dynamically created attrs.
*/
struct iio_dev_attr {
struct device_attribute dev_attr;
int address;
int val2;
+ struct list_head l;
+ struct iio_chan_spec *c;
};

#define to_iio_dev_attr(_dev_attr) \
@@ -259,13 +263,14 @@ struct iio_const_attr {
#define IIO_EVENT_ATTR_DATA_RDY(_show, _store, _mask, _handler) \
IIO_EVENT_ATTR(data_rdy, _show, _store, _mask, _handler)

-#define IIO_EV_CLASS_BUFFER 0
-#define IIO_EV_CLASS_IN 1
-#define IIO_EV_CLASS_ACCEL 2
-#define IIO_EV_CLASS_GYRO 3
-#define IIO_EV_CLASS_MAGN 4
-#define IIO_EV_CLASS_LIGHT 5
-#define IIO_EV_CLASS_PROXIMITY 6
+/* must match our channel defs */
+#define IIO_EV_CLASS_IN IIO_IN
+#define IIO_EV_CLASS_ACCEL IIO_ACCEL
+#define IIO_EV_CLASS_GYRO IIO_GYRO
+#define IIO_EV_CLASS_MAGN IIO_MAGN
+#define IIO_EV_CLASS_LIGHT IIO_LIGHT
+#define IIO_EV_CLASS_PROXIMITY IIO_PROXIMITY
+#define IIO_EV_CLASS_BUFFER IIO_BUFFER

#define IIO_EV_MOD_X 0
#define IIO_EV_MOD_Y 1
@@ -287,6 +292,10 @@ struct iio_const_attr {
#define IIO_EV_DIR_RISING 1
#define IIO_EV_DIR_FALLING 2

+#define IIO_EV_TYPE_MAX 8
+#define IIO_EV_BIT(type, direction) \
+ (1 << (type*IIO_EV_TYPE_MAX + direction))
+
#define IIO_EVENT_CODE(channelclass, orient_bit, number, \
modifier, type, direction) \
(channelclass | (orient_bit << 8) | ((number) << 9) | \
@@ -303,6 +312,14 @@ struct iio_const_attr {
#define IIO_BUFFER_EVENT_CODE(code) \
(IIO_EV_CLASS_BUFFER | (code << 8))

+#define IIO_EVENT_CODE_EXTRACT_DIR(mask) ((mask >> 24) & 0xf)
+
+/* Event code number extraction depends on which type of event we have.
+ * Perhaps review this function in the future*/
+#define IIO_EVENT_CODE_EXTRACT_NUM(mask) ((mask >> 9) & 0x0f)
+
+#define IIO_EVENT_CODE_EXTRACT_MODIFIER(mask) ((mask >> 13) & 0x7)
+
/**
* IIO_EVENT_ATTR_RING_50_FULL - ring buffer event to indicate 50% full
* @_show: output method for the attribute
--
1.7.3.4

2011-03-31 15:29:00

by Arnd Bergmann

[permalink] [raw]
Subject: Re: [RFC PATCH 00/21] IIO: Channel registration rework, buffer chardev combining and rewrite of triggers as 'virtual' irq_chips.

On Thursday 31 March 2011, Jonathan Cameron wrote:
> I'm afraid what in many ways makes sense as three separate
> series have gotten rather intertwined. I can probably unpick
> them but it will involve a lot of intermediate code in
> lis3l02dq (which is our fiddly example for this set) that I'd
> rather avoid. Hence lets have a guide to what various people
> might be interested in:

Hi Jonathan,

Great to see your progress here. Unfortunately I currently don't
have much time to look at the patches in the necessary depth,
since I'll be travelling for the next two weeks up till ELC
in San Francisco. I might find some time to look at the patches,
but don't promise anything now, so please remind me later
if you want specific feedback.

Arnd

2011-03-31 15:48:52

by Thomas Gleixner

[permalink] [raw]
Subject: Re: [PATCH 13/21] arm: irq: export set flags

On Thu, 31 Mar 2011, Jonathan Cameron wrote:

> ---
> arch/arm/kernel/irq.c | 1 +
> 1 files changed, 1 insertions(+), 0 deletions(-)
>
> diff --git a/arch/arm/kernel/irq.c b/arch/arm/kernel/irq.c
> index 3535d37..e4fd9e4 100644
> --- a/arch/arm/kernel/irq.c
> +++ b/arch/arm/kernel/irq.c
> @@ -163,6 +163,7 @@ void set_irq_flags(unsigned int irq, unsigned int iflags)
> desc->status &= ~IRQ_NOAUTOEN;
> raw_spin_unlock_irqrestore(&desc->lock, flags);
> }
> +EXPORT_SYMBOL(set_irq_flags);

For one it would be EXPORT_SYMBOL_GPL, but that code has changed, so
the patch wont apply. Please make sure your patches apply to latest
linus git next time.

So the code looks like this now.

void set_irq_flags(unsigned int irq, unsigned int iflags)
{
unsigned long clr = 0, set = IRQ_NOREQUEST | IRQ_NOPROBE | IRQ_NOAUTOEN;

if (irq >= nr_irqs) {
printk(KERN_ERR "Trying to set irq flags for IRQ%d\n", irq);
return;
}

if (iflags & IRQF_VALID)
clr |= IRQ_NOREQUEST;
if (iflags & IRQF_PROBE)
clr |= IRQ_NOPROBE;
if (!(iflags & IRQF_NOAUTOEN))
clr |= IRQ_NOAUTOEN;
/* Order is clear bits in "clr" then set bits in "set" */
irq_modify_status(irq, clr, set & ~clr);
}

There are tons of drivers which use it under an ARM ifdef. That's
really silly. The flags are in 100% of the cases constants, so if we
remove the irq_nr check, then the compiler can optimize that code down
to
irq_modify_status(irq, X, Y);

So we have two options here to solve that.

Use coccinelle and sweep the whole tree and convert it to

irq_modify_status(irq, X, Y);

calls or move that set_irq_flags() stuff as an inline helper (minus
the printk) - the core code does the irq validity check already - into
linux/interrupt.h and clean up all the ifdef ARM stuff in drivers/*

I'd vote for the first one, but I have no real objection against the
second solution.

Russell ??

Thanks,

tglx

2011-04-02 18:33:19

by Jonathan Cameron

[permalink] [raw]
Subject: Re: [PATCH 15/21] staging:iio: Add infrastructure for irq_chip based triggers

As a heads up, the multiple consumers case is completely broken. There is
also a lockup that can occur if the trigger is too much faster than we can
handle (being rigorous with the usecounter is the fix for that).

Next version will have the following additional patch merged into this one.
(probably with correct locking on the various parameters - unlike here).

There's a reason this was an RFC for now ;)

---
drivers/staging/iio/industrialio-trigger.c | 35 ++++++++++++++++++++--------
1 files changed, 25 insertions(+), 10 deletions(-)

diff --git a/drivers/staging/iio/industrialio-trigger.c b/drivers/staging/iio/industrialio-trigger.c
index a350f25..f3b8e05 100644
--- a/drivers/staging/iio/industrialio-trigger.c
+++ b/drivers/staging/iio/industrialio-trigger.c
@@ -164,11 +164,14 @@ static struct iio_trigger *iio_trigger_find_by_name(const char *name,
void iio_trigger_poll(struct iio_trigger *trig, s64 time)
{
int i;
- for (i = 0; i < CONFIG_IIO_CONSUMERS_PER_TRIGGER; i++)
- if (trig->subirqs[i].enabled) {
- trig->use_count++;
- generic_handle_irq(trig->subirq_base + i);
- }
+ if (!trig->use_count) {
+ for (i = 0; i < CONFIG_IIO_CONSUMERS_PER_TRIGGER; i++)
+ if (trig->subirqs[i].enabled) {
+ trig->use_count++;
+ generic_handle_irq(trig->subirq_base + i);
+ }
+ }
}
EXPORT_SYMBOL(iio_trigger_poll);

@@ -208,13 +211,18 @@ int iio_trigger_attach_poll_func(struct iio_trigger *trig,
struct iio_poll_func *pf)
{
int ret = 0;
-
+ bool notinuse
+ = bitmap_empty(trig->pool, CONFIG_IIO_CONSUMERS_PER_TRIGGER);
+
pf->irq = iio_trigger_get_irq(trig);
ret = request_threaded_irq(pf->irq, NULL, pf->handler,
pf->type, pf->name,
pf);
- if (trig->set_trigger_state)
+
+
+ if (trig->set_trigger_state && notinuse) {
ret = trig->set_trigger_state(trig, true);
+ }

return ret;
}
@@ -223,9 +231,16 @@ EXPORT_SYMBOL(iio_trigger_attach_poll_func);
int iio_trigger_dettach_poll_func(struct iio_trigger *trig,
struct iio_poll_func *pf)
{
- int ret = trig->set_trigger_state(trig, false);
- if (ret)
- goto error_ret;
+ int ret = 0;
+ bool no_other_users
+ = (bitmap_weight(trig->pool,
+ CONFIG_IIO_CONSUMERS_PER_TRIGGER) == 1);
+
+ if (trig->set_trigger_state && no_other_users) {
+ ret = trig->set_trigger_state(trig, false);
+ if (ret)
+ goto error_ret;
+ }
iio_trigger_put_irq(trig, pf->irq);
free_irq(pf->irq, pf);

--
1.7.3.4



> ---
> drivers/staging/Makefile | 2 +-
> drivers/staging/iio/Kconfig | 12 ++
> drivers/staging/iio/Makefile | 2 +
> drivers/staging/iio/iio-board-info.h | 24 ++++
> drivers/staging/iio/iio-core.h | 13 ++
> drivers/staging/iio/iio.h | 13 ++
> drivers/staging/iio/industrialio-board-info.c | 25 ++++
> drivers/staging/iio/industrialio-core.c | 28 +++++
> drivers/staging/iio/industrialio-trigger.c | 146 ++++++++++++++++++++-----
> drivers/staging/iio/trigger.h | 40 +++++++-
> 10 files changed, 275 insertions(+), 30 deletions(-)
>
> diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile
> index cfd13cd..ee7594c 100644
> --- a/drivers/staging/Makefile
> +++ b/drivers/staging/Makefile
> @@ -42,7 +42,7 @@ obj-$(CONFIG_HYPERV) += hv/
> obj-$(CONFIG_VME_BUS) += vme/
> obj-$(CONFIG_MRST_RAR_HANDLER) += memrar/
> obj-$(CONFIG_DX_SEP) += sep/
> -obj-$(CONFIG_IIO) += iio/
> +obj-y += iio/
> obj-$(CONFIG_CS5535_GPIO) += cs5535_gpio/
> obj-$(CONFIG_ZRAM) += zram/
> obj-$(CONFIG_XVMALLOC) += zram/
> diff --git a/drivers/staging/iio/Kconfig b/drivers/staging/iio/Kconfig
> index 6775bf9..df9aa08 100644
> --- a/drivers/staging/iio/Kconfig
> +++ b/drivers/staging/iio/Kconfig
> @@ -12,6 +12,10 @@ menuconfig IIO
> Documentation/industrialio for more information.
> if IIO
>
> +config IIO_BOARDINFO
> + boolean
> + default y
> +
> config IIO_RING_BUFFER
> bool "Enable ring buffer support within IIO"
> help
> @@ -42,12 +46,20 @@ endif # IIO_RINGBUFFER
>
> config IIO_TRIGGER
> boolean "Enable triggered sampling support"
> + depends on IIO_BOARDINFO
> help
> Provides IIO core support for triggers. Currently these
> are used to initialize capture of samples to push into
> ring buffers. The triggers are effectively a 'capture
> data now' interrupt.
>
> +config IIO_CONSUMERS_PER_TRIGGER
> + int "Maximum number of consumers per trigger"
> + depends on IIO_TRIGGER
> + default "2"
> + help
> + This value controls the maximum number of consumers that a
> + given trigger may handle. Default is 2.
>
> source "drivers/staging/iio/accel/Kconfig"
> source "drivers/staging/iio/adc/Kconfig"
> diff --git a/drivers/staging/iio/Makefile b/drivers/staging/iio/Makefile
> index bb5c95c..37b4d12 100644
> --- a/drivers/staging/iio/Makefile
> +++ b/drivers/staging/iio/Makefile
> @@ -2,6 +2,8 @@
> # Makefile for the industrial I/O core.
> #
>
> +obj-$(CONFIG_IIO_BOARDINFO) += industrialio-board-info.o
> +
> obj-$(CONFIG_IIO) += industrialio.o
> industrialio-y := industrialio-core.o
> industrialio-$(CONFIG_IIO_RING_BUFFER) += industrialio-ring.o
> diff --git a/drivers/staging/iio/iio-board-info.h b/drivers/staging/iio/iio-board-info.h
> new file mode 100644
> index 0000000..c608a4d
> --- /dev/null
> +++ b/drivers/staging/iio/iio-board-info.h
> @@ -0,0 +1,24 @@
> +/*
> + * Board information for IIO.
> + *
> + * Copyright (c) 2011 Jonathan Cameron <[email protected]>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + */
> +
> +#ifdef CONFIG_IIO_BOARDINFO
> +
> +/**
> + * iio_set_irq_pool() - provid iio with a pool of virtaul interrupts to use
> + * @start: start of the pool
> + * @num: number of interupts in the pool
> + **/
> +void iio_set_irq_pool(int start, int num);
> +
> +#else
> +
> +static inline void iio_set_irq_pool(int start, int num) {};
> +
> +#endif
> diff --git a/drivers/staging/iio/iio-core.h b/drivers/staging/iio/iio-core.h
> new file mode 100644
> index 0000000..2c4f433
> --- /dev/null
> +++ b/drivers/staging/iio/iio-core.h
> @@ -0,0 +1,13 @@
> +/*
> + * IIO core header. Should never be included by drivers.
> + *
> + * Copyright (c) 2011 Jonathan Cameron <[email protected]>
> + *
> + * 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.
> + */
> +
> +#define IIO_MAX_POOL 128
> +extern int __iio_irqstart;
> +extern int __iio_irqnum;
> diff --git a/drivers/staging/iio/iio.h b/drivers/staging/iio/iio.h
> index fb6caae..add0659 100644
> --- a/drivers/staging/iio/iio.h
> +++ b/drivers/staging/iio/iio.h
> @@ -304,6 +304,19 @@ extern dev_t iio_devt;
> extern struct bus_type iio_bus_type;
>
> /**
> + * iio_irq_pool_get_range() - get a set of irqs from the pool
> + * @num: number of interrupts requested
> + **/
> +int iio_irq_pool_get_range(int num);
> +
> +/**
> + * iio_irq_pool_put_range()
> + * @start: first irq to return to the pool
> + * @num: number to return
> + **/
> +void iio_irq_pool_put_range(int start, int num);
> +
> +/**
> * iio_put_device() - reference counted deallocation of struct device
> * @dev: the iio_device containing the device
> **/
> diff --git a/drivers/staging/iio/industrialio-board-info.c b/drivers/staging/iio/industrialio-board-info.c
> new file mode 100644
> index 0000000..28f9156
> --- /dev/null
> +++ b/drivers/staging/iio/industrialio-board-info.c
> @@ -0,0 +1,25 @@
> +/* IIO Board info
> + *
> + * Copyright (c) 2011 Jonathan Cameron
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms of the GNU General Public License version 2 as published by
> + * the Free Software Foundation.
> + */
> +
> +#include <linux/module.h>
> +#include <linux/kernel.h>
> +#include "iio-core.h"
> +
> +int __iio_irqstart;
> +EXPORT_SYMBOL(__iio_irqstart);
> +int __iio_irqnum;
> +EXPORT_SYMBOL(__iio_irqnum);
> +
> +void __init iio_set_irq_pool(int start, int num)
> +{
> + __iio_irqstart = start;
> + __iio_irqnum = num;
> + if (num > IIO_MAX_POOL)
> + __iio_irqnum = IIO_MAX_POOL;
> +}
> diff --git a/drivers/staging/iio/industrialio-core.c b/drivers/staging/iio/industrialio-core.c
> index 0473f55..39b987d 100644
> --- a/drivers/staging/iio/industrialio-core.c
> +++ b/drivers/staging/iio/industrialio-core.c
> @@ -23,6 +23,7 @@
> #include <linux/cdev.h>
> #include <linux/slab.h>
> #include "iio.h"
> +#include "iio-core.h"
> #include "trigger_consumer.h"
>
> #define IIO_ID_PREFIX "device"
> @@ -35,6 +36,33 @@ static DEFINE_IDA(iio_chrdev_ida);
> /* Lock used to protect both of the above */
> static DEFINE_SPINLOCK(iio_ida_lock);
>
> +static DEFINE_MUTEX(iio_irq_pool_lock);
> +static unsigned long iio_irq_pool_mask[BITS_TO_LONGS(IIO_MAX_POOL)] = {};
> +
> +int iio_irq_pool_get_range(int num)
> +{
> + int ret;
> +
> + mutex_lock(&iio_irq_pool_lock);
> + ret = bitmap_find_free_region(iio_irq_pool_mask,
> + __iio_irqnum, ilog2(num));
> + if (ret >= 0)
> + ret += __iio_irqstart;
> + mutex_unlock(&iio_irq_pool_lock);
> +
> + return ret;
> +}
> +EXPORT_SYMBOL(iio_irq_pool_get_range);
> +
> +void iio_irq_pool_put_range(int start, int num)
> +{
> + mutex_lock(&iio_irq_pool_lock);
> + bitmap_release_region(iio_irq_pool_mask,
> + start - __iio_irqstart, ilog2(num));
> + mutex_unlock(&iio_irq_pool_lock);
> +}
> +EXPORT_SYMBOL(iio_irq_pool_put_range);
> +
> dev_t iio_devt;
> EXPORT_SYMBOL(iio_devt);
>
> diff --git a/drivers/staging/iio/industrialio-trigger.c b/drivers/staging/iio/industrialio-trigger.c
> index 083847c..8100822 100644
> --- a/drivers/staging/iio/industrialio-trigger.c
> +++ b/drivers/staging/iio/industrialio-trigger.c
> @@ -163,6 +163,7 @@ static struct iio_trigger *iio_trigger_find_by_name(const char *name,
>
> void iio_trigger_poll(struct iio_trigger *trig, s64 time)
> {
> + int i;
> struct iio_poll_func *pf_cursor;
>
> list_for_each_entry(pf_cursor, &trig->pollfunc_list, list) {
> @@ -178,6 +179,11 @@ void iio_trigger_poll(struct iio_trigger *trig, s64 time)
> trig->use_count++;
> }
> }
> + for (i = 0; i < CONFIG_IIO_CONSUMERS_PER_TRIGGER; i++)
> + if (trig->subirqs[i].enabled) {
> + trig->use_count++;
> + generic_handle_irq(trig->subirq_base + i);
> + }
> }
> EXPORT_SYMBOL(iio_trigger_poll);
>
> @@ -219,16 +225,23 @@ int iio_trigger_attach_poll_func(struct iio_trigger *trig,
> int ret = 0;
> unsigned long flags;
>
> - spin_lock_irqsave(&trig->pollfunc_list_lock, flags);
> - list_add_tail(&pf->list, &trig->pollfunc_list);
> - spin_unlock_irqrestore(&trig->pollfunc_list_lock, flags);
> -
> + if (pf->handler) {
> + pf->irq = iio_trigger_get_irq(trig);
> + ret = request_threaded_irq(pf->irq, NULL, pf->handler,
> + pf->type, pf->name,
> + pf);
> + } else {
> + spin_lock_irqsave(&trig->pollfunc_list_lock, flags);
> + list_add_tail(&pf->list, &trig->pollfunc_list);
> + spin_unlock_irqrestore(&trig->pollfunc_list_lock, flags);
> + }
> if (trig->set_trigger_state)
> ret = trig->set_trigger_state(trig, true);
> if (ret) {
> printk(KERN_ERR "set trigger state failed\n");
> list_del(&pf->list);
> }
> +
> return ret;
> }
> EXPORT_SYMBOL(iio_trigger_attach_poll_func);
> @@ -240,31 +253,42 @@ int iio_trigger_dettach_poll_func(struct iio_trigger *trig,
> unsigned long flags;
> int ret = -EINVAL;
>
> - spin_lock_irqsave(&trig->pollfunc_list_lock, flags);
> - list_for_each_entry(pf_cursor, &trig->pollfunc_list, list)
> - if (pf_cursor == pf) {
> - ret = 0;
> - break;
> + if (pf->handler) {
> + ret = trig->set_trigger_state(trig, false);
> + if (ret)
> + goto error_ret;
> + iio_trigger_put_irq(trig, pf->irq);
> + free_irq(pf->irq, pf);
> + } else {
> + spin_lock_irqsave(&trig->pollfunc_list_lock, flags);
> + list_for_each_entry(pf_cursor, &trig->pollfunc_list, list)
> + if (pf_cursor == pf) {
> + ret = 0;
> + break;
> + }
> + if (!ret) {
> + if (list_is_singular(&trig->pollfunc_list)
> + && trig->set_trigger_state) {
> + spin_unlock_irqrestore(&trig
> + ->pollfunc_list_lock,
> + flags);
> + /* May sleep hence cannot hold the spin lock */
> + ret = trig->set_trigger_state(trig, false);
> + if (ret)
> + goto error_ret;
> + spin_lock_irqsave(&trig->pollfunc_list_lock,
> + flags);
> + }
> + /*
> + * Now we can delete safe in the knowledge that, if
> + * this is the last pollfunc then we have disabled
> + * the trigger anyway and so nothing should be able
> + * to call the pollfunc.
> + */
> + list_del(&pf_cursor->list);
> }
> - if (!ret) {
> - if (list_is_singular(&trig->pollfunc_list)
> - && trig->set_trigger_state) {
> - spin_unlock_irqrestore(&trig->pollfunc_list_lock,
> - flags);
> - /* May sleep hence cannot hold the spin lock */
> - ret = trig->set_trigger_state(trig, false);
> - if (ret)
> - goto error_ret;
> - spin_lock_irqsave(&trig->pollfunc_list_lock, flags);
> - }
> - /*
> - * Now we can delete safe in the knowledge that, if this is
> - * the last pollfunc then we have disabled the trigger anyway
> - * and so nothing should be able to call the pollfunc.
> - */
> - list_del(&pf_cursor->list);
> + spin_unlock_irqrestore(&trig->pollfunc_list_lock, flags);
> }
> - spin_unlock_irqrestore(&trig->pollfunc_list_lock, flags);
>
> error_ret:
> return ret;
> @@ -337,6 +361,21 @@ static const struct attribute_group iio_trigger_consumer_attr_group = {
> static void iio_trig_release(struct device *device)
> {
> struct iio_trigger *trig = to_iio_trigger(device);
> + int i;
> +
> + if (trig->subirq_base) {
> + for (i = 0; i < CONFIG_IIO_CONSUMERS_PER_TRIGGER; i++) {
> + set_irq_flags(trig->subirq_base + i, 0);
> + irq_set_chip(trig->subirq_base + i,
> + NULL);
> + irq_set_handler(trig->subirq_base + i,
> + NULL);
> + }
> +
> + kfree(trig->subirq_chip.name);
> + iio_irq_pool_put_range(trig->subirq_base,
> + CONFIG_IIO_CONSUMERS_PER_TRIGGER);
> + }
> kfree(trig);
> iio_put();
> }
> @@ -345,11 +384,30 @@ static struct device_type iio_trig_type = {
> .release = iio_trig_release,
> };
>
> -struct iio_trigger *iio_allocate_trigger(void)
> +static void iio_trig_subirqmask(struct irq_data *d)
> +{
> + struct irq_chip *chip = irq_data_get_irq_chip(d);
> + struct iio_trigger *trig
> + = container_of(chip,
> + struct iio_trigger, subirq_chip);
> + trig->subirqs[d->irq - trig->subirq_base].enabled = false;
> +}
> +
> +static void iio_trig_subirqunmask(struct irq_data *d)
> +{
> + struct irq_chip *chip = irq_data_get_irq_chip(d);
> + struct iio_trigger *trig
> + = container_of(chip,
> + struct iio_trigger, subirq_chip);
> + trig->subirqs[d->irq - trig->subirq_base].enabled = true;
> +}
> +
> +struct iio_trigger *iio_allocate_trigger_named(const char *name)
> {
> struct iio_trigger *trig;
> trig = kzalloc(sizeof *trig, GFP_KERNEL);
> if (trig) {
> + int i;
> trig->dev.type = &iio_trig_type;
> trig->dev.bus = &iio_bus_type;
> device_initialize(&trig->dev);
> @@ -357,10 +415,42 @@ struct iio_trigger *iio_allocate_trigger(void)
> spin_lock_init(&trig->pollfunc_list_lock);
> INIT_LIST_HEAD(&trig->list);
> INIT_LIST_HEAD(&trig->pollfunc_list);
> +
> + if (name) {
> + mutex_init(&trig->pool_lock);
> + trig->subirq_base
> + = iio_irq_pool_get_range(
> + CONFIG_IIO_CONSUMERS_PER_TRIGGER);
> + if (trig->subirq_base < 0) {
> + kfree(trig);
> + return NULL;
> + }
> + trig->subirq_chip.name = kstrdup(name, GFP_KERNEL);
> + if (trig->subirq_chip.name == NULL) {
> + kfree(trig);
> + return NULL;
> + }
> + trig->subirq_chip.irq_mask = &iio_trig_subirqmask;
> + trig->subirq_chip.irq_unmask = &iio_trig_subirqunmask;
> + for (i = 0; i < CONFIG_IIO_CONSUMERS_PER_TRIGGER; i++) {
> + irq_set_chip(trig->subirq_base + i,
> + &trig->subirq_chip);
> + irq_set_handler(trig->subirq_base + i,
> + &handle_simple_irq);
> + set_irq_flags(trig->subirq_base + i,
> + IRQF_VALID);
> + }
> + }
> iio_get();
> }
> return trig;
> }
> +EXPORT_SYMBOL(iio_allocate_trigger_named);
> +
> +struct iio_trigger *iio_allocate_trigger(void)
> +{
> + return iio_allocate_trigger_named(NULL);
> +}
> EXPORT_SYMBOL(iio_allocate_trigger);
>
> void iio_free_trigger(struct iio_trigger *trig)
> diff --git a/drivers/staging/iio/trigger.h b/drivers/staging/iio/trigger.h
> index c6ab32f..2e52004 100644
> --- a/drivers/staging/iio/trigger.h
> +++ b/drivers/staging/iio/trigger.h
> @@ -6,9 +6,15 @@
> * under the terms of the GNU General Public License version 2 as published by
> * the Free Software Foundation.
> */
> +#include <linux/irq.h>
> +
> #ifndef _IIO_TRIGGER_H_
> #define _IIO_TRIGGER_H_
>
> +struct iio_subirq {
> + bool enabled;
> +};
> +
> /**
> * struct iio_trigger - industrial I/O trigger device
> *
> @@ -43,6 +49,13 @@ struct iio_trigger {
>
> int (*set_trigger_state)(struct iio_trigger *trig, bool state);
> int (*try_reenable)(struct iio_trigger *trig);
> +
> + struct irq_chip subirq_chip;
> + int subirq_base;
> +
> + struct iio_subirq subirqs[CONFIG_IIO_CONSUMERS_PER_TRIGGER];
> + unsigned long pool[BITS_TO_LONGS(CONFIG_IIO_CONSUMERS_PER_TRIGGER)];
> + struct mutex pool_lock;
> };
>
> static inline struct iio_trigger *to_iio_trigger(struct device *d)
> @@ -114,6 +127,27 @@ int iio_trigger_dettach_poll_func(struct iio_trigger *trig,
> void iio_trigger_poll(struct iio_trigger *trig, s64 time);
> void iio_trigger_notify_done(struct iio_trigger *trig);
>
> +static inline int iio_trigger_get_irq(struct iio_trigger *trig)
> +{
> + int ret;
> + mutex_lock(&trig->pool_lock);
> + ret = bitmap_find_free_region(trig->pool,
> + CONFIG_IIO_CONSUMERS_PER_TRIGGER,
> + ilog2(1));
> + mutex_unlock(&trig->pool_lock);
> + if (ret >= 0)
> + ret += trig->subirq_base;
> +
> + return ret;
> +};
> +
> +static inline void iio_trigger_put_irq(struct iio_trigger *trig, int irq)
> +{
> + mutex_lock(&trig->pool_lock);
> + clear_bit(irq - trig->subirq_base, trig->pool);
> + mutex_unlock(&trig->pool_lock);
> +};
> +
> /**
> * struct iio_poll_func - poll function pair
> *
> @@ -137,6 +171,10 @@ struct iio_poll_func {
> void (*poll_func_immediate)(struct iio_dev *indio_dev);
> void (*poll_func_main)(struct iio_dev *private_data, s64 time);
>
> + irqreturn_t (*handler)(int irq, void *p);
> + int type;
> + char *name;
> + int irq;
> };
>
> int iio_alloc_pollfunc(struct iio_dev *indio_dev,
> @@ -151,7 +189,7 @@ int iio_triggered_ring_postenable(struct iio_dev *indio_dev);
> int iio_triggered_ring_predisable(struct iio_dev *indio_dev);
>
> struct iio_trigger *iio_allocate_trigger(void);
> -
> +struct iio_trigger *iio_allocate_trigger_named(const char *name);
> void iio_free_trigger(struct iio_trigger *trig);
>
> #endif /* _IIO_TRIGGER_H_ */

2011-04-04 12:10:58

by Hennerich, Michael

[permalink] [raw]
Subject: Re: [RFC PATCH 00/21] IIO: Channel registration rework, buffer chardev combining and rewrite of triggers as 'virtual' irq_chips.

On 03/31/2011 04:53 PM, Jonathan Cameron wrote:
> Dear All,
>
> I'm afraid what in many ways makes sense as three separate
> series have gotten rather intertwined. I can probably unpick
> them but it will involve a lot of intermediate code in
> lis3l02dq (which is our fiddly example for this set) that I'd
> rather avoid. Hence lets have a guide to what various people
> might be interested in:
>
> 1) Channel registration rework (this has previous been in linux-iio
> but really comes into it's own after we remove various special
> cases later in this code).
>
> Patches 1, 2, 3, 21
> (8) - cleanups Arnd Bergmann pointed out in passing.
>
Good approach. It removes quite a bit on duplicated code across drivers.
At the moment I can't think of existing drivers that couldn't moved over
to the
new channel registration method. However there are some limitations.
read_raw() value is currently type int, depending on the channel type,
int type might be to short.


> 2) Flattening together of (some) of the chardevs (buffer related ones).
> As Arnd pointed out, there is really a use case for having multiple
> watershed type events from ring buffers. Much better to have a
> single one (be that at a controllable point). Firstly this removes
> the need for the event escalation code. Second, this single 'event'
> can then be indicated to user space purely using polling on the
> access chrdev. This approach does need an additional attribute to
> tell user space what the poll succeeding indicates (tbd).
>
> I haven't for now merged the ring handling with the more general
> event handling as Arnd suggested. This is for two reasons
> 1) It's much easier to debug this done in a couple of steps
> 2) The approach Arnd suggested may work well, but it is rather
> different to how other bits of the kernel pass event type data
> to user space. It's an interesting idea, but I'd rather any
> discussion of that approach was separate from the obviously
> big gains seen here.
>
> Patches 4, 5, 6, 7, 17
>
I appreciate the removal of the buffer event chardev. Adding support for
poll is also a good thing to do.
However support for a blocking read might also fit some use cases.

> 3) Reworking the triggering infrastructure to use 'virtual' irq_chips
> This approach was suggested by Thomas Gleixner.
> Before we had explicit trigger consumer lists. This made for a very
> clunky implementation when we considered moving things over to
> threaded interrupts. Thomas pointed out we were reinventing the
> wheel and suggested more or less what we have here (I hope ;)
>
Using threaded interrupts, greatly reduces use of additional workqueues
and excessive interrupt enable and disables.

> Patches 9 and 10 are minor rearrangements of code in the one
> driver I know of where the physical interrupt line for events
> is the same as that for data ready signals (though not at the
> same time).
>
I wouldn't consider this being a corner case. I know quite a few devices
that trigger data availability,
and other events from the same physical interrupt line, and they may do
it at the same time.

> In a rare situation we have complete control of these virtual
> interrupts within the subsystem. As such we want to be able to
> continue to build the subsystem as a module. This requires a
> couple of additional exports in the generic irq core code and
> also arm (for my test board anyway).
> Patches 13 and 14 make these changes. I hope they won't prove
> to controversial.
>
> Patch 15 adds a board info built in element to the IIO subsystem
> so we have a means of platform data telling us what interrupt
> numbers are available for us to play with. Does anyone have
> a better way of doing this? Patch 16 is an example of what
> needs to go in board files.
>
Since this is purely platform dependent, setting the irq pool from the
board setup looks acceptable to me, and depending on the arch or machine
it might be necessary two tweak some other defines.
However many arches define NR_IRQS always greater than actually used. So
why not make IR-Base a Kconfig option?

> 18, 19 move lis3l02dq over to the threaded interrupts.
>
> 20 rips out the old approach. Until this patch current drivers
> using the old system should still work. I make no guarantees
> about combining consumers of both types though!
>
>
> Note that this series sits on top of the cleanup series:
> [PATCH 0/8] staging:iio:mixed bag of fixes and cleanups
> http://marc.info/?l=linux-iio&m=130039556909192&w=2
> on top of staging-next (which right now might mean mainline is
> also fine).
>
> There are clearly corners to be cleaned up and exact interfaces
> elements to be pinned down, but I'd like to get some opinions
> on what we have here.
>
> All comments welcomed!
>
> Note, wrt to the recent discussion on a strategy for moving IIO
> out of staging, my intent is to pin down these big changes before
> we attempt to move these elements out. Note that the initial
> steps of that move may occur in parallel with these changes.
>
> Some of these patches may only be applied during that move as
> that is a sensible point to update and test every driver.
>
> Thanks to Arnd and Thomas for their contributions to this.
>
> Jonathan
>
> Jonathan Cameron (21):
> staging:iio: allow channels to be set up using a table of
> iio_channel_spec structures.
> staging:iio:lis3l02dq - experimental move to new channel_spec
> approach.
> staging:iio:max1363 - experimental move to channel_spec registration.
> staging:iio: remove ability to escalate events.
> staging:iio: Add polling of events on the ring access chrdev.
> staging:iio: remove legacy event chrdev for the buffers
> staging:iio: Buffer device flattening.
> staging:iio:lis3l02dq: General cleanup
> staging:iio: Push interrupt setup down into the drivers for event
> lines.
> staging:iio: lis3l02dq - separate entirely interrupt handling for
> thesholds from that for the datardy signal.
> staging:iio: Remove legacy event handling.
> staging:iio:lis3l02dq make threshold interrupt threaded.
> arm: irq: export set flags
> irq: export handle_simple_irq and irq_to_desc to allow for virtual
> irqs in IIO
> staging:iio: Add infrastructure for irq_chip based triggers
> stargate2 - add an IIO interrupt pool
> staging:iio:Documentation generic_buffer.c update to new abi for
> buffers.
> staging:iio:ring_sw add function needed for threaded irq.
> staging:iio:lis3l02dq move to threaded trigger handling.
> staging:iio:trigger remove legacy pollfunc elements.
> staging:iio: rip out scan_el attributes. Now handled as
> iio_dev_attrs like everything else.
>
> arch/arm/kernel/irq.c | 1 +
> arch/arm/mach-pxa/stargate2.c | 6 +-
> drivers/staging/Makefile | 2 +-
> drivers/staging/iio/Documentation/generic_buffer.c | 53 +-
> drivers/staging/iio/Kconfig | 12 +
> drivers/staging/iio/Makefile | 2 +
> drivers/staging/iio/accel/lis3l02dq.h | 25 +-
> drivers/staging/iio/accel/lis3l02dq_core.c | 700 +++++-------
> drivers/staging/iio/accel/lis3l02dq_ring.c | 421 +++----
> drivers/staging/iio/adc/max1363.h | 8 +-
> drivers/staging/iio/adc/max1363_core.c | 1277 ++++++++------------
> drivers/staging/iio/adc/max1363_ring.c | 1 -
> drivers/staging/iio/chrdev.h | 33 +-
> drivers/staging/iio/iio-board-info.h | 24 +
> drivers/staging/iio/iio-core.h | 13 +
> drivers/staging/iio/iio.h | 250 +++--
> drivers/staging/iio/industrialio-board-info.c | 25 +
> drivers/staging/iio/industrialio-core.c | 724 ++++++++---
> drivers/staging/iio/industrialio-ring.c | 315 +++---
> drivers/staging/iio/industrialio-trigger.c | 150 ++--
> drivers/staging/iio/ring_generic.h | 79 +-
> drivers/staging/iio/ring_sw.c | 42 +-
> drivers/staging/iio/ring_sw.h | 1 +
> drivers/staging/iio/sysfs.h | 152 +--
> drivers/staging/iio/trigger.h | 68 +-
> kernel/irq/chip.c | 1 +
> kernel/irq/irqdesc.c | 1 +
> 27 files changed, 2116 insertions(+), 2270 deletions(-)
> create mode 100644 drivers/staging/iio/iio-board-info.h
> create mode 100644 drivers/staging/iio/iio-core.h
> create mode 100644 drivers/staging/iio/industrialio-board-info.c
>
> --
> 1.7.3.4
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-iio" in
> the body of a message to [email protected]
> More majordomo info at http://vger.kernel.org/majordomo-info.html
>


--
Greetings,
Michael

--
Analog Devices GmbH Wilhelm-Wagenfeld-Str. 6 80807 Muenchen
Sitz der Gesellschaft: Muenchen; Registergericht: Muenchen HRB 40368;
Geschaeftsfuehrer:Dr.Carsten Suckrow, Thomas Wessel, William A. Martin,
Margaret Seif

2011-04-04 13:15:39

by Jonathan Cameron

[permalink] [raw]
Subject: Re: [RFC PATCH 00/21] IIO: Channel registration rework, buffer chardev combining and rewrite of triggers as 'virtual' irq_chips.

On 04/04/11 13:02, Michael Hennerich wrote:
> On 03/31/2011 04:53 PM, Jonathan Cameron wrote:
>> Dear All,
>>
>> I'm afraid what in many ways makes sense as three separate
>> series have gotten rather intertwined. I can probably unpick
>> them but it will involve a lot of intermediate code in
>> lis3l02dq (which is our fiddly example for this set) that I'd
>> rather avoid. Hence lets have a guide to what various people
>> might be interested in:
>>
>> 1) Channel registration rework (this has previous been in linux-iio
>> but really comes into it's own after we remove various special
>> cases later in this code).
>>
>> Patches 1, 2, 3, 21
>> (8) - cleanups Arnd Bergmann pointed out in passing.
>>
> Good approach. It removes quite a bit on duplicated code across drivers.
> At the moment I can't think of existing drivers that couldn't moved over
> to the
> new channel registration method.
There are a few that will require quite a bit more code - principally the
light sensors with their rather odd channel naming. I'll do one of those
shortly and see what we end up with.

> However there are some limitations.
> read_raw() value is currently type int, depending on the channel type,
> int type might be too short.
True. How far do you think we should go? s64? I did wonder if it makes sense
to have two value pointers (perhaps NULL) So base unit (val1) and
decimal places of base unit (val2).

So true raw values (e.g. sensor readings) will only set val1, but we have plenty
of space for things like scale at sufficient accuracy. That also means we can
flatten together the attributes in the core for both cases (not a great saving
but nice to have none the less).

What do you think?

>
>
>> 2) Flattening together of (some) of the chardevs (buffer related ones).
>> As Arnd pointed out, there is really a use case for having multiple
>> watershed type events from ring buffers. Much better to have a
>> single one (be that at a controllable point). Firstly this removes
>> the need for the event escalation code. Second, this single 'event'
>> can then be indicated to user space purely using polling on the
>> access chrdev. This approach does need an additional attribute to
>> tell user space what the poll succeeding indicates (tbd).
>>
>> I haven't for now merged the ring handling with the more general
>> event handling as Arnd suggested. This is for two reasons
>> 1) It's much easier to debug this done in a couple of steps
>> 2) The approach Arnd suggested may work well, but it is rather
>> different to how other bits of the kernel pass event type data
>> to user space. It's an interesting idea, but I'd rather any
>> discussion of that approach was separate from the obviously
>> big gains seen here.
>>
>> Patches 4, 5, 6, 7, 17
>>
> I appreciate the removal of the buffer event chardev. Adding support for
> poll is also a good thing to do.
> However support for a blocking read might also fit some use cases.
Good point. I guess blocking on any content and poll for the watershead
gives the best of both worlds. The blocking read is more down to the
individual implementations than a core feature though - so one to do
after this patch set.
>
>> 3) Reworking the triggering infrastructure to use 'virtual' irq_chips
>> This approach was suggested by Thomas Gleixner.
>> Before we had explicit trigger consumer lists. This made for a very
>> clunky implementation when we considered moving things over to
>> threaded interrupts. Thomas pointed out we were reinventing the
>> wheel and suggested more or less what we have here (I hope ;)
>>
> Using threaded interrupts, greatly reduces use of additional workqueues
> and excessive interrupt enable and disables.
There is a nasty side issue here. What do we do if we are getting triggers
faster than all of the consumers can work at? Right now things tend to
stall. I think we just want to gracefully stop the relevant trigger
if this happens. I'm not quite sure how we can notify userspace that this
has happened... Perhaps POLLERR?
>
>> Patches 9 and 10 are minor rearrangements of code in the one
>> driver I know of where the physical interrupt line for events
>> is the same as that for data ready signals (though not at the
>> same time).
>>
> I wouldn't consider this being a corner case. I know quite a few devices
> that trigger data availability,
> and other events from the same physical interrupt line, and they may do
> it at the same time.
If they do it at the same time things may get a bit nasty. Things are somewhat
simpler after some of the later patches, as the irq requests are entirely
handled in the drivers. Thus the driver could have one interrupt handler.
The restriction will be that it would only be able to do nested irq calls
limiting us to not having a top half for anything triggered from such an
interrupt. This is because identifying whether we have a dataready or
other event will require querying the device and hence sleeping. Note
the sysfs trigger driver will also have this restriction (as posted yesterday).

For devices where they share the line but cannot happen at the same time I'd
prefer to do what we have in the lis3l02dq and completely separate the two
uses of the interrupt line.

>
>> In a rare situation we have complete control of these virtual
>> interrupts within the subsystem. As such we want to be able to
>> continue to build the subsystem as a module. This requires a
>> couple of additional exports in the generic irq core code and
>> also arm (for my test board anyway).
>> Patches 13 and 14 make these changes. I hope they won't prove
>> to controversial.
>>
>> Patch 15 adds a board info built in element to the IIO subsystem
>> so we have a means of platform data telling us what interrupt
>> numbers are available for us to play with. Does anyone have
>> a better way of doing this? Patch 16 is an example of what
>> needs to go in board files.
>>
> Since this is purely platform dependent, setting the irq pool from the
> board setup looks acceptable to me, and depending on the arch or machine
> it might be necessary two tweak some other defines.
> However many arches define NR_IRQS always greater than actually used. So
> why not make IR-Base a Kconfig option?
There is currently a nasty hack in the irq codes to deal with the fact that
for at least some (maybe all) arm chips NR_IRQS is set to those on the SOC
and doesn't include any others. The work around for that is that all the
irq handling adds a chunk of padding. I would hope that will go away at
some point in the future.
Otherwise, yes we could indeed make it a KCONFIG option subject to some
fiddling with individual arches to make it work. This may be one to tackle
when moving out of staging rather than now though.

Perhaps we need to try it on a few architectures and see what is needed on each?

Thanks for taking a look!

Jonathan

2011-04-04 13:25:23

by Jonathan Cameron

[permalink] [raw]
Subject: Re: [RFC PATCH 00/21] IIO: Channel registration rework, buffer chardev combining and rewrite of triggers as 'virtual' irq_chips.

On 04/04/11 14:17, Jonathan Cameron wrote:
> On 04/04/11 13:02, Michael Hennerich wrote:
>> On 03/31/2011 04:53 PM, Jonathan Cameron wrote:
>>> Dear All,
>>>
>>> I'm afraid what in many ways makes sense as three separate
>>> series have gotten rather intertwined. I can probably unpick
>>> them but it will involve a lot of intermediate code in
>>> lis3l02dq (which is our fiddly example for this set) that I'd
>>> rather avoid. Hence lets have a guide to what various people
>>> might be interested in:
>>>
>>> 1) Channel registration rework (this has previous been in linux-iio
>>> but really comes into it's own after we remove various special
>>> cases later in this code).
>>>
>>> Patches 1, 2, 3, 21
>>> (8) - cleanups Arnd Bergmann pointed out in passing.
>>>
>> Good approach. It removes quite a bit on duplicated code across drivers.
>> At the moment I can't think of existing drivers that couldn't moved over
>> to the
>> new channel registration method.
> There are a few that will require quite a bit more code - principally the
> light sensors with their rather odd channel naming. I'll do one of those
> shortly and see what we end up with.
>
>> However there are some limitations.
>> read_raw() value is currently type int, depending on the channel type,
>> int type might be too short.
> True. How far do you think we should go? s64? I did wonder if it makes sense
> to have two value pointers (perhaps NULL) So base unit (val1) and
> decimal places of base unit (val2).
>
> So true raw values (e.g. sensor readings) will only set val1, but we have plenty
> of space for things like scale at sufficient accuracy. That also means we can
> flatten together the attributes in the core for both cases (not a great saving
> but nice to have none the less).
Oops, obviously if we do combine the functions then passing NULL pointers is
nonsensical. Perhaps the return value can indicate which parts are meaningful.
>
> What do you think?
>

2011-04-04 14:49:57

by Arnd Bergmann

[permalink] [raw]
Subject: Re: [RFC PATCH 00/21] IIO: Channel registration rework, buffer chardev combining and rewrite of triggers as 'virtual' irq_chips.

On Monday 04 April 2011, Jonathan Cameron wrote:
> On 04/04/11 13:02, Michael Hennerich wrote:
> >> 2) Flattening together of (some) of the chardevs (buffer related ones).
> >> As Arnd pointed out, there is really a use case for having multiple
> >> watershed type events from ring buffers. Much better to have a
> >> single one (be that at a controllable point). Firstly this removes
> >> the need for the event escalation code. Second, this single 'event'
> >> can then be indicated to user space purely using polling on the
> >> access chrdev. This approach does need an additional attribute to
> >> tell user space what the poll succeeding indicates (tbd).
> >>
> >> I haven't for now merged the ring handling with the more general
> >> event handling as Arnd suggested. This is for two reasons
> >> 1) It's much easier to debug this done in a couple of steps
> >> 2) The approach Arnd suggested may work well, but it is rather
> >> different to how other bits of the kernel pass event type data
> >> to user space. It's an interesting idea, but I'd rather any
> >> discussion of that approach was separate from the obviously
> >> big gains seen here.
> >>
> >> Patches 4, 5, 6, 7, 17
> >>
> > I appreciate the removal of the buffer event chardev. Adding support for
> > poll is also a good thing to do.
> > However support for a blocking read might also fit some use cases.
> Good point. I guess blocking on any content and poll for the watershead
> gives the best of both worlds. The blocking read is more down to the
> individual implementations than a core feature though - so one to do
> after this patch set.

You should implement both blocking and non-blocking read in the core, IMO.
This is how pipes generally work and what the opn()/read() man pages say it
works.

> >> 3) Reworking the triggering infrastructure to use 'virtual' irq_chips
> >> This approach was suggested by Thomas Gleixner.
> >> Before we had explicit trigger consumer lists. This made for a very
> >> clunky implementation when we considered moving things over to
> >> threaded interrupts. Thomas pointed out we were reinventing the
> >> wheel and suggested more or less what we have here (I hope ;)
> >>
> > Using threaded interrupts, greatly reduces use of additional workqueues
> > and excessive interrupt enable and disables.
> There is a nasty side issue here. What do we do if we are getting triggers
> faster than all of the consumers can work at? Right now things tend to
> stall. I think we just want to gracefully stop the relevant trigger
> if this happens. I'm not quite sure how we can notify userspace that this
> has happened... Perhaps POLLERR?

I'd say use POLLERR to signal to user space that something bad has happened,
then return the status in an ioctl().

> >> Patches 9 and 10 are minor rearrangements of code in the one
> >> driver I know of where the physical interrupt line for events
> >> is the same as that for data ready signals (though not at the
> >> same time).
> >>
> > I wouldn't consider this being a corner case. I know quite a few devices
> > that trigger data availability,
> > and other events from the same physical interrupt line, and they may do
> > it at the same time.
> If they do it at the same time things may get a bit nasty. Things are somewhat
> simpler after some of the later patches, as the irq requests are entirely
> handled in the drivers. Thus the driver could have one interrupt handler.
> The restriction will be that it would only be able to do nested irq calls
> limiting us to not having a top half for anything triggered from such an
> interrupt. This is because identifying whether we have a dataready or
> other event will require querying the device and hence sleeping. Note
> the sysfs trigger driver will also have this restriction (as posted yesterday).
>
> For devices where they share the line but cannot happen at the same time I'd
> prefer to do what we have in the lis3l02dq and completely separate the two
> uses of the interrupt line.

Can't you just have callback functions in the core that get called for a
specific event, and let the device driver take care of seperating the
sources?


Arnd

2011-04-04 14:52:42

by Hennerich, Michael

[permalink] [raw]
Subject: Re: [RFC PATCH 00/21] IIO: Channel registration rework, buffer chardev combining and rewrite of triggers as 'virtual' irq_chips.

On 04/04/2011 03:17 PM, Jonathan Cameron wrote:
> On 04/04/11 13:02, Michael Hennerich wrote:
>
>> On 03/31/2011 04:53 PM, Jonathan Cameron wrote:
>>
>>> Dear All,
>>>
>>> I'm afraid what in many ways makes sense as three separate
>>> series have gotten rather intertwined. I can probably unpick
>>> them but it will involve a lot of intermediate code in
>>> lis3l02dq (which is our fiddly example for this set) that I'd
>>> rather avoid. Hence lets have a guide to what various people
>>> might be interested in:
>>>
>>> 1) Channel registration rework (this has previous been in linux-iio
>>> but really comes into it's own after we remove various special
>>> cases later in this code).
>>>
>>> Patches 1, 2, 3, 21
>>> (8) - cleanups Arnd Bergmann pointed out in passing.
>>>
>>>
>> Good approach. It removes quite a bit on duplicated code across drivers.
>> At the moment I can't think of existing drivers that couldn't moved over
>> to the
>> new channel registration method.
>>
> There are a few that will require quite a bit more code - principally the
> light sensors with their rather odd channel naming. I'll do one of those
> shortly and see what we end up with.
>
>
>> However there are some limitations.
>> read_raw() value is currently type int, depending on the channel type,
>> int type might be too short.
>>
> True. How far do you think we should go? s64? I did wonder if it makes sense
> to have two value pointers (perhaps NULL) So base unit (val1) and
> decimal places of base unit (val2).
>
> So true raw values (e.g. sensor readings) will only set val1, but we have plenty
> of space for things like scale at sufficient accuracy. That also means we can
> flatten together the attributes in the core for both cases (not a great saving
> but nice to have none the less).
>
> What do you think?
>
64-bit arithmetic is a bit tricky on Linux. On some platforms you can't
use the native 64-bit divide.
You have to use do_div() instead. So I don't think we should always use
type s64.
As you proposed in your follow up email - depending on the return value
we can use val1 and val2.
>
>>
>>
>>> 2) Flattening together of (some) of the chardevs (buffer related ones).
>>> As Arnd pointed out, there is really a use case for having multiple
>>> watershed type events from ring buffers. Much better to have a
>>> single one (be that at a controllable point). Firstly this removes
>>> the need for the event escalation code. Second, this single 'event'
>>> can then be indicated to user space purely using polling on the
>>> access chrdev. This approach does need an additional attribute to
>>> tell user space what the poll succeeding indicates (tbd).
>>>
>>> I haven't for now merged the ring handling with the more general
>>> event handling as Arnd suggested. This is for two reasons
>>> 1) It's much easier to debug this done in a couple of steps
>>> 2) The approach Arnd suggested may work well, but it is rather
>>> different to how other bits of the kernel pass event type data
>>> to user space. It's an interesting idea, but I'd rather any
>>> discussion of that approach was separate from the obviously
>>> big gains seen here.
>>>
>>> Patches 4, 5, 6, 7, 17
>>>
>>>
>> I appreciate the removal of the buffer event chardev. Adding support for
>> poll is also a good thing to do.
>> However support for a blocking read might also fit some use cases.
>>
> Good point. I guess blocking on any content and poll for the watershead
> gives the best of both worlds. The blocking read is more down to the
> individual implementations than a core feature though - so one to do
> after this patch set.
>
>>
>>
>>> 3) Reworking the triggering infrastructure to use 'virtual' irq_chips
>>> This approach was suggested by Thomas Gleixner.
>>> Before we had explicit trigger consumer lists. This made for a very
>>> clunky implementation when we considered moving things over to
>>> threaded interrupts. Thomas pointed out we were reinventing the
>>> wheel and suggested more or less what we have here (I hope ;)
>>>
>>>
>> Using threaded interrupts, greatly reduces use of additional workqueues
>> and excessive interrupt enable and disables.
>>
> There is a nasty side issue here. What do we do if we are getting triggers
> faster than all of the consumers can work at? Right now things tend to
> stall. I think we just want to gracefully stop the relevant trigger
> if this happens. I'm not quite sure how we can notify userspace that this
> has happened... Perhaps POLLERR?
>
I think we had a similar problems before. We definitely need to signal
the error.
For poll, POLLERR seems to be the only suitable return value.
>>
>>
>>> Patches 9 and 10 are minor rearrangements of code in the one
>>> driver I know of where the physical interrupt line for events
>>> is the same as that for data ready signals (though not at the
>>> same time).
>>>
>>>
>> I wouldn't consider this being a corner case. I know quite a few devices
>> that trigger data availability,
>> and other events from the same physical interrupt line, and they may do
>> it at the same time.
>>
> If they do it at the same time things may get a bit nasty. Things are somewhat
> simpler after some of the later patches, as the irq requests are entirely
> handled in the drivers. Thus the driver could have one interrupt handler.
> The restriction will be that it would only be able to do nested irq calls
> limiting us to not having a top half for anything triggered from such an
> interrupt. This is because identifying whether we have a dataready or
> other event will require querying the device and hence sleeping. Note
> the sysfs trigger driver will also have this restriction (as posted yesterday).
>
> For devices where they share the line but cannot happen at the same time I'd
> prefer to do what we have in the lis3l02dq and completely separate the two
> uses of the interrupt line.
>
>
>>
>>
>>> In a rare situation we have complete control of these virtual
>>> interrupts within the subsystem. As such we want to be able to
>>> continue to build the subsystem as a module. This requires a
>>> couple of additional exports in the generic irq core code and
>>> also arm (for my test board anyway).
>>> Patches 13 and 14 make these changes. I hope they won't prove
>>> to controversial.
>>>
>>> Patch 15 adds a board info built in element to the IIO subsystem
>>> so we have a means of platform data telling us what interrupt
>>> numbers are available for us to play with. Does anyone have
>>> a better way of doing this? Patch 16 is an example of what
>>> needs to go in board files.
>>>
>>>
>> Since this is purely platform dependent, setting the irq pool from the
>> board setup looks acceptable to me, and depending on the arch or machine
>> it might be necessary two tweak some other defines.
>> However many arches define NR_IRQS always greater than actually used. So
>> why not make IR-Base a Kconfig option?
>>
> There is currently a nasty hack in the irq codes to deal with the fact that
> for at least some (maybe all) arm chips NR_IRQS is set to those on the SOC
> and doesn't include any others. The work around for that is that all the
> irq handling adds a chunk of padding. I would hope that will go away at
> some point in the future.
>
Back in 2009, when doing the ADP5520 MFD, I came to the same conclusion.
Sad to see that things are still the same.

http://kerneltrap.org/mailarchive/linux-kernel/2009/9/29/4492190/thread

> Otherwise, yes we could indeed make it a KCONFIG option subject to some
> fiddling with individual arches to make it work. This may be one to tackle
> when moving out of staging rather than now though.
>
> Perhaps we need to try it on a few architectures and see what is needed on each
>
> Thanks for taking a look!
>
> Jonathan
>
>
>


--
Greetings,
Michael

--
Analog Devices GmbH Wilhelm-Wagenfeld-Str. 6 80807 Muenchen
Sitz der Gesellschaft: Muenchen; Registergericht: Muenchen HRB 40368;
Geschaeftsfuehrer:Dr.Carsten Suckrow, Thomas Wessel, William A. Martin,
Margaret Seif

2011-04-04 17:49:34

by Jonathan Cameron

[permalink] [raw]
Subject: Re: [RFC PATCH 00/21] IIO: Channel registration rework, buffer chardev combining and rewrite of triggers as 'virtual' irq_chips.

On 04/04/11 15:49, Arnd Bergmann wrote:
> On Monday 04 April 2011, Jonathan Cameron wrote:
>> On 04/04/11 13:02, Michael Hennerich wrote:
>>>> 2) Flattening together of (some) of the chardevs (buffer related ones).
>>>> As Arnd pointed out, there is really a use case for having multiple
>>>> watershed type events from ring buffers. Much better to have a
>>>> single one (be that at a controllable point). Firstly this removes
>>>> the need for the event escalation code. Second, this single 'event'
>>>> can then be indicated to user space purely using polling on the
>>>> access chrdev. This approach does need an additional attribute to
>>>> tell user space what the poll succeeding indicates (tbd).
>>>>
>>>> I haven't for now merged the ring handling with the more general
>>>> event handling as Arnd suggested. This is for two reasons
>>>> 1) It's much easier to debug this done in a couple of steps
>>>> 2) The approach Arnd suggested may work well, but it is rather
>>>> different to how other bits of the kernel pass event type data
>>>> to user space. It's an interesting idea, but I'd rather any
>>>> discussion of that approach was separate from the obviously
>>>> big gains seen here.
>>>>
>>>> Patches 4, 5, 6, 7, 17
>>>>
>>> I appreciate the removal of the buffer event chardev. Adding support for
>>> poll is also a good thing to do.
>>> However support for a blocking read might also fit some use cases.
>> Good point. I guess blocking on any content and poll for the watershead
>> gives the best of both worlds. The blocking read is more down to the
>> individual implementations than a core feature though - so one to do
>> after this patch set.
>
7> You should implement both blocking and non-blocking read in the core, IMO.
> This is how pipes generally work and what the opn()/read() man pages say it
> works.
I misphrased what I said above. The key thing here is that this is partly
core support and partly a matter of individual buffer implementations being
able to support this. Note we are playing unconventional games with poll
as the poll will only succeed when a threshold on how much data is in the
buffer is passed (not this threshold may be 1, but usually isn't).
In some buffers we don't know if there is new data until this value is passed
or a hardware read occurs.

My feeling is we should have blocking reads block on the absense of any data not
on the threshold as we are doing with poll. Thus not all buffers will support
blocking reads - if they don't the core should fail on the attempt to open
the character devicce.

Anyhow, all I was really suggesting is that this is postponed for now
and handled in a later patch set. I'm far from keen to keep adding stuff
to ring_sw given I want to ditch the thing entirely! What we have in this
series to handle poll in that buffer implementation is a pretty dirty hack.

>
>>>> 3) Reworking the triggering infrastructure to use 'virtual' irq_chips
>>>> This approach was suggested by Thomas Gleixner.
>>>> Before we had explicit trigger consumer lists. This made for a very
>>>> clunky implementation when we considered moving things over to
>>>> threaded interrupts. Thomas pointed out we were reinventing the
>>>> wheel and suggested more or less what we have here (I hope ;)
>>>>
>>> Using threaded interrupts, greatly reduces use of additional workqueues
>>> and excessive interrupt enable and disables.
>> There is a nasty side issue here. What do we do if we are getting triggers
>> faster than all of the consumers can work at? Right now things tend to
>> stall. I think we just want to gracefully stop the relevant trigger
>> if this happens. I'm not quite sure how we can notify userspace that this
>> has happened... Perhaps POLLERR?
>
> I'd say use POLLERR to signal to user space that something bad has happened,
> then return the status in an ioctl().
Yup, that's what I had in mind. Again, may be in a later patch set. This one
is pretty unmanageable as it is!
>
>>>> Patches 9 and 10 are minor rearrangements of code in the one
>>>> driver I know of where the physical interrupt line for events
>>>> is the same as that for data ready signals (though not at the
>>>> same time).
>>>>
>>> I wouldn't consider this being a corner case. I know quite a few devices
>>> that trigger data availability,
>>> and other events from the same physical interrupt line, and they may do
>>> it at the same time.
>> If they do it at the same time things may get a bit nasty. Things are somewhat
>> simpler after some of the later patches, as the irq requests are entirely
>> handled in the drivers. Thus the driver could have one interrupt handler.
>> The restriction will be that it would only be able to do nested irq calls
>> limiting us to not having a top half for anything triggered from such an
>> interrupt. This is because identifying whether we have a dataready or
>> other event will require querying the device and hence sleeping. Note
>> the sysfs trigger driver will also have this restriction (as posted yesterday).
>>
>> For devices where they share the line but cannot happen at the same time I'd
>> prefer to do what we have in the lis3l02dq and completely separate the two
>> uses of the interrupt line.
>
> Can't you just have callback functions in the core that get called for a
> specific event, and let the device driver take care of seperating the
> sources?
>
That's more or less what is happening currently, it's just occurring via two
separate interrupt handlers only one of which is active at a time. This was
mainly done because it allowed me to separate the rewriting of the buffer
handling entirely from that of the events.

To summarize the situation for the lis3l02dq

1) If dataready is enabled no other events are generated (hardware constraint).
2) Other events require a bus read to identify them (hence sleep). Technically
the driver can know if there is only one such event, but it still needs to
acknowledge it.

The dataready acts as an input to the irq_chip based trigger demultiplexing.
As no sleep is needed, we can do non nested irqs, thus allowing other devices
that trigger off this to have top halves. These top halves are typically used
for flipping gpio connected 'capture now' lines. If the device doesn't have one
of these it typically only has a thread function. Note, if you need to sleep,
to trigger a hardware capture pin then it defeats the object anyway!

Thus by the end of the set this thread contains, the event interrupt and the
dataready interrupts were treated as two separate entities that just happened
to use the same physical wire (but not at the same time).

An alternative is the following applied on top of this series (more or less,
it's on my working tree so there might be some fuzz).
I guess this is marginally cleaner so might as well go with it. Technically
I had the events as oneshot before and they aren't any more. They didn't
actually need to be.

[PATCH] staging:iio:lis3l02dq remerge the two interrupt handlers.

Does add a small burden to both handlers, but the gain is somewhat
simpler code.

Signed-off-by: Jonathan Cameron <[email protected]>
---
drivers/staging/iio/accel/lis3l02dq.h | 5 +++
drivers/staging/iio/accel/lis3l02dq_core.c | 50 +++++++++++----------------
drivers/staging/iio/accel/lis3l02dq_ring.c | 29 +++++++---------
3 files changed, 38 insertions(+), 46 deletions(-)

diff --git a/drivers/staging/iio/accel/lis3l02dq.h b/drivers/staging/iio/accel/lis3l02dq.h
index e7f64bd..b0ace13 100644
--- a/drivers/staging/iio/accel/lis3l02dq.h
+++ b/drivers/staging/iio/accel/lis3l02dq.h
@@ -162,6 +162,7 @@ struct lis3l02dq_state {
u8 *tx;
u8 *rx;
struct mutex buf_lock;
+ bool trigger_on;
};

#define lis3l02dq_h_to_s(_h) \
@@ -202,7 +203,11 @@ void lis3l02dq_unconfigure_ring(struct iio_dev *indio_dev);
#define lis3l02dq_alloc_buf iio_kfifo_allocate
#define lis3l02dq_register_buf_funcs iio_kfifo_register_funcs
#endif
+irqreturn_t lis3l02dq_data_rdy_trig_poll(int irq, void *private);
+#define lis3l02dq_th lis3l02dq_data_rdy_trig_poll
+
#else /* CONFIG_IIO_RING_BUFFER */
+#define lis3l02dq_th lis3l02dq_noring

static inline void lis3l02dq_remove_trigger(struct iio_dev *indio_dev)
{
diff --git a/drivers/staging/iio/accel/lis3l02dq_core.c b/drivers/staging/iio/accel/lis3l02dq_core.c
index 46f0633..9a2f870 100644
--- a/drivers/staging/iio/accel/lis3l02dq_core.c
+++ b/drivers/staging/iio/accel/lis3l02dq_core.c
@@ -37,6 +37,11 @@
* It's in the likely to be added comment at the top of spi.h.
* This means that use cannot be made of spi_write etc.
*/
+/* direct copy of the irq_default_primary_handler */
+static irqreturn_t lis3l02dq_noring(int irq, void *private)
+{
+ return IRQ_WAKE_THREAD;
+}

/**
* lis3l02dq_spi_read_reg_8() - read single byte from a single register
@@ -533,19 +538,13 @@ static ssize_t lis3l02dq_read_event_config(struct iio_dev *indio_dev,

int lis3l02dq_disable_all_events(struct iio_dev *indio_dev)
{
- struct iio_sw_ring_helper_state *h
- = iio_dev_get_devdata(indio_dev);
- struct lis3l02dq_state *st = lis3l02dq_h_to_s(h);
int ret;
u8 control, val;
- bool irqtofree;

ret = lis3l02dq_spi_read_reg_8(indio_dev,
LIS3L02DQ_REG_CTRL_2_ADDR,
&control);

- irqtofree = !!(control & LIS3L02DQ_REG_CTRL_2_ENABLE_INTERRUPT);
-
control &= ~LIS3L02DQ_REG_CTRL_2_ENABLE_INTERRUPT;
ret = lis3l02dq_spi_write_reg_8(indio_dev,
LIS3L02DQ_REG_CTRL_2_ADDR,
@@ -566,9 +565,6 @@ int lis3l02dq_disable_all_events(struct iio_dev *indio_dev)
if (ret)
goto error_ret;

- if (irqtofree)
- free_irq(st->us->irq, indio_dev);
-
ret = control;
error_ret:
return ret;
@@ -578,9 +574,6 @@ static int lis3l02dq_write_event_config(struct iio_dev *indio_dev,
int event_code,
int state)
{
- struct iio_sw_ring_helper_state *h
- = iio_dev_get_devdata(indio_dev);
- struct lis3l02dq_state *st = lis3l02dq_h_to_s(h);
int ret = 0;
u8 val, control;
u8 currentlyset;
@@ -612,18 +605,6 @@ static int lis3l02dq_write_event_config(struct iio_dev *indio_dev,
}

if (changed) {
- if (!(control & LIS3L02DQ_REG_CTRL_2_ENABLE_INTERRUPT)) {
- ret = request_threaded_irq(st->us->irq,
- NULL,
- &lis3l02dq_event_handler,
- IRQF_TRIGGER_RISING |
- IRQF_ONESHOT,
- "lis3l02dq_event",
- indio_dev);
- if (ret)
- goto error_ret;
- }
-
ret = lis3l02dq_spi_write_reg_8(indio_dev,
LIS3L02DQ_REG_WAKE_UP_CFG_ADDR,
&val);
@@ -637,10 +618,6 @@ static int lis3l02dq_write_event_config(struct iio_dev *indio_dev,
&control);
if (ret)
goto error_ret;
-
- /* remove interrupt handler if nothing is still on */
- if (!(val & 0x3f))
- free_irq(st->us->irq, indio_dev);
}

error_ret:
@@ -725,9 +702,18 @@ static int __devinit lis3l02dq_probe(struct spi_device *spi)
}

if (spi->irq && gpio_is_valid(irq_to_gpio(spi->irq)) > 0) {
- ret = lis3l02dq_probe_trigger(st->help.indio_dev);
+ ret = request_threaded_irq(st->us->irq,
+ &lis3l02dq_th,
+ &lis3l02dq_event_handler,
+ IRQF_TRIGGER_RISING,
+ "lis3l02dq",
+ st->help.indio_dev);
if (ret)
goto error_uninitialize_ring;
+
+ ret = lis3l02dq_probe_trigger(st->help.indio_dev);
+ if (ret)
+ goto error_free_interrupt;
}

/* Get the device into a sane initial state */
@@ -739,6 +725,9 @@ static int __devinit lis3l02dq_probe(struct spi_device *spi)
error_remove_trigger:
if (st->help.indio_dev->modes & INDIO_RING_TRIGGERED)
lis3l02dq_remove_trigger(st->help.indio_dev);
+error_free_interrupt:
+ if (spi->irq && gpio_is_valid(irq_to_gpio(spi->irq)) > 0)
+ free_irq(st->us->irq, st->help.indio_dev);
error_uninitialize_ring:
iio_ring_buffer_unregister(st->help.indio_dev->ring);
error_unreg_ring_funcs:
@@ -801,7 +790,8 @@ static int lis3l02dq_remove(struct spi_device *spi)
goto err_ret;

flush_scheduled_work();
-
+ if (spi->irq && gpio_is_valid(irq_to_gpio(spi->irq)) > 0)
+ free_irq(st->us->irq, indio_dev);
lis3l02dq_remove_trigger(indio_dev);
iio_ring_buffer_unregister(indio_dev->ring);
lis3l02dq_unconfigure_ring(indio_dev);
diff --git a/drivers/staging/iio/accel/lis3l02dq_ring.c b/drivers/staging/iio/accel/lis3l02dq_ring.c
index a611778..de2c602 100644
--- a/drivers/staging/iio/accel/lis3l02dq_ring.c
+++ b/drivers/staging/iio/accel/lis3l02dq_ring.c
@@ -31,11 +31,17 @@ static inline u16 combine_8_to_16(u8 lower, u8 upper)
/**
* lis3l02dq_data_rdy_trig_poll() the event handler for the data rdy trig
**/
-static irqreturn_t lis3l02dq_data_rdy_trig_poll(int irq, void *private)
+irqreturn_t lis3l02dq_data_rdy_trig_poll(int irq, void *private)
{
- iio_trigger_poll(private, iio_get_time_ns());
+ struct iio_dev *indio_dev = private;
+ struct iio_sw_ring_helper_state *h = iio_dev_get_devdata(indio_dev);
+ struct lis3l02dq_state *st = lis3l02dq_h_to_s(h);

- return IRQ_HANDLED;
+ if (st->trigger_on) {
+ iio_trigger_poll(st->trig, iio_get_time_ns());
+ return IRQ_HANDLED;
+ } else
+ return IRQ_WAKE_THREAD;
}

/**
@@ -203,8 +209,7 @@ __lis3l02dq_write_data_ready_config(struct device *dev, bool state)
&valold);
if (ret)
goto error_ret;
-
- free_irq(st->us->irq, st->trig);
+ st->trigger_on = false;
/* Enable requested */
} else if (state && !currentlyset) {
/* if not set, enable requested */
@@ -215,22 +220,13 @@ __lis3l02dq_write_data_ready_config(struct device *dev, bool state)

valold = ret |
LIS3L02DQ_REG_CTRL_2_ENABLE_DATA_READY_GENERATION;
- ret = request_irq(st->us->irq,
- lis3l02dq_data_rdy_trig_poll,
- IRQF_TRIGGER_RISING, "lis3l02dq_datardy",
- st->trig);
- if (ret)
- goto error_ret;
-
+ st->trigger_on = true;
ret = lis3l02dq_spi_write_reg_8(indio_dev,
LIS3L02DQ_REG_CTRL_2_ADDR,
&valold);
- if (ret) {
- free_irq(st->us->irq, st->trig);
+ if (ret)
goto error_ret;
- }
}

return 0;

--
1.7.3.4

2011-04-04 18:07:34

by Jonathan Cameron

[permalink] [raw]
Subject: Re: [RFC PATCH 00/21] IIO: Channel registration rework, buffer chardev combining and rewrite of triggers as 'virtual' irq_chips.

....
>>
>>> However there are some limitations.
>>> read_raw() value is currently type int, depending on the channel type,
>>> int type might be too short.
>>>
>> True. How far do you think we should go? s64? I did wonder if it makes sense
>> to have two value pointers (perhaps NULL) So base unit (val1) and
>> decimal places of base unit (val2).
>>
>> So true raw values (e.g. sensor readings) will only set val1, but we have plenty
>> of space for things like scale at sufficient accuracy. That also means we can
>> flatten together the attributes in the core for both cases (not a great saving
>> but nice to have none the less).
>>
>> What do you think?
>>
> 64-bit arithmetic is a bit tricky on Linux. On some platforms you can't
> use the native 64-bit divide.
> You have to use do_div() instead. So I don't think we should always use
> type s64.
> As you proposed in your follow up email - depending on the return value
> we can use val1 and val2.
Cool, I'll give that a go. I'll also blindly port a few more drivers
over to the new framework and see where problems occur.
>>>
>>>
>>>> Patches 9 and 10 are minor rearrangements of code in the one
>>>> driver I know of where the physical interrupt line for events
>>>> is the same as that for data ready signals (though not at the
>>>> same time).
>>>>
>>>>
>>> I wouldn't consider this being a corner case. I know quite a few devices
>>> that trigger data availability,
>>> and other events from the same physical interrupt line, and they may do
>>> it at the same time.
>>>
>> If they do it at the same time things may get a bit nasty. Things are somewhat
>> simpler after some of the later patches, as the irq requests are entirely
>> handled in the drivers. Thus the driver could have one interrupt handler.
>> The restriction will be that it would only be able to do nested irq calls
>> limiting us to not having a top half for anything triggered from such an
>> interrupt. This is because identifying whether we have a dataready or
>> other event will require querying the device and hence sleeping. Note
>> the sysfs trigger driver will also have this restriction (as posted yesterday).
>>
>> For devices where they share the line but cannot happen at the same time I'd
>> prefer to do what we have in the lis3l02dq and completely separate the two
>> uses of the interrupt line.
I've been persuaded otherwise ;) See other branch of thread.
>>
>>
>>>
>>>
>>>> In a rare situation we have complete control of these virtual
>>>> interrupts within the subsystem. As such we want to be able to
>>>> continue to build the subsystem as a module. This requires a
>>>> couple of additional exports in the generic irq core code and
>>>> also arm (for my test board anyway).
>>>> Patches 13 and 14 make these changes. I hope they won't prove
>>>> to controversial.
>>>>
>>>> Patch 15 adds a board info built in element to the IIO subsystem
>>>> so we have a means of platform data telling us what interrupt
>>>> numbers are available for us to play with. Does anyone have
>>>> a better way of doing this? Patch 16 is an example of what
>>>> needs to go in board files.
>>>>
>>>>
>>> Since this is purely platform dependent, setting the irq pool from the
>>> board setup looks acceptable to me, and depending on the arch or machine
>>> it might be necessary two tweak some other defines.
>>> However many arches define NR_IRQS always greater than actually used. So
>>> why not make IR-Base a Kconfig option?
>>>
>> There is currently a nasty hack in the irq codes to deal with the fact that
>> for at least some (maybe all) arm chips NR_IRQS is set to those on the SOC
>> and doesn't include any others. The work around for that is that all the
>> irq handling adds a chunk of padding. I would hope that will go away at
>> some point in the future.
>>
> Back in 2009, when doing the ADP5520 MFD, I came to the same conclusion.
> Sad to see that things are still the same.
>
> http://kerneltrap.org/mailarchive/linux-kernel/2009/9/29/4492190/thread
Yes. I guess that fuzz has to happen somewhere even if it is just a case
of platforms defining it to be big enough for all known boards (which is
hideous).
...

2011-04-11 18:36:43

by Jonathan Cameron

[permalink] [raw]
Subject: Re: [RFC PATCH 00/21] IIO: Channel registration rework, buffer chardev combining and rewrite of triggers as 'virtual' irq_chips.


Just a quick heads up. I am not intending to repost this series
to lkml. Given with carrying drivers through past
"staging:iio: Remove legacy event handling" has resulted
in over 50 patches that are mostly very much driver specific,
it seems silly to bombard the general list.

I will be posting that set on linux-iio if anyone is interested
(after a little more cleaning up).

I will cherry pick a few bits such as the additional
exports from genirq to post to lkml.

Seems rude to drop a huge patch set on everyone when
only a few corners will be of general interest.

Jonathan