18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * mb1232.c - Support for MaxBotix I2CXL-MaxSonar-EZ series ultrasonic 48c2ecf20Sopenharmony_ci * ranger with i2c interface 58c2ecf20Sopenharmony_ci * actually tested with mb1232 type 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Copyright (c) 2019 Andreas Klinger <ak@it-klinger.de> 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * For details about the device see: 108c2ecf20Sopenharmony_ci * https://www.maxbotix.com/documents/I2CXL-MaxSonar-EZ_Datasheet.pdf 118c2ecf20Sopenharmony_ci */ 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#include <linux/err.h> 148c2ecf20Sopenharmony_ci#include <linux/i2c.h> 158c2ecf20Sopenharmony_ci#include <linux/of_irq.h> 168c2ecf20Sopenharmony_ci#include <linux/delay.h> 178c2ecf20Sopenharmony_ci#include <linux/module.h> 188c2ecf20Sopenharmony_ci#include <linux/bitops.h> 198c2ecf20Sopenharmony_ci#include <linux/iio/iio.h> 208c2ecf20Sopenharmony_ci#include <linux/iio/sysfs.h> 218c2ecf20Sopenharmony_ci#include <linux/iio/buffer.h> 228c2ecf20Sopenharmony_ci#include <linux/iio/trigger_consumer.h> 238c2ecf20Sopenharmony_ci#include <linux/iio/triggered_buffer.h> 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci/* registers of MaxSonar device */ 268c2ecf20Sopenharmony_ci#define MB1232_RANGE_COMMAND 0x51 /* Command for reading range */ 278c2ecf20Sopenharmony_ci#define MB1232_ADDR_UNLOCK_1 0xAA /* Command 1 for changing address */ 288c2ecf20Sopenharmony_ci#define MB1232_ADDR_UNLOCK_2 0xA5 /* Command 2 for changing address */ 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_cistruct mb1232_data { 318c2ecf20Sopenharmony_ci struct i2c_client *client; 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci struct mutex lock; 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci /* 368c2ecf20Sopenharmony_ci * optionally a gpio can be used to announce when ranging has 378c2ecf20Sopenharmony_ci * finished 388c2ecf20Sopenharmony_ci * since we are just using the falling trigger of it we request 398c2ecf20Sopenharmony_ci * only the interrupt for announcing when data is ready to be read 408c2ecf20Sopenharmony_ci */ 418c2ecf20Sopenharmony_ci struct completion ranging; 428c2ecf20Sopenharmony_ci int irqnr; 438c2ecf20Sopenharmony_ci /* Ensure correct alignment of data to push to IIO buffer */ 448c2ecf20Sopenharmony_ci struct { 458c2ecf20Sopenharmony_ci s16 distance; 468c2ecf20Sopenharmony_ci s64 ts __aligned(8); 478c2ecf20Sopenharmony_ci } scan; 488c2ecf20Sopenharmony_ci}; 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_cistatic irqreturn_t mb1232_handle_irq(int irq, void *dev_id) 518c2ecf20Sopenharmony_ci{ 528c2ecf20Sopenharmony_ci struct iio_dev *indio_dev = dev_id; 538c2ecf20Sopenharmony_ci struct mb1232_data *data = iio_priv(indio_dev); 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci complete(&data->ranging); 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci return IRQ_HANDLED; 588c2ecf20Sopenharmony_ci} 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_cistatic s16 mb1232_read_distance(struct mb1232_data *data) 618c2ecf20Sopenharmony_ci{ 628c2ecf20Sopenharmony_ci struct i2c_client *client = data->client; 638c2ecf20Sopenharmony_ci int ret; 648c2ecf20Sopenharmony_ci s16 distance; 658c2ecf20Sopenharmony_ci __be16 buf; 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci mutex_lock(&data->lock); 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci reinit_completion(&data->ranging); 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci ret = i2c_smbus_write_byte(client, MB1232_RANGE_COMMAND); 728c2ecf20Sopenharmony_ci if (ret < 0) { 738c2ecf20Sopenharmony_ci dev_err(&client->dev, "write command - err: %d\n", ret); 748c2ecf20Sopenharmony_ci goto error_unlock; 758c2ecf20Sopenharmony_ci } 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci if (data->irqnr >= 0) { 788c2ecf20Sopenharmony_ci /* it cannot take more than 100 ms */ 798c2ecf20Sopenharmony_ci ret = wait_for_completion_killable_timeout(&data->ranging, 808c2ecf20Sopenharmony_ci HZ/10); 818c2ecf20Sopenharmony_ci if (ret < 0) 828c2ecf20Sopenharmony_ci goto error_unlock; 838c2ecf20Sopenharmony_ci else if (ret == 0) { 848c2ecf20Sopenharmony_ci ret = -ETIMEDOUT; 858c2ecf20Sopenharmony_ci goto error_unlock; 868c2ecf20Sopenharmony_ci } 878c2ecf20Sopenharmony_ci } else { 888c2ecf20Sopenharmony_ci /* use simple sleep if announce irq is not connected */ 898c2ecf20Sopenharmony_ci msleep(15); 908c2ecf20Sopenharmony_ci } 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci ret = i2c_master_recv(client, (char *)&buf, sizeof(buf)); 938c2ecf20Sopenharmony_ci if (ret < 0) { 948c2ecf20Sopenharmony_ci dev_err(&client->dev, "i2c_master_recv: ret=%d\n", ret); 958c2ecf20Sopenharmony_ci goto error_unlock; 968c2ecf20Sopenharmony_ci } 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci distance = __be16_to_cpu(buf); 998c2ecf20Sopenharmony_ci /* check for not returning misleading error codes */ 1008c2ecf20Sopenharmony_ci if (distance < 0) { 1018c2ecf20Sopenharmony_ci dev_err(&client->dev, "distance=%d\n", distance); 1028c2ecf20Sopenharmony_ci ret = -EINVAL; 1038c2ecf20Sopenharmony_ci goto error_unlock; 1048c2ecf20Sopenharmony_ci } 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci mutex_unlock(&data->lock); 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci return distance; 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_cierror_unlock: 1118c2ecf20Sopenharmony_ci mutex_unlock(&data->lock); 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci return ret; 1148c2ecf20Sopenharmony_ci} 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_cistatic irqreturn_t mb1232_trigger_handler(int irq, void *p) 1178c2ecf20Sopenharmony_ci{ 1188c2ecf20Sopenharmony_ci struct iio_poll_func *pf = p; 1198c2ecf20Sopenharmony_ci struct iio_dev *indio_dev = pf->indio_dev; 1208c2ecf20Sopenharmony_ci struct mb1232_data *data = iio_priv(indio_dev); 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci data->scan.distance = mb1232_read_distance(data); 1238c2ecf20Sopenharmony_ci if (data->scan.distance < 0) 1248c2ecf20Sopenharmony_ci goto err; 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci iio_push_to_buffers_with_timestamp(indio_dev, &data->scan, 1278c2ecf20Sopenharmony_ci pf->timestamp); 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_cierr: 1308c2ecf20Sopenharmony_ci iio_trigger_notify_done(indio_dev->trig); 1318c2ecf20Sopenharmony_ci return IRQ_HANDLED; 1328c2ecf20Sopenharmony_ci} 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_cistatic int mb1232_read_raw(struct iio_dev *indio_dev, 1358c2ecf20Sopenharmony_ci struct iio_chan_spec const *channel, int *val, 1368c2ecf20Sopenharmony_ci int *val2, long mask) 1378c2ecf20Sopenharmony_ci{ 1388c2ecf20Sopenharmony_ci struct mb1232_data *data = iio_priv(indio_dev); 1398c2ecf20Sopenharmony_ci int ret; 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci if (channel->type != IIO_DISTANCE) 1428c2ecf20Sopenharmony_ci return -EINVAL; 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci switch (mask) { 1458c2ecf20Sopenharmony_ci case IIO_CHAN_INFO_RAW: 1468c2ecf20Sopenharmony_ci ret = mb1232_read_distance(data); 1478c2ecf20Sopenharmony_ci if (ret < 0) 1488c2ecf20Sopenharmony_ci return ret; 1498c2ecf20Sopenharmony_ci *val = ret; 1508c2ecf20Sopenharmony_ci return IIO_VAL_INT; 1518c2ecf20Sopenharmony_ci case IIO_CHAN_INFO_SCALE: 1528c2ecf20Sopenharmony_ci /* 1 LSB is 1 cm */ 1538c2ecf20Sopenharmony_ci *val = 0; 1548c2ecf20Sopenharmony_ci *val2 = 10000; 1558c2ecf20Sopenharmony_ci return IIO_VAL_INT_PLUS_MICRO; 1568c2ecf20Sopenharmony_ci default: 1578c2ecf20Sopenharmony_ci return -EINVAL; 1588c2ecf20Sopenharmony_ci } 1598c2ecf20Sopenharmony_ci} 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_cistatic const struct iio_chan_spec mb1232_channels[] = { 1628c2ecf20Sopenharmony_ci { 1638c2ecf20Sopenharmony_ci .type = IIO_DISTANCE, 1648c2ecf20Sopenharmony_ci .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | 1658c2ecf20Sopenharmony_ci BIT(IIO_CHAN_INFO_SCALE), 1668c2ecf20Sopenharmony_ci .scan_index = 0, 1678c2ecf20Sopenharmony_ci .scan_type = { 1688c2ecf20Sopenharmony_ci .sign = 's', 1698c2ecf20Sopenharmony_ci .realbits = 16, 1708c2ecf20Sopenharmony_ci .storagebits = 16, 1718c2ecf20Sopenharmony_ci .endianness = IIO_CPU, 1728c2ecf20Sopenharmony_ci }, 1738c2ecf20Sopenharmony_ci }, 1748c2ecf20Sopenharmony_ci IIO_CHAN_SOFT_TIMESTAMP(1), 1758c2ecf20Sopenharmony_ci}; 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_cistatic const struct iio_info mb1232_info = { 1788c2ecf20Sopenharmony_ci .read_raw = mb1232_read_raw, 1798c2ecf20Sopenharmony_ci}; 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_cistatic int mb1232_probe(struct i2c_client *client, 1828c2ecf20Sopenharmony_ci const struct i2c_device_id *id) 1838c2ecf20Sopenharmony_ci{ 1848c2ecf20Sopenharmony_ci struct iio_dev *indio_dev; 1858c2ecf20Sopenharmony_ci struct mb1232_data *data; 1868c2ecf20Sopenharmony_ci int ret; 1878c2ecf20Sopenharmony_ci struct device *dev = &client->dev; 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci if (!i2c_check_functionality(client->adapter, 1908c2ecf20Sopenharmony_ci I2C_FUNC_SMBUS_READ_BYTE | 1918c2ecf20Sopenharmony_ci I2C_FUNC_SMBUS_WRITE_BYTE)) 1928c2ecf20Sopenharmony_ci return -ENODEV; 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci indio_dev = devm_iio_device_alloc(dev, sizeof(*data)); 1958c2ecf20Sopenharmony_ci if (!indio_dev) 1968c2ecf20Sopenharmony_ci return -ENOMEM; 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci data = iio_priv(indio_dev); 1998c2ecf20Sopenharmony_ci i2c_set_clientdata(client, indio_dev); 2008c2ecf20Sopenharmony_ci data->client = client; 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci indio_dev->info = &mb1232_info; 2038c2ecf20Sopenharmony_ci indio_dev->name = id->name; 2048c2ecf20Sopenharmony_ci indio_dev->modes = INDIO_DIRECT_MODE; 2058c2ecf20Sopenharmony_ci indio_dev->channels = mb1232_channels; 2068c2ecf20Sopenharmony_ci indio_dev->num_channels = ARRAY_SIZE(mb1232_channels); 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci mutex_init(&data->lock); 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci init_completion(&data->ranging); 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci data->irqnr = irq_of_parse_and_map(dev->of_node, 0); 2138c2ecf20Sopenharmony_ci if (data->irqnr <= 0) { 2148c2ecf20Sopenharmony_ci /* usage of interrupt is optional */ 2158c2ecf20Sopenharmony_ci data->irqnr = -1; 2168c2ecf20Sopenharmony_ci } else { 2178c2ecf20Sopenharmony_ci ret = devm_request_irq(dev, data->irqnr, mb1232_handle_irq, 2188c2ecf20Sopenharmony_ci IRQF_TRIGGER_FALLING, id->name, indio_dev); 2198c2ecf20Sopenharmony_ci if (ret < 0) { 2208c2ecf20Sopenharmony_ci dev_err(dev, "request_irq: %d\n", ret); 2218c2ecf20Sopenharmony_ci return ret; 2228c2ecf20Sopenharmony_ci } 2238c2ecf20Sopenharmony_ci } 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci ret = devm_iio_triggered_buffer_setup(dev, indio_dev, 2268c2ecf20Sopenharmony_ci iio_pollfunc_store_time, mb1232_trigger_handler, NULL); 2278c2ecf20Sopenharmony_ci if (ret < 0) { 2288c2ecf20Sopenharmony_ci dev_err(dev, "setup of iio triggered buffer failed\n"); 2298c2ecf20Sopenharmony_ci return ret; 2308c2ecf20Sopenharmony_ci } 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci return devm_iio_device_register(dev, indio_dev); 2338c2ecf20Sopenharmony_ci} 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_cistatic const struct of_device_id of_mb1232_match[] = { 2368c2ecf20Sopenharmony_ci { .compatible = "maxbotix,mb1202", }, 2378c2ecf20Sopenharmony_ci { .compatible = "maxbotix,mb1212", }, 2388c2ecf20Sopenharmony_ci { .compatible = "maxbotix,mb1222", }, 2398c2ecf20Sopenharmony_ci { .compatible = "maxbotix,mb1232", }, 2408c2ecf20Sopenharmony_ci { .compatible = "maxbotix,mb1242", }, 2418c2ecf20Sopenharmony_ci { .compatible = "maxbotix,mb7040", }, 2428c2ecf20Sopenharmony_ci { .compatible = "maxbotix,mb7137", }, 2438c2ecf20Sopenharmony_ci {}, 2448c2ecf20Sopenharmony_ci}; 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, of_mb1232_match); 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_cistatic const struct i2c_device_id mb1232_id[] = { 2498c2ecf20Sopenharmony_ci { "maxbotix-mb1202", }, 2508c2ecf20Sopenharmony_ci { "maxbotix-mb1212", }, 2518c2ecf20Sopenharmony_ci { "maxbotix-mb1222", }, 2528c2ecf20Sopenharmony_ci { "maxbotix-mb1232", }, 2538c2ecf20Sopenharmony_ci { "maxbotix-mb1242", }, 2548c2ecf20Sopenharmony_ci { "maxbotix-mb7040", }, 2558c2ecf20Sopenharmony_ci { "maxbotix-mb7137", }, 2568c2ecf20Sopenharmony_ci { } 2578c2ecf20Sopenharmony_ci}; 2588c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, mb1232_id); 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_cistatic struct i2c_driver mb1232_driver = { 2618c2ecf20Sopenharmony_ci .driver = { 2628c2ecf20Sopenharmony_ci .name = "maxbotix-mb1232", 2638c2ecf20Sopenharmony_ci .of_match_table = of_mb1232_match, 2648c2ecf20Sopenharmony_ci }, 2658c2ecf20Sopenharmony_ci .probe = mb1232_probe, 2668c2ecf20Sopenharmony_ci .id_table = mb1232_id, 2678c2ecf20Sopenharmony_ci}; 2688c2ecf20Sopenharmony_cimodule_i2c_driver(mb1232_driver); 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ciMODULE_AUTHOR("Andreas Klinger <ak@it-klinger.de>"); 2718c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Maxbotix I2CXL-MaxSonar i2c ultrasonic ranger driver"); 2728c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 273