diff options
author | Peter Meerwald <pmeerw@pmeerw.net> | 2013-11-12 14:43:00 -0500 |
---|---|---|
committer | Jonathan Cameron <jic23@kernel.org> | 2013-11-24 16:07:13 -0500 |
commit | cc26ad455f57b42dd40ca01c4c220516218390f6 (patch) | |
tree | e43d2645431f66998c6e38a0f74a9ef0f25cc5b3 /drivers/iio/pressure | |
parent | a35e1fd26826faa5595bcb4c8ab370f331221ff7 (diff) |
iio: Add Freescale MPL3115A2 pressure / temperature sensor driver
I2C-controlled MEMS sensor with 20-bit pressure measurement (pascal) and
12-bit temperature measurement
driver only exposes basic functionality, see TODO remarks
datasheet: http://cache.freescale.com/files/sensors/doc/data_sheet/MPL3115A2.pdf
v2:
* store 20-bit value in 32-bit buffer element (instead of 24-bit)
* zero buffer to prevent kernel data leak to userspace
* fix mutex unlock in trigger handler (thanks Andi Shyti)
Signed-off-by: Peter Meerwald <pmeerw@pmeerw.net>
Reviewed-by: Andi Shyti <andi@etezian.org>
Signed-off-by: Jonathan Cameron <jic23@kernel.org>
Diffstat (limited to 'drivers/iio/pressure')
-rw-r--r-- | drivers/iio/pressure/Kconfig | 12 | ||||
-rw-r--r-- | drivers/iio/pressure/Makefile | 1 | ||||
-rw-r--r-- | drivers/iio/pressure/mpl3115.c | 329 |
3 files changed, 342 insertions, 0 deletions
diff --git a/drivers/iio/pressure/Kconfig b/drivers/iio/pressure/Kconfig index 4f2e0f9bad8c..a8b9cae5c173 100644 --- a/drivers/iio/pressure/Kconfig +++ b/drivers/iio/pressure/Kconfig | |||
@@ -5,6 +5,18 @@ | |||
5 | 5 | ||
6 | menu "Pressure sensors" | 6 | menu "Pressure sensors" |
7 | 7 | ||
8 | config MPL3115 | ||
9 | tristate "Freescale MPL3115A2 pressure sensor driver" | ||
10 | depends on I2C | ||
11 | select IIO_BUFFER | ||
12 | select IIO_TRIGGERED_BUFFER | ||
13 | help | ||
14 | Say yes here to build support for the Freescale MPL3115A2 | ||
15 | pressure sensor / altimeter. | ||
16 | |||
17 | To compile this driver as a module, choose M here: the module | ||
18 | will be called mpl3115. | ||
19 | |||
8 | config IIO_ST_PRESS | 20 | config IIO_ST_PRESS |
9 | tristate "STMicroelectronics pressure sensor Driver" | 21 | tristate "STMicroelectronics pressure sensor Driver" |
10 | depends on (I2C || SPI_MASTER) && SYSFS | 22 | depends on (I2C || SPI_MASTER) && SYSFS |
diff --git a/drivers/iio/pressure/Makefile b/drivers/iio/pressure/Makefile index be71464c2752..42bb9fcf5436 100644 --- a/drivers/iio/pressure/Makefile +++ b/drivers/iio/pressure/Makefile | |||
@@ -3,6 +3,7 @@ | |||
3 | # | 3 | # |
4 | 4 | ||
5 | # When adding new entries keep the list in alphabetical order | 5 | # When adding new entries keep the list in alphabetical order |
6 | obj-$(CONFIG_MPL3115) += mpl3115.o | ||
6 | obj-$(CONFIG_IIO_ST_PRESS) += st_pressure.o | 7 | obj-$(CONFIG_IIO_ST_PRESS) += st_pressure.o |
7 | st_pressure-y := st_pressure_core.o | 8 | st_pressure-y := st_pressure_core.o |
8 | st_pressure-$(CONFIG_IIO_BUFFER) += st_pressure_buffer.o | 9 | st_pressure-$(CONFIG_IIO_BUFFER) += st_pressure_buffer.o |
diff --git a/drivers/iio/pressure/mpl3115.c b/drivers/iio/pressure/mpl3115.c new file mode 100644 index 000000000000..ac8c8ab723e5 --- /dev/null +++ b/drivers/iio/pressure/mpl3115.c | |||
@@ -0,0 +1,329 @@ | |||
1 | /* | ||
2 | * mpl3115.c - Support for Freescale MPL3115A2 pressure/temperature sensor | ||
3 | * | ||
4 | * Copyright (c) 2013 Peter Meerwald <pmeerw@pmeerw.net> | ||
5 | * | ||
6 | * This file is subject to the terms and conditions of version 2 of | ||
7 | * the GNU General Public License. See the file COPYING in the main | ||
8 | * directory of this archive for more details. | ||
9 | * | ||
10 | * (7-bit I2C slave address 0x60) | ||
11 | * | ||
12 | * TODO: FIFO buffer, altimeter mode, oversampling, continuous mode, | ||
13 | * interrupts, user offset correction, raw mode | ||
14 | */ | ||
15 | |||
16 | #include <linux/module.h> | ||
17 | #include <linux/i2c.h> | ||
18 | #include <linux/iio/iio.h> | ||
19 | #include <linux/iio/sysfs.h> | ||
20 | #include <linux/iio/trigger_consumer.h> | ||
21 | #include <linux/iio/buffer.h> | ||
22 | #include <linux/iio/triggered_buffer.h> | ||
23 | #include <linux/delay.h> | ||
24 | |||
25 | #define MPL3115_STATUS 0x00 | ||
26 | #define MPL3115_OUT_PRESS 0x01 /* MSB first, 20 bit */ | ||
27 | #define MPL3115_OUT_TEMP 0x04 /* MSB first, 12 bit */ | ||
28 | #define MPL3115_WHO_AM_I 0x0c | ||
29 | #define MPL3115_CTRL_REG1 0x26 | ||
30 | |||
31 | #define MPL3115_DEVICE_ID 0xc4 | ||
32 | |||
33 | #define MPL3115_STATUS_PRESS_RDY BIT(2) | ||
34 | #define MPL3115_STATUS_TEMP_RDY BIT(1) | ||
35 | |||
36 | #define MPL3115_CTRL_RESET BIT(2) /* software reset */ | ||
37 | #define MPL3115_CTRL_OST BIT(1) /* initiate measurement */ | ||
38 | #define MPL3115_CTRL_ACTIVE BIT(0) /* continuous measurement */ | ||
39 | #define MPL3115_CTRL_OS_258MS (BIT(5) | BIT(4)) /* 64x oversampling */ | ||
40 | |||
41 | struct mpl3115_data { | ||
42 | struct i2c_client *client; | ||
43 | struct mutex lock; | ||
44 | u8 ctrl_reg1; | ||
45 | }; | ||
46 | |||
47 | static int mpl3115_request(struct mpl3115_data *data) | ||
48 | { | ||
49 | int ret, tries = 15; | ||
50 | |||
51 | /* trigger measurement */ | ||
52 | ret = i2c_smbus_write_byte_data(data->client, MPL3115_CTRL_REG1, | ||
53 | data->ctrl_reg1 | MPL3115_CTRL_OST); | ||
54 | if (ret < 0) | ||
55 | return ret; | ||
56 | |||
57 | while (tries-- > 0) { | ||
58 | ret = i2c_smbus_read_byte_data(data->client, MPL3115_CTRL_REG1); | ||
59 | if (ret < 0) | ||
60 | return ret; | ||
61 | /* wait for data ready, i.e. OST cleared */ | ||
62 | if (!(ret & MPL3115_CTRL_OST)) | ||
63 | break; | ||
64 | msleep(20); | ||
65 | } | ||
66 | |||
67 | if (tries < 0) { | ||
68 | dev_err(&data->client->dev, "data not ready\n"); | ||
69 | return -EIO; | ||
70 | } | ||
71 | |||
72 | return 0; | ||
73 | } | ||
74 | |||
75 | static int mpl3115_read_raw(struct iio_dev *indio_dev, | ||
76 | struct iio_chan_spec const *chan, | ||
77 | int *val, int *val2, long mask) | ||
78 | { | ||
79 | struct mpl3115_data *data = iio_priv(indio_dev); | ||
80 | s32 tmp = 0; | ||
81 | int ret; | ||
82 | |||
83 | switch (mask) { | ||
84 | case IIO_CHAN_INFO_RAW: | ||
85 | if (iio_buffer_enabled(indio_dev)) | ||
86 | return -EBUSY; | ||
87 | |||
88 | switch (chan->type) { | ||
89 | case IIO_PRESSURE: /* in 0.25 pascal / LSB */ | ||
90 | mutex_lock(&data->lock); | ||
91 | ret = mpl3115_request(data); | ||
92 | if (ret < 0) { | ||
93 | mutex_unlock(&data->lock); | ||
94 | return ret; | ||
95 | } | ||
96 | ret = i2c_smbus_read_i2c_block_data(data->client, | ||
97 | MPL3115_OUT_PRESS, 3, (u8 *) &tmp); | ||
98 | mutex_unlock(&data->lock); | ||
99 | if (ret < 0) | ||
100 | return ret; | ||
101 | *val = sign_extend32(be32_to_cpu(tmp) >> 12, 23); | ||
102 | return IIO_VAL_INT; | ||
103 | case IIO_TEMP: /* in 0.0625 celsius / LSB */ | ||
104 | mutex_lock(&data->lock); | ||
105 | ret = mpl3115_request(data); | ||
106 | if (ret < 0) { | ||
107 | mutex_unlock(&data->lock); | ||
108 | return ret; | ||
109 | } | ||
110 | ret = i2c_smbus_read_i2c_block_data(data->client, | ||
111 | MPL3115_OUT_TEMP, 2, (u8 *) &tmp); | ||
112 | mutex_unlock(&data->lock); | ||
113 | if (ret < 0) | ||
114 | return ret; | ||
115 | *val = sign_extend32(be32_to_cpu(tmp) >> 20, 15); | ||
116 | return IIO_VAL_INT; | ||
117 | default: | ||
118 | return -EINVAL; | ||
119 | } | ||
120 | case IIO_CHAN_INFO_SCALE: | ||
121 | switch (chan->type) { | ||
122 | case IIO_PRESSURE: | ||
123 | *val = 0; | ||
124 | *val2 = 250; /* want kilopascal */ | ||
125 | return IIO_VAL_INT_PLUS_MICRO; | ||
126 | case IIO_TEMP: | ||
127 | *val = 0; | ||
128 | *val2 = 62500; | ||
129 | return IIO_VAL_INT_PLUS_MICRO; | ||
130 | default: | ||
131 | return -EINVAL; | ||
132 | } | ||
133 | } | ||
134 | return -EINVAL; | ||
135 | } | ||
136 | |||
137 | static irqreturn_t mpl3115_trigger_handler(int irq, void *p) | ||
138 | { | ||
139 | struct iio_poll_func *pf = p; | ||
140 | struct iio_dev *indio_dev = pf->indio_dev; | ||
141 | struct mpl3115_data *data = iio_priv(indio_dev); | ||
142 | u8 buffer[16]; /* 32-bit channel + 16-bit channel + padding + ts */ | ||
143 | int ret, pos = 0; | ||
144 | |||
145 | mutex_lock(&data->lock); | ||
146 | ret = mpl3115_request(data); | ||
147 | if (ret < 0) { | ||
148 | mutex_unlock(&data->lock); | ||
149 | goto done; | ||
150 | } | ||
151 | |||
152 | memset(buffer, 0, sizeof(buffer)); | ||
153 | if (test_bit(0, indio_dev->active_scan_mask)) { | ||
154 | ret = i2c_smbus_read_i2c_block_data(data->client, | ||
155 | MPL3115_OUT_PRESS, 3, &buffer[pos]); | ||
156 | if (ret < 0) { | ||
157 | mutex_unlock(&data->lock); | ||
158 | goto done; | ||
159 | } | ||
160 | pos += 4; | ||
161 | } | ||
162 | |||
163 | if (test_bit(1, indio_dev->active_scan_mask)) { | ||
164 | ret = i2c_smbus_read_i2c_block_data(data->client, | ||
165 | MPL3115_OUT_TEMP, 2, &buffer[pos]); | ||
166 | if (ret < 0) { | ||
167 | mutex_unlock(&data->lock); | ||
168 | goto done; | ||
169 | } | ||
170 | } | ||
171 | mutex_unlock(&data->lock); | ||
172 | |||
173 | iio_push_to_buffers_with_timestamp(indio_dev, buffer, | ||
174 | iio_get_time_ns()); | ||
175 | |||
176 | done: | ||
177 | iio_trigger_notify_done(indio_dev->trig); | ||
178 | return IRQ_HANDLED; | ||
179 | } | ||
180 | |||
181 | static const struct iio_chan_spec mpl3115_channels[] = { | ||
182 | { | ||
183 | .type = IIO_PRESSURE, | ||
184 | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), | ||
185 | BIT(IIO_CHAN_INFO_SCALE), | ||
186 | .scan_index = 0, | ||
187 | .scan_type = { | ||
188 | .sign = 's', | ||
189 | .realbits = 20, | ||
190 | .storagebits = 32, | ||
191 | .shift = 12, | ||
192 | .endianness = IIO_BE, | ||
193 | } | ||
194 | }, | ||
195 | { | ||
196 | .type = IIO_TEMP, | ||
197 | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), | ||
198 | BIT(IIO_CHAN_INFO_SCALE), | ||
199 | .scan_index = 1, | ||
200 | .scan_type = { | ||
201 | .sign = 's', | ||
202 | .realbits = 12, | ||
203 | .storagebits = 16, | ||
204 | .shift = 4, | ||
205 | .endianness = IIO_BE, | ||
206 | } | ||
207 | }, | ||
208 | IIO_CHAN_SOFT_TIMESTAMP(2), | ||
209 | }; | ||
210 | |||
211 | static const struct iio_info mpl3115_info = { | ||
212 | .read_raw = &mpl3115_read_raw, | ||
213 | .driver_module = THIS_MODULE, | ||
214 | }; | ||
215 | |||
216 | static int mpl3115_probe(struct i2c_client *client, | ||
217 | const struct i2c_device_id *id) | ||
218 | { | ||
219 | struct mpl3115_data *data; | ||
220 | struct iio_dev *indio_dev; | ||
221 | int ret; | ||
222 | |||
223 | ret = i2c_smbus_read_byte_data(client, MPL3115_WHO_AM_I); | ||
224 | if (ret < 0) | ||
225 | return ret; | ||
226 | if (ret != MPL3115_DEVICE_ID) | ||
227 | return -ENODEV; | ||
228 | |||
229 | indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); | ||
230 | if (!indio_dev) | ||
231 | return -ENOMEM; | ||
232 | |||
233 | data = iio_priv(indio_dev); | ||
234 | data->client = client; | ||
235 | mutex_init(&data->lock); | ||
236 | |||
237 | i2c_set_clientdata(client, indio_dev); | ||
238 | indio_dev->info = &mpl3115_info; | ||
239 | indio_dev->name = id->name; | ||
240 | indio_dev->dev.parent = &client->dev; | ||
241 | indio_dev->modes = INDIO_DIRECT_MODE; | ||
242 | indio_dev->channels = mpl3115_channels; | ||
243 | indio_dev->num_channels = ARRAY_SIZE(mpl3115_channels); | ||
244 | |||
245 | /* software reset, I2C transfer is aborted (fails) */ | ||
246 | i2c_smbus_write_byte_data(client, MPL3115_CTRL_REG1, | ||
247 | MPL3115_CTRL_RESET); | ||
248 | msleep(50); | ||
249 | |||
250 | data->ctrl_reg1 = MPL3115_CTRL_OS_258MS; | ||
251 | ret = i2c_smbus_write_byte_data(client, MPL3115_CTRL_REG1, | ||
252 | data->ctrl_reg1); | ||
253 | if (ret < 0) | ||
254 | return ret; | ||
255 | |||
256 | ret = iio_triggered_buffer_setup(indio_dev, NULL, | ||
257 | mpl3115_trigger_handler, NULL); | ||
258 | if (ret < 0) | ||
259 | return ret; | ||
260 | |||
261 | ret = iio_device_register(indio_dev); | ||
262 | if (ret < 0) | ||
263 | goto buffer_cleanup; | ||
264 | return 0; | ||
265 | |||
266 | buffer_cleanup: | ||
267 | iio_triggered_buffer_cleanup(indio_dev); | ||
268 | return ret; | ||
269 | } | ||
270 | |||
271 | static int mpl3115_standby(struct mpl3115_data *data) | ||
272 | { | ||
273 | return i2c_smbus_write_byte_data(data->client, MPL3115_CTRL_REG1, | ||
274 | data->ctrl_reg1 & ~MPL3115_CTRL_ACTIVE); | ||
275 | } | ||
276 | |||
277 | static int mpl3115_remove(struct i2c_client *client) | ||
278 | { | ||
279 | struct iio_dev *indio_dev = i2c_get_clientdata(client); | ||
280 | |||
281 | iio_device_unregister(indio_dev); | ||
282 | iio_triggered_buffer_cleanup(indio_dev); | ||
283 | mpl3115_standby(iio_priv(indio_dev)); | ||
284 | |||
285 | return 0; | ||
286 | } | ||
287 | |||
288 | #ifdef CONFIG_PM_SLEEP | ||
289 | static int mpl3115_suspend(struct device *dev) | ||
290 | { | ||
291 | return mpl3115_standby(iio_priv(i2c_get_clientdata( | ||
292 | to_i2c_client(dev)))); | ||
293 | } | ||
294 | |||
295 | static int mpl3115_resume(struct device *dev) | ||
296 | { | ||
297 | struct mpl3115_data *data = iio_priv(i2c_get_clientdata( | ||
298 | to_i2c_client(dev))); | ||
299 | |||
300 | return i2c_smbus_write_byte_data(data->client, MPL3115_CTRL_REG1, | ||
301 | data->ctrl_reg1); | ||
302 | } | ||
303 | |||
304 | static SIMPLE_DEV_PM_OPS(mpl3115_pm_ops, mpl3115_suspend, mpl3115_resume); | ||
305 | #define MPL3115_PM_OPS (&mpl3115_pm_ops) | ||
306 | #else | ||
307 | #define MPL3115_PM_OPS NULL | ||
308 | #endif | ||
309 | |||
310 | static const struct i2c_device_id mpl3115_id[] = { | ||
311 | { "mpl3115", 0 }, | ||
312 | { } | ||
313 | }; | ||
314 | MODULE_DEVICE_TABLE(i2c, mpl3115_id); | ||
315 | |||
316 | static struct i2c_driver mpl3115_driver = { | ||
317 | .driver = { | ||
318 | .name = "mpl3115", | ||
319 | .pm = MPL3115_PM_OPS, | ||
320 | }, | ||
321 | .probe = mpl3115_probe, | ||
322 | .remove = mpl3115_remove, | ||
323 | .id_table = mpl3115_id, | ||
324 | }; | ||
325 | module_i2c_driver(mpl3115_driver); | ||
326 | |||
327 | MODULE_AUTHOR("Peter Meerwald <pmeerw@pmeerw.net>"); | ||
328 | MODULE_DESCRIPTION("Freescale MPL3115 pressure/temperature driver"); | ||
329 | MODULE_LICENSE("GPL"); | ||