diff options
Diffstat (limited to 'drivers/iio/imu/adis_buffer.c')
-rw-r--r-- | drivers/iio/imu/adis_buffer.c | 159 |
1 files changed, 159 insertions, 0 deletions
diff --git a/drivers/iio/imu/adis_buffer.c b/drivers/iio/imu/adis_buffer.c new file mode 100644 index 000000000000..a91b4cbdc73a --- /dev/null +++ b/drivers/iio/imu/adis_buffer.c | |||
@@ -0,0 +1,159 @@ | |||
1 | /* | ||
2 | * Common library for ADIS16XXX devices | ||
3 | * | ||
4 | * Copyright 2012 Analog Devices Inc. | ||
5 | * Author: Lars-Peter Clausen <lars@metafoo.de> | ||
6 | * | ||
7 | * Licensed under the GPL-2 or later. | ||
8 | */ | ||
9 | |||
10 | #include <linux/export.h> | ||
11 | #include <linux/interrupt.h> | ||
12 | #include <linux/mutex.h> | ||
13 | #include <linux/kernel.h> | ||
14 | #include <linux/spi/spi.h> | ||
15 | #include <linux/slab.h> | ||
16 | |||
17 | #include <linux/iio/iio.h> | ||
18 | #include <linux/iio/buffer.h> | ||
19 | #include <linux/iio/trigger_consumer.h> | ||
20 | #include <linux/iio/triggered_buffer.h> | ||
21 | #include <linux/iio/imu/adis.h> | ||
22 | |||
23 | int adis_update_scan_mode(struct iio_dev *indio_dev, | ||
24 | const unsigned long *scan_mask) | ||
25 | { | ||
26 | struct adis *adis = iio_device_get_drvdata(indio_dev); | ||
27 | const struct iio_chan_spec *chan; | ||
28 | unsigned int scan_count; | ||
29 | unsigned int i, j; | ||
30 | __be16 *tx, *rx; | ||
31 | |||
32 | kfree(adis->xfer); | ||
33 | kfree(adis->buffer); | ||
34 | |||
35 | scan_count = indio_dev->scan_bytes / 2; | ||
36 | |||
37 | adis->xfer = kcalloc(scan_count + 1, sizeof(*adis->xfer), GFP_KERNEL); | ||
38 | if (!adis->xfer) | ||
39 | return -ENOMEM; | ||
40 | |||
41 | adis->buffer = kzalloc(indio_dev->scan_bytes * 2, GFP_KERNEL); | ||
42 | if (!adis->buffer) | ||
43 | return -ENOMEM; | ||
44 | |||
45 | rx = adis->buffer; | ||
46 | tx = rx + indio_dev->scan_bytes; | ||
47 | |||
48 | spi_message_init(&adis->msg); | ||
49 | |||
50 | for (j = 0; j <= scan_count; j++) { | ||
51 | adis->xfer[j].bits_per_word = 8; | ||
52 | if (j != scan_count) | ||
53 | adis->xfer[j].cs_change = 1; | ||
54 | adis->xfer[j].len = 2; | ||
55 | adis->xfer[j].delay_usecs = adis->data->read_delay; | ||
56 | if (j < scan_count) | ||
57 | adis->xfer[j].tx_buf = &tx[j]; | ||
58 | if (j >= 1) | ||
59 | adis->xfer[j].rx_buf = &rx[j - 1]; | ||
60 | spi_message_add_tail(&adis->xfer[j], &adis->msg); | ||
61 | } | ||
62 | |||
63 | chan = indio_dev->channels; | ||
64 | for (i = 0; i < indio_dev->num_channels; i++, chan++) { | ||
65 | if (!test_bit(chan->scan_index, scan_mask)) | ||
66 | continue; | ||
67 | *tx++ = cpu_to_be16(chan->address << 8); | ||
68 | } | ||
69 | |||
70 | return 0; | ||
71 | } | ||
72 | EXPORT_SYMBOL_GPL(adis_update_scan_mode); | ||
73 | |||
74 | static irqreturn_t adis_trigger_handler(int irq, void *p) | ||
75 | { | ||
76 | struct iio_poll_func *pf = p; | ||
77 | struct iio_dev *indio_dev = pf->indio_dev; | ||
78 | struct adis *adis = iio_device_get_drvdata(indio_dev); | ||
79 | int ret; | ||
80 | |||
81 | if (!adis->buffer) | ||
82 | return -ENOMEM; | ||
83 | |||
84 | ret = spi_sync(adis->spi, &adis->msg); | ||
85 | if (ret) | ||
86 | dev_err(&adis->spi->dev, "Failed to read data: %d", ret); | ||
87 | |||
88 | /* Guaranteed to be aligned with 8 byte boundary */ | ||
89 | if (indio_dev->scan_timestamp) { | ||
90 | void *b = adis->buffer + indio_dev->scan_bytes - sizeof(s64); | ||
91 | *(s64 *)b = pf->timestamp; | ||
92 | } | ||
93 | |||
94 | iio_push_to_buffers(indio_dev, adis->buffer); | ||
95 | |||
96 | iio_trigger_notify_done(indio_dev->trig); | ||
97 | |||
98 | return IRQ_HANDLED; | ||
99 | } | ||
100 | |||
101 | /** | ||
102 | * adis_setup_buffer_and_trigger() - Sets up buffer and trigger for the adis device | ||
103 | * @adis: The adis device. | ||
104 | * @indio_dev: The IIO device. | ||
105 | * @trigger_handler: Optional trigger handler, may be NULL. | ||
106 | * | ||
107 | * Returns 0 on success, a negative error code otherwise. | ||
108 | * | ||
109 | * This function sets up the buffer and trigger for a adis devices. If | ||
110 | * 'trigger_handler' is NULL the default trigger handler will be used. The | ||
111 | * default trigger handler will simply read the registers assigned to the | ||
112 | * currently active channels. | ||
113 | * | ||
114 | * adis_cleanup_buffer_and_trigger() should be called to free the resources | ||
115 | * allocated by this function. | ||
116 | */ | ||
117 | int adis_setup_buffer_and_trigger(struct adis *adis, struct iio_dev *indio_dev, | ||
118 | irqreturn_t (*trigger_handler)(int, void *)) | ||
119 | { | ||
120 | int ret; | ||
121 | |||
122 | if (!trigger_handler) | ||
123 | trigger_handler = adis_trigger_handler; | ||
124 | |||
125 | ret = iio_triggered_buffer_setup(indio_dev, &iio_pollfunc_store_time, | ||
126 | trigger_handler, NULL); | ||
127 | if (ret) | ||
128 | return ret; | ||
129 | |||
130 | if (adis->spi->irq) { | ||
131 | ret = adis_probe_trigger(adis, indio_dev); | ||
132 | if (ret) | ||
133 | goto error_buffer_cleanup; | ||
134 | } | ||
135 | return 0; | ||
136 | |||
137 | error_buffer_cleanup: | ||
138 | iio_triggered_buffer_cleanup(indio_dev); | ||
139 | return ret; | ||
140 | } | ||
141 | EXPORT_SYMBOL_GPL(adis_setup_buffer_and_trigger); | ||
142 | |||
143 | /** | ||
144 | * adis_cleanup_buffer_and_trigger() - Free buffer and trigger resources | ||
145 | * @adis: The adis device. | ||
146 | * @indio_dev: The IIO device. | ||
147 | * | ||
148 | * Frees resources allocated by adis_setup_buffer_and_trigger() | ||
149 | */ | ||
150 | void adis_cleanup_buffer_and_trigger(struct adis *adis, | ||
151 | struct iio_dev *indio_dev) | ||
152 | { | ||
153 | if (adis->spi->irq) | ||
154 | adis_remove_trigger(adis); | ||
155 | kfree(adis->buffer); | ||
156 | kfree(adis->xfer); | ||
157 | iio_triggered_buffer_cleanup(indio_dev); | ||
158 | } | ||
159 | EXPORT_SYMBOL_GPL(adis_cleanup_buffer_and_trigger); | ||