Commit | Line | Data |
---|---|---|
a2d8be68 PM |
1 | /* |
2 | * t5403.c - Support for EPCOS T5403 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 0x77) | |
11 | * | |
12 | * TODO: end-of-conversion irq | |
13 | */ | |
14 | ||
15 | #include <linux/module.h> | |
16 | #include <linux/i2c.h> | |
17 | #include <linux/iio/iio.h> | |
18 | #include <linux/iio/sysfs.h> | |
19 | #include <linux/delay.h> | |
20 | ||
21 | #define T5403_DATA 0xf5 /* data, LSB first, 16 bit */ | |
22 | #define T5403_CALIB_DATA 0x8e /* 10 calibration coeff., LSB first, 16 bit */ | |
23 | #define T5403_SLAVE_ADDR 0x88 /* I2C slave address, 0x77 */ | |
24 | #define T5403_COMMAND 0xf1 | |
25 | ||
26 | /* command bits */ | |
27 | #define T5403_MODE_SHIFT 3 /* conversion time: 2, 8, 16, 66 ms */ | |
28 | #define T5403_PT BIT(1) /* 0 .. pressure, 1 .. temperature measurement */ | |
29 | #define T5403_SCO BIT(0) /* start conversion */ | |
30 | ||
31 | #define T5403_MODE_LOW 0 | |
32 | #define T5403_MODE_STANDARD 1 | |
33 | #define T5403_MODE_HIGH 2 | |
34 | #define T5403_MODE_ULTRA_HIGH 3 | |
35 | ||
36 | #define T5403_I2C_MASK (~BIT(7)) | |
37 | #define T5403_I2C_ADDR 0x77 | |
38 | ||
39 | static const int t5403_pressure_conv_ms[] = {2, 8, 16, 66}; | |
40 | ||
41 | struct t5403_data { | |
42 | struct i2c_client *client; | |
43 | struct mutex lock; | |
44 | int mode; | |
45 | __le16 c[10]; | |
46 | }; | |
47 | ||
48 | #define T5403_C_U16(i) le16_to_cpu(data->c[(i) - 1]) | |
49 | #define T5403_C(i) sign_extend32(T5403_C_U16(i), 15) | |
50 | ||
51 | static int t5403_read(struct t5403_data *data, bool pressure) | |
52 | { | |
53 | int wait_time = 3; /* wakeup time in ms */ | |
54 | ||
55 | int ret = i2c_smbus_write_byte_data(data->client, T5403_COMMAND, | |
56 | (pressure ? (data->mode << T5403_MODE_SHIFT) : T5403_PT) | | |
57 | T5403_SCO); | |
58 | if (ret < 0) | |
59 | return ret; | |
60 | ||
61 | wait_time += pressure ? t5403_pressure_conv_ms[data->mode] : 2; | |
62 | ||
63 | msleep(wait_time); | |
64 | ||
65 | return i2c_smbus_read_word_data(data->client, T5403_DATA); | |
66 | } | |
67 | ||
68 | static int t5403_comp_pressure(struct t5403_data *data, int *val, int *val2) | |
69 | { | |
70 | int ret; | |
71 | s16 t_r; | |
72 | u16 p_r; | |
73 | s32 S, O, X; | |
74 | ||
75 | mutex_lock(&data->lock); | |
76 | ||
77 | ret = t5403_read(data, false); | |
78 | if (ret < 0) | |
79 | goto done; | |
80 | t_r = ret; | |
81 | ||
82 | ret = t5403_read(data, true); | |
83 | if (ret < 0) | |
84 | goto done; | |
85 | p_r = ret; | |
86 | ||
87 | /* see EPCOS application note */ | |
88 | S = T5403_C_U16(3) + (s32) T5403_C_U16(4) * t_r / 0x20000 + | |
89 | T5403_C(5) * t_r / 0x8000 * t_r / 0x80000 + | |
90 | T5403_C(9) * t_r / 0x8000 * t_r / 0x8000 * t_r / 0x10000; | |
91 | ||
92 | O = T5403_C(6) * 0x4000 + T5403_C(7) * t_r / 8 + | |
93 | T5403_C(8) * t_r / 0x8000 * t_r / 16 + | |
94 | T5403_C(9) * t_r / 0x8000 * t_r / 0x10000 * t_r; | |
95 | ||
96 | X = (S * p_r + O) / 0x4000; | |
97 | ||
98 | X += ((X - 75000) * (X - 75000) / 0x10000 - 9537) * | |
99 | T5403_C(10) / 0x10000; | |
100 | ||
101 | *val = X / 1000; | |
102 | *val2 = (X % 1000) * 1000; | |
103 | ||
104 | done: | |
105 | mutex_unlock(&data->lock); | |
106 | return ret; | |
107 | } | |
108 | ||
109 | static int t5403_comp_temp(struct t5403_data *data, int *val) | |
110 | { | |
111 | int ret; | |
112 | s16 t_r; | |
113 | ||
114 | mutex_lock(&data->lock); | |
115 | ret = t5403_read(data, false); | |
116 | if (ret < 0) | |
117 | goto done; | |
118 | t_r = ret; | |
119 | ||
120 | /* see EPCOS application note */ | |
121 | *val = ((s32) T5403_C_U16(1) * t_r / 0x100 + | |
122 | (s32) T5403_C_U16(2) * 0x40) * 1000 / 0x10000; | |
123 | ||
124 | done: | |
125 | mutex_unlock(&data->lock); | |
126 | return ret; | |
127 | } | |
128 | ||
129 | static int t5403_read_raw(struct iio_dev *indio_dev, | |
130 | struct iio_chan_spec const *chan, | |
131 | int *val, int *val2, long mask) | |
132 | { | |
133 | struct t5403_data *data = iio_priv(indio_dev); | |
134 | int ret; | |
135 | ||
136 | switch (mask) { | |
137 | case IIO_CHAN_INFO_PROCESSED: | |
138 | switch (chan->type) { | |
139 | case IIO_PRESSURE: | |
140 | ret = t5403_comp_pressure(data, val, val2); | |
141 | if (ret < 0) | |
142 | return ret; | |
143 | return IIO_VAL_INT_PLUS_MICRO; | |
144 | case IIO_TEMP: | |
145 | ret = t5403_comp_temp(data, val); | |
146 | if (ret < 0) | |
147 | return ret; | |
148 | return IIO_VAL_INT; | |
149 | default: | |
150 | return -EINVAL; | |
151 | } | |
152 | case IIO_CHAN_INFO_INT_TIME: | |
153 | *val = 0; | |
154 | *val2 = t5403_pressure_conv_ms[data->mode] * 1000; | |
155 | return IIO_VAL_INT_PLUS_MICRO; | |
156 | default: | |
157 | return -EINVAL; | |
158 | } | |
159 | } | |
160 | ||
161 | static int t5403_write_raw(struct iio_dev *indio_dev, | |
162 | struct iio_chan_spec const *chan, | |
163 | int val, int val2, long mask) | |
164 | { | |
165 | struct t5403_data *data = iio_priv(indio_dev); | |
166 | int i; | |
167 | ||
168 | switch (mask) { | |
169 | case IIO_CHAN_INFO_INT_TIME: | |
170 | if (val != 0) | |
171 | return -EINVAL; | |
172 | for (i = 0; i < ARRAY_SIZE(t5403_pressure_conv_ms); i++) | |
173 | if (val2 == t5403_pressure_conv_ms[i] * 1000) { | |
174 | mutex_lock(&data->lock); | |
175 | data->mode = i; | |
176 | mutex_unlock(&data->lock); | |
177 | return 0; | |
178 | } | |
179 | return -EINVAL; | |
180 | default: | |
181 | return -EINVAL; | |
182 | } | |
183 | } | |
184 | ||
185 | static const struct iio_chan_spec t5403_channels[] = { | |
186 | { | |
187 | .type = IIO_PRESSURE, | |
188 | .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED) | | |
189 | BIT(IIO_CHAN_INFO_INT_TIME), | |
190 | }, | |
191 | { | |
192 | .type = IIO_TEMP, | |
193 | .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), | |
194 | }, | |
195 | }; | |
196 | ||
197 | static IIO_CONST_ATTR_INT_TIME_AVAIL("0.002 0.008 0.016 0.066"); | |
198 | ||
199 | static struct attribute *t5403_attributes[] = { | |
200 | &iio_const_attr_integration_time_available.dev_attr.attr, | |
201 | NULL | |
202 | }; | |
203 | ||
204 | static const struct attribute_group t5403_attribute_group = { | |
205 | .attrs = t5403_attributes, | |
206 | }; | |
207 | ||
208 | static const struct iio_info t5403_info = { | |
209 | .read_raw = &t5403_read_raw, | |
210 | .write_raw = &t5403_write_raw, | |
211 | .attrs = &t5403_attribute_group, | |
212 | .driver_module = THIS_MODULE, | |
213 | }; | |
214 | ||
215 | static int t5403_probe(struct i2c_client *client, | |
216 | const struct i2c_device_id *id) | |
217 | { | |
218 | struct t5403_data *data; | |
219 | struct iio_dev *indio_dev; | |
220 | int ret; | |
221 | ||
222 | if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WORD_DATA | | |
223 | I2C_FUNC_SMBUS_I2C_BLOCK)) | |
f8d9d3b4 | 224 | return -EOPNOTSUPP; |
a2d8be68 PM |
225 | |
226 | ret = i2c_smbus_read_byte_data(client, T5403_SLAVE_ADDR); | |
227 | if (ret < 0) | |
228 | return ret; | |
229 | if ((ret & T5403_I2C_MASK) != T5403_I2C_ADDR) | |
230 | return -ENODEV; | |
231 | ||
232 | indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); | |
233 | if (!indio_dev) | |
234 | return -ENOMEM; | |
235 | ||
236 | data = iio_priv(indio_dev); | |
237 | data->client = client; | |
238 | mutex_init(&data->lock); | |
239 | ||
240 | i2c_set_clientdata(client, indio_dev); | |
241 | indio_dev->info = &t5403_info; | |
242 | indio_dev->name = id->name; | |
243 | indio_dev->dev.parent = &client->dev; | |
244 | indio_dev->modes = INDIO_DIRECT_MODE; | |
245 | indio_dev->channels = t5403_channels; | |
246 | indio_dev->num_channels = ARRAY_SIZE(t5403_channels); | |
247 | ||
248 | data->mode = T5403_MODE_STANDARD; | |
249 | ||
250 | ret = i2c_smbus_read_i2c_block_data(data->client, T5403_CALIB_DATA, | |
251 | sizeof(data->c), (u8 *) data->c); | |
252 | if (ret < 0) | |
253 | return ret; | |
254 | ||
255 | return devm_iio_device_register(&client->dev, indio_dev); | |
256 | } | |
257 | ||
258 | static const struct i2c_device_id t5403_id[] = { | |
259 | { "t5403", 0 }, | |
260 | { } | |
261 | }; | |
262 | MODULE_DEVICE_TABLE(i2c, t5403_id); | |
263 | ||
264 | static struct i2c_driver t5403_driver = { | |
265 | .driver = { | |
266 | .name = "t5403", | |
267 | }, | |
268 | .probe = t5403_probe, | |
269 | .id_table = t5403_id, | |
270 | }; | |
271 | module_i2c_driver(t5403_driver); | |
272 | ||
273 | MODULE_AUTHOR("Peter Meerwald <pmeerw@pmeerw.net>"); | |
274 | MODULE_DESCRIPTION("EPCOS T5403 pressure/temperature sensor driver"); | |
275 | MODULE_LICENSE("GPL"); |