Commit | Line | Data |
---|---|---|
78f83902 AK |
1 | /* |
2 | * srf08.c - Support for Devantech SRF08 ultrasonic ranger | |
3 | * | |
4 | * Copyright (c) 2016 Andreas Klinger <ak@it-klinger.de> | |
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 | * For details about the device see: | |
11 | * http://www.robot-electronics.co.uk/htm/srf08tech.html | |
12 | */ | |
13 | ||
14 | #include <linux/err.h> | |
15 | #include <linux/i2c.h> | |
16 | #include <linux/delay.h> | |
17 | #include <linux/module.h> | |
18 | #include <linux/bitops.h> | |
19 | #include <linux/iio/iio.h> | |
20 | #include <linux/iio/sysfs.h> | |
21 | ||
22 | /* registers of SRF08 device */ | |
23 | #define SRF08_WRITE_COMMAND 0x00 /* Command Register */ | |
24 | #define SRF08_WRITE_MAX_GAIN 0x01 /* Max Gain Register: 0 .. 31 */ | |
25 | #define SRF08_WRITE_RANGE 0x02 /* Range Register: 0 .. 255 */ | |
26 | #define SRF08_READ_SW_REVISION 0x00 /* Software Revision */ | |
27 | #define SRF08_READ_LIGHT 0x01 /* Light Sensor during last echo */ | |
28 | #define SRF08_READ_ECHO_1_HIGH 0x02 /* Range of first echo received */ | |
29 | #define SRF08_READ_ECHO_1_LOW 0x03 /* Range of first echo received */ | |
30 | ||
31 | #define SRF08_CMD_RANGING_CM 0x51 /* Ranging Mode - Result in cm */ | |
32 | ||
33 | #define SRF08_DEFAULT_GAIN 1025 /* default analogue value of Gain */ | |
34 | #define SRF08_DEFAULT_RANGE 6020 /* default value of Range in mm */ | |
35 | ||
36 | struct srf08_data { | |
37 | struct i2c_client *client; | |
38 | int sensitivity; /* Gain */ | |
39 | int range_mm; /* max. Range in mm */ | |
40 | struct mutex lock; | |
41 | }; | |
42 | ||
43 | /* | |
44 | * in the documentation one can read about the "Gain" of the device | |
45 | * which is used here for amplifying the signal and filtering out unwanted | |
46 | * ones. | |
47 | * But with ADC's this term is already used differently and that's why it | |
48 | * is called "Sensitivity" here. | |
49 | */ | |
50 | static const int srf08_sensitivity[] = { | |
51 | 94, 97, 100, 103, 107, 110, 114, 118, | |
52 | 123, 128, 133, 139, 145, 152, 159, 168, | |
53 | 177, 187, 199, 212, 227, 245, 265, 288, | |
54 | 317, 352, 395, 450, 524, 626, 777, 1025 }; | |
55 | ||
56 | static int srf08_read_ranging(struct srf08_data *data) | |
57 | { | |
58 | struct i2c_client *client = data->client; | |
59 | int ret, i; | |
60 | int waittime; | |
61 | ||
62 | mutex_lock(&data->lock); | |
63 | ||
64 | ret = i2c_smbus_write_byte_data(data->client, | |
65 | SRF08_WRITE_COMMAND, SRF08_CMD_RANGING_CM); | |
66 | if (ret < 0) { | |
67 | dev_err(&client->dev, "write command - err: %d\n", ret); | |
68 | mutex_unlock(&data->lock); | |
69 | return ret; | |
70 | } | |
71 | ||
72 | /* | |
73 | * we read here until a correct version number shows up as | |
74 | * suggested by the documentation | |
75 | * | |
76 | * with an ultrasonic speed of 343 m/s and a roundtrip of it | |
77 | * sleep the expected duration and try to read from the device | |
78 | * if nothing useful is read try it in a shorter grid | |
79 | * | |
80 | * polling for not more than 20 ms should be enough | |
81 | */ | |
82 | waittime = 1 + data->range_mm / 172; | |
83 | msleep(waittime); | |
84 | for (i = 0; i < 4; i++) { | |
85 | ret = i2c_smbus_read_byte_data(data->client, | |
86 | SRF08_READ_SW_REVISION); | |
87 | ||
88 | /* check if a valid version number is read */ | |
89 | if (ret < 255 && ret > 0) | |
90 | break; | |
91 | msleep(5); | |
92 | } | |
93 | ||
94 | if (ret >= 255 || ret <= 0) { | |
95 | dev_err(&client->dev, "device not ready\n"); | |
96 | mutex_unlock(&data->lock); | |
97 | return -EIO; | |
98 | } | |
99 | ||
100 | ret = i2c_smbus_read_word_swapped(data->client, | |
101 | SRF08_READ_ECHO_1_HIGH); | |
102 | if (ret < 0) { | |
103 | dev_err(&client->dev, "cannot read distance: ret=%d\n", ret); | |
104 | mutex_unlock(&data->lock); | |
105 | return ret; | |
106 | } | |
107 | ||
108 | mutex_unlock(&data->lock); | |
109 | ||
110 | return ret; | |
111 | } | |
112 | ||
113 | static int srf08_read_raw(struct iio_dev *indio_dev, | |
114 | struct iio_chan_spec const *channel, int *val, | |
115 | int *val2, long mask) | |
116 | { | |
117 | struct srf08_data *data = iio_priv(indio_dev); | |
118 | int ret; | |
119 | ||
120 | if (channel->type != IIO_DISTANCE) | |
121 | return -EINVAL; | |
122 | ||
123 | switch (mask) { | |
124 | case IIO_CHAN_INFO_RAW: | |
125 | ret = srf08_read_ranging(data); | |
126 | if (ret < 0) | |
127 | return ret; | |
128 | *val = ret; | |
129 | return IIO_VAL_INT; | |
130 | case IIO_CHAN_INFO_SCALE: | |
131 | /* 1 LSB is 1 cm */ | |
132 | *val = 0; | |
133 | *val2 = 10000; | |
134 | return IIO_VAL_INT_PLUS_MICRO; | |
135 | default: | |
136 | return -EINVAL; | |
137 | } | |
138 | } | |
139 | ||
140 | static ssize_t srf08_show_range_mm_available(struct device *dev, | |
141 | struct device_attribute *attr, char *buf) | |
142 | { | |
143 | return sprintf(buf, "[0.043 0.043 11.008]\n"); | |
144 | } | |
145 | ||
146 | static IIO_DEVICE_ATTR(sensor_max_range_available, S_IRUGO, | |
147 | srf08_show_range_mm_available, NULL, 0); | |
148 | ||
149 | static ssize_t srf08_show_range_mm(struct device *dev, | |
150 | struct device_attribute *attr, char *buf) | |
151 | { | |
152 | struct iio_dev *indio_dev = dev_to_iio_dev(dev); | |
153 | struct srf08_data *data = iio_priv(indio_dev); | |
154 | ||
155 | return sprintf(buf, "%d.%03d\n", data->range_mm / 1000, | |
156 | data->range_mm % 1000); | |
157 | } | |
158 | ||
159 | /* | |
160 | * set the range of the sensor to an even multiple of 43 mm | |
161 | * which corresponds to 1 LSB in the register | |
162 | * | |
163 | * register value corresponding range | |
164 | * 0x00 43 mm | |
165 | * 0x01 86 mm | |
166 | * 0x02 129 mm | |
167 | * ... | |
168 | * 0xFF 11008 mm | |
169 | */ | |
170 | static ssize_t srf08_write_range_mm(struct srf08_data *data, unsigned int val) | |
171 | { | |
172 | int ret; | |
173 | struct i2c_client *client = data->client; | |
174 | unsigned int mod; | |
175 | u8 regval; | |
176 | ||
177 | ret = val / 43 - 1; | |
178 | mod = val % 43; | |
179 | ||
180 | if (mod || (ret < 0) || (ret > 255)) | |
181 | return -EINVAL; | |
182 | ||
183 | regval = ret; | |
184 | ||
185 | mutex_lock(&data->lock); | |
186 | ||
187 | ret = i2c_smbus_write_byte_data(client, SRF08_WRITE_RANGE, regval); | |
188 | if (ret < 0) { | |
189 | dev_err(&client->dev, "write_range - err: %d\n", ret); | |
190 | mutex_unlock(&data->lock); | |
191 | return ret; | |
192 | } | |
193 | ||
194 | data->range_mm = val; | |
195 | ||
196 | mutex_unlock(&data->lock); | |
197 | ||
198 | return 0; | |
199 | } | |
200 | ||
201 | static ssize_t srf08_store_range_mm(struct device *dev, | |
202 | struct device_attribute *attr, | |
203 | const char *buf, size_t len) | |
204 | { | |
205 | struct iio_dev *indio_dev = dev_to_iio_dev(dev); | |
206 | struct srf08_data *data = iio_priv(indio_dev); | |
207 | int ret; | |
208 | int integer, fract; | |
209 | ||
210 | ret = iio_str_to_fixpoint(buf, 100, &integer, &fract); | |
211 | if (ret) | |
212 | return ret; | |
213 | ||
214 | ret = srf08_write_range_mm(data, integer * 1000 + fract); | |
215 | if (ret < 0) | |
216 | return ret; | |
217 | ||
218 | return len; | |
219 | } | |
220 | ||
221 | static IIO_DEVICE_ATTR(sensor_max_range, S_IRUGO | S_IWUSR, | |
222 | srf08_show_range_mm, srf08_store_range_mm, 0); | |
223 | ||
224 | static ssize_t srf08_show_sensitivity_available(struct device *dev, | |
225 | struct device_attribute *attr, char *buf) | |
226 | { | |
227 | int i, len = 0; | |
228 | ||
229 | for (i = 0; i < ARRAY_SIZE(srf08_sensitivity); i++) | |
230 | len += sprintf(buf + len, "%d ", srf08_sensitivity[i]); | |
231 | ||
232 | len += sprintf(buf + len, "\n"); | |
233 | ||
234 | return len; | |
235 | } | |
236 | ||
237 | static IIO_DEVICE_ATTR(sensor_sensitivity_available, S_IRUGO, | |
238 | srf08_show_sensitivity_available, NULL, 0); | |
239 | ||
240 | static ssize_t srf08_show_sensitivity(struct device *dev, | |
241 | struct device_attribute *attr, char *buf) | |
242 | { | |
243 | struct iio_dev *indio_dev = dev_to_iio_dev(dev); | |
244 | struct srf08_data *data = iio_priv(indio_dev); | |
245 | int len; | |
246 | ||
247 | len = sprintf(buf, "%d\n", data->sensitivity); | |
248 | ||
249 | return len; | |
250 | } | |
251 | ||
252 | static ssize_t srf08_write_sensitivity(struct srf08_data *data, | |
253 | unsigned int val) | |
254 | { | |
255 | struct i2c_client *client = data->client; | |
256 | int ret, i; | |
257 | u8 regval; | |
258 | ||
259 | for (i = 0; i < ARRAY_SIZE(srf08_sensitivity); i++) | |
260 | if (val == srf08_sensitivity[i]) { | |
261 | regval = i; | |
262 | break; | |
263 | } | |
264 | ||
265 | if (i >= ARRAY_SIZE(srf08_sensitivity)) | |
266 | return -EINVAL; | |
267 | ||
268 | mutex_lock(&data->lock); | |
269 | ||
270 | ret = i2c_smbus_write_byte_data(client, | |
271 | SRF08_WRITE_MAX_GAIN, regval); | |
272 | if (ret < 0) { | |
273 | dev_err(&client->dev, "write_sensitivity - err: %d\n", ret); | |
274 | mutex_unlock(&data->lock); | |
275 | return ret; | |
276 | } | |
277 | ||
278 | data->sensitivity = val; | |
279 | ||
280 | mutex_unlock(&data->lock); | |
281 | ||
282 | return 0; | |
283 | } | |
284 | ||
285 | static ssize_t srf08_store_sensitivity(struct device *dev, | |
286 | struct device_attribute *attr, | |
287 | const char *buf, size_t len) | |
288 | { | |
289 | struct iio_dev *indio_dev = dev_to_iio_dev(dev); | |
290 | struct srf08_data *data = iio_priv(indio_dev); | |
291 | int ret; | |
292 | unsigned int val; | |
293 | ||
294 | ret = kstrtouint(buf, 10, &val); | |
295 | if (ret) | |
296 | return ret; | |
297 | ||
298 | ret = srf08_write_sensitivity(data, val); | |
299 | if (ret < 0) | |
300 | return ret; | |
301 | ||
302 | return len; | |
303 | } | |
304 | ||
305 | static IIO_DEVICE_ATTR(sensor_sensitivity, S_IRUGO | S_IWUSR, | |
306 | srf08_show_sensitivity, srf08_store_sensitivity, 0); | |
307 | ||
308 | static struct attribute *srf08_attributes[] = { | |
309 | &iio_dev_attr_sensor_max_range.dev_attr.attr, | |
310 | &iio_dev_attr_sensor_max_range_available.dev_attr.attr, | |
311 | &iio_dev_attr_sensor_sensitivity.dev_attr.attr, | |
312 | &iio_dev_attr_sensor_sensitivity_available.dev_attr.attr, | |
313 | NULL, | |
314 | }; | |
315 | ||
316 | static const struct attribute_group srf08_attribute_group = { | |
317 | .attrs = srf08_attributes, | |
318 | }; | |
319 | ||
320 | static const struct iio_chan_spec srf08_channels[] = { | |
321 | { | |
322 | .type = IIO_DISTANCE, | |
323 | .info_mask_separate = | |
324 | BIT(IIO_CHAN_INFO_RAW) | | |
325 | BIT(IIO_CHAN_INFO_SCALE), | |
326 | }, | |
327 | }; | |
328 | ||
329 | static const struct iio_info srf08_info = { | |
330 | .read_raw = srf08_read_raw, | |
331 | .attrs = &srf08_attribute_group, | |
332 | .driver_module = THIS_MODULE, | |
333 | }; | |
334 | ||
335 | static int srf08_probe(struct i2c_client *client, | |
336 | const struct i2c_device_id *id) | |
337 | { | |
338 | struct iio_dev *indio_dev; | |
339 | struct srf08_data *data; | |
340 | int ret; | |
341 | ||
342 | if (!i2c_check_functionality(client->adapter, | |
343 | I2C_FUNC_SMBUS_READ_BYTE_DATA | | |
344 | I2C_FUNC_SMBUS_WRITE_BYTE_DATA | | |
345 | I2C_FUNC_SMBUS_READ_WORD_DATA)) | |
346 | return -ENODEV; | |
347 | ||
348 | indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); | |
349 | if (!indio_dev) | |
350 | return -ENOMEM; | |
351 | ||
352 | data = iio_priv(indio_dev); | |
353 | i2c_set_clientdata(client, indio_dev); | |
354 | data->client = client; | |
355 | ||
356 | indio_dev->name = "srf08"; | |
357 | indio_dev->dev.parent = &client->dev; | |
358 | indio_dev->modes = INDIO_DIRECT_MODE; | |
359 | indio_dev->info = &srf08_info; | |
360 | indio_dev->channels = srf08_channels; | |
361 | indio_dev->num_channels = ARRAY_SIZE(srf08_channels); | |
362 | ||
363 | mutex_init(&data->lock); | |
364 | ||
365 | /* | |
366 | * set default values of device here | |
367 | * these register values cannot be read from the hardware | |
368 | * therefore set driver specific default values | |
369 | */ | |
370 | ret = srf08_write_range_mm(data, SRF08_DEFAULT_RANGE); | |
371 | if (ret < 0) | |
372 | return ret; | |
373 | ||
374 | ret = srf08_write_sensitivity(data, SRF08_DEFAULT_GAIN); | |
375 | if (ret < 0) | |
376 | return ret; | |
377 | ||
378 | return devm_iio_device_register(&client->dev, indio_dev); | |
379 | } | |
380 | ||
381 | static const struct i2c_device_id srf08_id[] = { | |
382 | { "srf08", 0 }, | |
383 | { } | |
384 | }; | |
385 | MODULE_DEVICE_TABLE(i2c, srf08_id); | |
386 | ||
387 | static struct i2c_driver srf08_driver = { | |
388 | .driver = { | |
389 | .name = "srf08", | |
390 | }, | |
391 | .probe = srf08_probe, | |
392 | .id_table = srf08_id, | |
393 | }; | |
394 | module_i2c_driver(srf08_driver); | |
395 | ||
396 | MODULE_AUTHOR("Andreas Klinger <ak@it-klinger.de>"); | |
397 | MODULE_DESCRIPTION("Devantech SRF08 ultrasonic ranger driver"); | |
398 | MODULE_LICENSE("GPL"); |