diff options
author | Peter Meerwald <pmeerw@pmeerw.net> | 2014-02-06 15:44:00 -0500 |
---|---|---|
committer | Jonathan Cameron <jic23@kernel.org> | 2014-06-14 08:15:30 -0400 |
commit | a2d8be6823017ccdde8db8b0f4fbe6a29580dd40 (patch) | |
tree | 1da722c1b5c38d0670a86c9081d992f62595cd04 /drivers | |
parent | 6c0690ecb682c94b958e0328c46bfcfd95c7698c (diff) |
iio: Add t5403 barometric pressure sensor driver
16-bit pressure and temperature sensor
the chip can do I2C and SPI, only the I2C interface is supported
by the driver at the moment
datasheet: http://www.epcos.com/inf/57/ds/T5400.pdf
application note: http://www.epcos.com/blob/993154/download/1/t5403-applicationnote.pdf
an out-of-tree driver targetting the input subsystem is at
https://github.com/unixphere/t5400, it was rejected here:
http://comments.gmane.org/gmane.linux.kernel.input/28107
v2: (thanks Hartmut Knaack)
* fix MODE_HIGH, equals 2
* check INT_TIME mask in write_raw()
Signed-off-by: Peter Meerwald <pmeerw@pmeerw.net>
Cc: Stefan Nilsson <stefan.nilsson@unixphere.com>
Reviewed-by: Hartmut Knaack <knaack.h@gmx.de>
Signed-off-by: Jonathan Cameron <jic23@kernel.org>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/iio/pressure/Kconfig | 10 | ||||
-rw-r--r-- | drivers/iio/pressure/Makefile | 1 | ||||
-rw-r--r-- | drivers/iio/pressure/t5403.c | 275 |
3 files changed, 286 insertions, 0 deletions
diff --git a/drivers/iio/pressure/Kconfig b/drivers/iio/pressure/Kconfig index ffac8ac1efca..15afbc919521 100644 --- a/drivers/iio/pressure/Kconfig +++ b/drivers/iio/pressure/Kconfig | |||
@@ -70,4 +70,14 @@ config IIO_ST_PRESS_SPI | |||
70 | depends on IIO_ST_PRESS | 70 | depends on IIO_ST_PRESS |
71 | depends on IIO_ST_SENSORS_SPI | 71 | depends on IIO_ST_SENSORS_SPI |
72 | 72 | ||
73 | config T5403 | ||
74 | tristate "EPCOS T5403 digital barometric pressure sensor driver" | ||
75 | depends on I2C | ||
76 | help | ||
77 | Say yes here to build support for the EPCOS T5403 pressure sensor | ||
78 | connected via I2C. | ||
79 | |||
80 | To compile this driver as a module, choose M here: the module | ||
81 | will be called t5403. | ||
82 | |||
73 | endmenu | 83 | endmenu |
diff --git a/drivers/iio/pressure/Makefile b/drivers/iio/pressure/Makefile index c53d2500737a..90a37e85cf21 100644 --- a/drivers/iio/pressure/Makefile +++ b/drivers/iio/pressure/Makefile | |||
@@ -9,6 +9,7 @@ obj-$(CONFIG_MPL3115) += mpl3115.o | |||
9 | obj-$(CONFIG_IIO_ST_PRESS) += st_pressure.o | 9 | obj-$(CONFIG_IIO_ST_PRESS) += st_pressure.o |
10 | st_pressure-y := st_pressure_core.o | 10 | st_pressure-y := st_pressure_core.o |
11 | st_pressure-$(CONFIG_IIO_BUFFER) += st_pressure_buffer.o | 11 | st_pressure-$(CONFIG_IIO_BUFFER) += st_pressure_buffer.o |
12 | obj-$(CONFIG_T5403) += t5403.o | ||
12 | 13 | ||
13 | obj-$(CONFIG_IIO_ST_PRESS_I2C) += st_pressure_i2c.o | 14 | obj-$(CONFIG_IIO_ST_PRESS_I2C) += st_pressure_i2c.o |
14 | obj-$(CONFIG_IIO_ST_PRESS_SPI) += st_pressure_spi.o | 15 | obj-$(CONFIG_IIO_ST_PRESS_SPI) += st_pressure_spi.o |
diff --git a/drivers/iio/pressure/t5403.c b/drivers/iio/pressure/t5403.c new file mode 100644 index 000000000000..e11cd3938d67 --- /dev/null +++ b/drivers/iio/pressure/t5403.c | |||
@@ -0,0 +1,275 @@ | |||
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)) | ||
224 | return -ENODEV; | ||
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"); | ||