]>
Commit | Line | Data |
---|---|---|
3017d90e PM |
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: | |
431386e7 | 120 | *val = -605; |
3017d90e PM |
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"); |