diff options
author | Peter Meerwald <pmeerw@pmeerw.net> | 2014-02-05 11:58:00 -0500 |
---|---|---|
committer | Jonathan Cameron <jic23@kernel.org> | 2014-05-03 06:31:36 -0400 |
commit | 3017d90e8931fbd5dbde06b4e793aebb141e4977 (patch) | |
tree | 6b2ace8b7963b135cd467c5f51099f5230c18418 | |
parent | 0828eddc56d38b70f9a9e5cf7a0050328a90e7a1 (diff) |
iio: Add Freescale MPL115A2 pressure / temperature sensor driver
I2C-controlled sensor with 10-bit pressure and temperature measurement
datasheet: http://cache.freescale.com/files/sensors/doc/data_sheet/MPL3115A2.pdf
v2:
* use devm_iio_device_register()
Signed-off-by: Peter Meerwald <pmeerw@pmeerw.net>
Signed-off-by: Jonathan Cameron <jic23@kernel.org>
-rw-r--r-- | drivers/iio/pressure/Kconfig | 10 | ||||
-rw-r--r-- | drivers/iio/pressure/Makefile | 1 | ||||
-rw-r--r-- | drivers/iio/pressure/mpl115.c | 211 |
3 files changed, 222 insertions, 0 deletions
diff --git a/drivers/iio/pressure/Kconfig b/drivers/iio/pressure/Kconfig index d88ff17fedb2..ffac8ac1efca 100644 --- a/drivers/iio/pressure/Kconfig +++ b/drivers/iio/pressure/Kconfig | |||
@@ -19,6 +19,16 @@ config HID_SENSOR_PRESS | |||
19 | To compile this driver as a module, choose M here: the module | 19 | To compile this driver as a module, choose M here: the module |
20 | will be called hid-sensor-press. | 20 | will be called hid-sensor-press. |
21 | 21 | ||
22 | config MPL115 | ||
23 | tristate "Freescale MPL115A2 pressure sensor driver" | ||
24 | depends on I2C | ||
25 | help | ||
26 | Say yes here to build support for the Freescale MPL115A2 | ||
27 | pressure sensor connected via I2C. | ||
28 | |||
29 | To compile this driver as a module, choose M here: the module | ||
30 | will be called mpl115. | ||
31 | |||
22 | config MPL3115 | 32 | config MPL3115 |
23 | tristate "Freescale MPL3115A2 pressure sensor driver" | 33 | tristate "Freescale MPL3115A2 pressure sensor driver" |
24 | depends on I2C | 34 | depends on I2C |
diff --git a/drivers/iio/pressure/Makefile b/drivers/iio/pressure/Makefile index 4a57bf65b04b..c53d2500737a 100644 --- a/drivers/iio/pressure/Makefile +++ b/drivers/iio/pressure/Makefile | |||
@@ -4,6 +4,7 @@ | |||
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_HID_SENSOR_PRESS) += hid-sensor-press.o | 6 | obj-$(CONFIG_HID_SENSOR_PRESS) += hid-sensor-press.o |
7 | obj-$(CONFIG_MPL115) += mpl115.o | ||
7 | obj-$(CONFIG_MPL3115) += mpl3115.o | 8 | obj-$(CONFIG_MPL3115) += mpl3115.o |
8 | obj-$(CONFIG_IIO_ST_PRESS) += st_pressure.o | 9 | obj-$(CONFIG_IIO_ST_PRESS) += st_pressure.o |
9 | st_pressure-y := st_pressure_core.o | 10 | st_pressure-y := st_pressure_core.o |
diff --git a/drivers/iio/pressure/mpl115.c b/drivers/iio/pressure/mpl115.c new file mode 100644 index 000000000000..f5ecd6e19f5d --- /dev/null +++ b/drivers/iio/pressure/mpl115.c | |||
@@ -0,0 +1,211 @@ | |||
1 | /* | ||
2 | * mpl115.c - Support for Freescale MPL115A2 pressure/temperature sensor | ||
3 | * | ||
4 | * Copyright (c) 2014 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: shutdown pin | ||
13 | * | ||
14 | */ | ||
15 | |||
16 | #include <linux/module.h> | ||
17 | #include <linux/i2c.h> | ||
18 | #include <linux/iio/iio.h> | ||
19 | #include <linux/delay.h> | ||
20 | |||
21 | #define MPL115_PADC 0x00 /* pressure ADC output value, MSB first, 10 bit */ | ||
22 | #define MPL115_TADC 0x02 /* temperature ADC output value, MSB first, 10 bit */ | ||
23 | #define MPL115_A0 0x04 /* 12 bit integer, 3 bit fraction */ | ||
24 | #define MPL115_B1 0x06 /* 2 bit integer, 13 bit fraction */ | ||
25 | #define MPL115_B2 0x08 /* 1 bit integer, 14 bit fraction */ | ||
26 | #define MPL115_C12 0x0a /* 0 bit integer, 13 bit fraction */ | ||
27 | #define MPL115_CONVERT 0x12 /* convert temperature and pressure */ | ||
28 | |||
29 | struct mpl115_data { | ||
30 | struct i2c_client *client; | ||
31 | struct mutex lock; | ||
32 | s16 a0; | ||
33 | s16 b1, b2; | ||
34 | s16 c12; | ||
35 | }; | ||
36 | |||
37 | static int mpl115_request(struct mpl115_data *data) | ||
38 | { | ||
39 | int ret = i2c_smbus_write_byte_data(data->client, MPL115_CONVERT, 0); | ||
40 | if (ret < 0) | ||
41 | return ret; | ||
42 | |||
43 | usleep_range(3000, 4000); | ||
44 | |||
45 | return 0; | ||
46 | } | ||
47 | |||
48 | static int mpl115_comp_pressure(struct mpl115_data *data, int *val, int *val2) | ||
49 | { | ||
50 | int ret; | ||
51 | u16 padc, tadc; | ||
52 | int a1, y1, pcomp; | ||
53 | unsigned kpa; | ||
54 | |||
55 | mutex_lock(&data->lock); | ||
56 | ret = mpl115_request(data); | ||
57 | if (ret < 0) | ||
58 | goto done; | ||
59 | |||
60 | ret = i2c_smbus_read_word_swapped(data->client, MPL115_PADC); | ||
61 | if (ret < 0) | ||
62 | goto done; | ||
63 | padc = ret >> 6; | ||
64 | |||
65 | ret = i2c_smbus_read_word_swapped(data->client, MPL115_TADC); | ||
66 | if (ret < 0) | ||
67 | goto done; | ||
68 | tadc = ret >> 6; | ||
69 | |||
70 | /* see Freescale AN3785 */ | ||
71 | a1 = data->b1 + ((data->c12 * tadc) >> 11); | ||
72 | y1 = (data->a0 << 10) + a1 * padc; | ||
73 | |||
74 | /* compensated pressure with 4 fractional bits */ | ||
75 | pcomp = (y1 + ((data->b2 * (int) tadc) >> 1)) >> 9; | ||
76 | |||
77 | kpa = pcomp * (115 - 50) / 1023 + (50 << 4); | ||
78 | *val = kpa >> 4; | ||
79 | *val2 = (kpa & 15) * (1000000 >> 4); | ||
80 | done: | ||
81 | mutex_unlock(&data->lock); | ||
82 | return ret; | ||
83 | } | ||
84 | |||
85 | static int mpl115_read_temp(struct mpl115_data *data) | ||
86 | { | ||
87 | int ret; | ||
88 | |||
89 | mutex_lock(&data->lock); | ||
90 | ret = mpl115_request(data); | ||
91 | if (ret < 0) | ||
92 | goto done; | ||
93 | ret = i2c_smbus_read_word_swapped(data->client, MPL115_TADC); | ||
94 | done: | ||
95 | mutex_unlock(&data->lock); | ||
96 | return ret; | ||
97 | } | ||
98 | |||
99 | static int mpl115_read_raw(struct iio_dev *indio_dev, | ||
100 | struct iio_chan_spec const *chan, | ||
101 | int *val, int *val2, long mask) | ||
102 | { | ||
103 | struct mpl115_data *data = iio_priv(indio_dev); | ||
104 | int ret; | ||
105 | |||
106 | switch (mask) { | ||
107 | case IIO_CHAN_INFO_PROCESSED: | ||
108 | ret = mpl115_comp_pressure(data, val, val2); | ||
109 | if (ret < 0) | ||
110 | return ret; | ||
111 | return IIO_VAL_INT_PLUS_MICRO; | ||
112 | case IIO_CHAN_INFO_RAW: | ||
113 | /* temperature -5.35 C / LSB, 472 LSB is 25 C */ | ||
114 | ret = mpl115_read_temp(data); | ||
115 | if (ret < 0) | ||
116 | return ret; | ||
117 | *val = ret >> 6; | ||
118 | return IIO_VAL_INT; | ||
119 | case IIO_CHAN_INFO_OFFSET: | ||
120 | *val = 605; | ||
121 | *val2 = 750000; | ||
122 | return IIO_VAL_INT_PLUS_MICRO; | ||
123 | case IIO_CHAN_INFO_SCALE: | ||
124 | *val = -186; | ||
125 | *val2 = 915888; | ||
126 | return IIO_VAL_INT_PLUS_MICRO; | ||
127 | } | ||
128 | return -EINVAL; | ||
129 | } | ||
130 | |||
131 | static const struct iio_chan_spec mpl115_channels[] = { | ||
132 | { | ||
133 | .type = IIO_PRESSURE, | ||
134 | .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), | ||
135 | }, | ||
136 | { | ||
137 | .type = IIO_TEMP, | ||
138 | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), | ||
139 | BIT(IIO_CHAN_INFO_OFFSET) | BIT(IIO_CHAN_INFO_SCALE), | ||
140 | }, | ||
141 | }; | ||
142 | |||
143 | static const struct iio_info mpl115_info = { | ||
144 | .read_raw = &mpl115_read_raw, | ||
145 | .driver_module = THIS_MODULE, | ||
146 | }; | ||
147 | |||
148 | static int mpl115_probe(struct i2c_client *client, | ||
149 | const struct i2c_device_id *id) | ||
150 | { | ||
151 | struct mpl115_data *data; | ||
152 | struct iio_dev *indio_dev; | ||
153 | int ret; | ||
154 | |||
155 | if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WORD_DATA)) | ||
156 | return -ENODEV; | ||
157 | |||
158 | indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); | ||
159 | if (!indio_dev) | ||
160 | return -ENOMEM; | ||
161 | |||
162 | data = iio_priv(indio_dev); | ||
163 | data->client = client; | ||
164 | mutex_init(&data->lock); | ||
165 | |||
166 | i2c_set_clientdata(client, indio_dev); | ||
167 | indio_dev->info = &mpl115_info; | ||
168 | indio_dev->name = id->name; | ||
169 | indio_dev->dev.parent = &client->dev; | ||
170 | indio_dev->modes = INDIO_DIRECT_MODE; | ||
171 | indio_dev->channels = mpl115_channels; | ||
172 | indio_dev->num_channels = ARRAY_SIZE(mpl115_channels); | ||
173 | |||
174 | ret = i2c_smbus_read_word_swapped(data->client, MPL115_A0); | ||
175 | if (ret < 0) | ||
176 | return ret; | ||
177 | data->a0 = ret; | ||
178 | ret = i2c_smbus_read_word_swapped(data->client, MPL115_B1); | ||
179 | if (ret < 0) | ||
180 | return ret; | ||
181 | data->b1 = ret; | ||
182 | ret = i2c_smbus_read_word_swapped(data->client, MPL115_B2); | ||
183 | if (ret < 0) | ||
184 | return ret; | ||
185 | data->b2 = ret; | ||
186 | ret = i2c_smbus_read_word_swapped(data->client, MPL115_C12); | ||
187 | if (ret < 0) | ||
188 | return ret; | ||
189 | data->c12 = ret; | ||
190 | |||
191 | return devm_iio_device_register(&client->dev, indio_dev); | ||
192 | } | ||
193 | |||
194 | static const struct i2c_device_id mpl115_id[] = { | ||
195 | { "mpl115", 0 }, | ||
196 | { } | ||
197 | }; | ||
198 | MODULE_DEVICE_TABLE(i2c, mpl115_id); | ||
199 | |||
200 | static struct i2c_driver mpl115_driver = { | ||
201 | .driver = { | ||
202 | .name = "mpl115", | ||
203 | }, | ||
204 | .probe = mpl115_probe, | ||
205 | .id_table = mpl115_id, | ||
206 | }; | ||
207 | module_i2c_driver(mpl115_driver); | ||
208 | |||
209 | MODULE_AUTHOR("Peter Meerwald <pmeerw@pmeerw.net>"); | ||
210 | MODULE_DESCRIPTION("Freescale MPL115 pressure/temperature driver"); | ||
211 | MODULE_LICENSE("GPL"); | ||