1234353Sdim// SPDX-License-Identifier: GPL-2.0 2204642Srdivacky/* 3204642Srdivacky * All Sensors DLH series low voltage digital pressure sensors 4204642Srdivacky * 5204642Srdivacky * Copyright (c) 2019 AVL DiTEST GmbH 6204642Srdivacky * Tomislav Denis <tomislav.denis@avl.com> 7204642Srdivacky * 8204642Srdivacky * Datasheet: https://www.allsensors.com/cad/DS-0355_Rev_B.PDF 9204642Srdivacky */ 10204642Srdivacky 11204642Srdivacky#include <linux/module.h> 12204642Srdivacky#include <linux/delay.h> 13204642Srdivacky#include <linux/i2c.h> 14204642Srdivacky#include <linux/iio/iio.h> 15204642Srdivacky#include <linux/iio/buffer.h> 16204642Srdivacky#include <linux/iio/trigger_consumer.h> 17204642Srdivacky#include <linux/iio/triggered_buffer.h> 18234353Sdim#include <asm/unaligned.h> 19204642Srdivacky 20204642Srdivacky/* Commands */ 21204642Srdivacky#define DLH_START_SINGLE 0xAA 22224145Sdim 23224145Sdim/* Status bits */ 24224145Sdim#define DLH_STATUS_OK 0x40 25204642Srdivacky 26204642Srdivacky/* DLH data format */ 27204642Srdivacky#define DLH_NUM_READ_BYTES 7 28204642Srdivacky#define DLH_NUM_DATA_BYTES 3 29204642Srdivacky#define DLH_NUM_PR_BITS 24 30204642Srdivacky#define DLH_NUM_TEMP_BITS 24 31204642Srdivacky 32204642Srdivacky/* DLH timings */ 33204642Srdivacky#define DLH_SINGLE_DUT_MS 5 34204642Srdivacky 35204642Srdivackyenum dhl_ids { 36204642Srdivacky dlhl60d, 37204642Srdivacky dlhl60g, 38204642Srdivacky}; 39204642Srdivacky 40204642Srdivackystruct dlh_info { 41204642Srdivacky u8 osdig; /* digital offset factor */ 42204642Srdivacky unsigned int fss; /* full scale span (inch H2O) */ 43204642Srdivacky}; 44204642Srdivacky 45204642Srdivackystruct dlh_state { 46204642Srdivacky struct i2c_client *client; 47204642Srdivacky struct dlh_info info; 48204642Srdivacky bool use_interrupt; 49204642Srdivacky struct completion completion; 50204642Srdivacky u8 rx_buf[DLH_NUM_READ_BYTES]; 51204642Srdivacky}; 52204642Srdivacky 53204642Srdivackystatic struct dlh_info dlh_info_tbl[] = { 54204642Srdivacky [dlhl60d] = { 55204642Srdivacky .osdig = 2, 56204642Srdivacky .fss = 120, 57204642Srdivacky }, 58204642Srdivacky [dlhl60g] = { 59204642Srdivacky .osdig = 10, 60204642Srdivacky .fss = 60, 61204642Srdivacky }, 62204642Srdivacky}; 63204642Srdivacky 64204642Srdivacky 65204642Srdivackystatic int dlh_cmd_start_single(struct dlh_state *st) 66204642Srdivacky{ 67204642Srdivacky int ret; 68204642Srdivacky 69204642Srdivacky ret = i2c_smbus_write_byte(st->client, DLH_START_SINGLE); 70204642Srdivacky if (ret) 71204642Srdivacky dev_err(&st->client->dev, 72204642Srdivacky "%s: I2C write byte failed\n", __func__); 73204642Srdivacky 74204642Srdivacky return ret; 75204642Srdivacky} 76204642Srdivacky 77204642Srdivackystatic int dlh_cmd_read_data(struct dlh_state *st) 78204642Srdivacky{ 79218893Sdim int ret; 80218893Sdim 81218893Sdim ret = i2c_master_recv(st->client, st->rx_buf, DLH_NUM_READ_BYTES); 82218893Sdim if (ret < 0) { 83218893Sdim dev_err(&st->client->dev, 84204642Srdivacky "%s: I2C read block failed\n", __func__); 85204642Srdivacky return ret; 86204642Srdivacky } 87204642Srdivacky 88204642Srdivacky if (st->rx_buf[0] != DLH_STATUS_OK) { 89218893Sdim dev_err(&st->client->dev, 90218893Sdim "%s: invalid status 0x%02x\n", __func__, st->rx_buf[0]); 91218893Sdim return -EBUSY; 92218893Sdim } 93218893Sdim 94218893Sdim return 0; 95218893Sdim} 96218893Sdim 97218893Sdimstatic int dlh_start_capture_and_read(struct dlh_state *st) 98218893Sdim{ 99218893Sdim int ret; 100204642Srdivacky 101204642Srdivacky if (st->use_interrupt) 102204642Srdivacky reinit_completion(&st->completion); 103218893Sdim 104204642Srdivacky ret = dlh_cmd_start_single(st); 105204642Srdivacky if (ret) 106218893Sdim return ret; 107204642Srdivacky 108218893Sdim if (st->use_interrupt) { 109218893Sdim ret = wait_for_completion_timeout(&st->completion, 110218893Sdim msecs_to_jiffies(DLH_SINGLE_DUT_MS)); 111218893Sdim if (!ret) { 112218893Sdim dev_err(&st->client->dev, 113218893Sdim "%s: conversion timed out\n", __func__); 114218893Sdim return -ETIMEDOUT; 115218893Sdim } 116218893Sdim } else { 117218893Sdim mdelay(DLH_SINGLE_DUT_MS); 118218893Sdim } 119218893Sdim 120218893Sdim return dlh_cmd_read_data(st); 121218893Sdim} 122218893Sdim 123218893Sdimstatic int dlh_read_direct(struct dlh_state *st, 124218893Sdim unsigned int *pressure, unsigned int *temperature) 125218893Sdim{ 126218893Sdim int ret; 127218893Sdim 128218893Sdim ret = dlh_start_capture_and_read(st); 129218893Sdim if (ret) 130218893Sdim return ret; 131218893Sdim 132218893Sdim *pressure = get_unaligned_be24(&st->rx_buf[1]); 133218893Sdim *temperature = get_unaligned_be24(&st->rx_buf[4]); 134218893Sdim 135218893Sdim return 0; 136218893Sdim} 137218893Sdim 138218893Sdimstatic int dlh_read_raw(struct iio_dev *indio_dev, 139218893Sdim struct iio_chan_spec const *channel, int *value, 140218893Sdim int *value2, long mask) 141204642Srdivacky{ 142204642Srdivacky struct dlh_state *st = iio_priv(indio_dev); 143218893Sdim unsigned int pressure, temperature; 144218893Sdim int ret; 145218893Sdim s64 tmp; 146218893Sdim s32 rem; 147218893Sdim 148218893Sdim switch (mask) { 149218893Sdim case IIO_CHAN_INFO_RAW: 150218893Sdim ret = iio_device_claim_direct_mode(indio_dev); 151218893Sdim if (ret) 152218893Sdim return ret; 153218893Sdim 154218893Sdim ret = dlh_read_direct(st, &pressure, &temperature); 155218893Sdim iio_device_release_direct_mode(indio_dev); 156218893Sdim if (ret) 157218893Sdim return ret; 158218893Sdim 159218893Sdim switch (channel->type) { 160218893Sdim case IIO_PRESSURE: 161218893Sdim *value = pressure; 162218893Sdim return IIO_VAL_INT; 163218893Sdim 164218893Sdim case IIO_TEMP: 165218893Sdim *value = temperature; 166218893Sdim return IIO_VAL_INT; 167204642Srdivacky 168204642Srdivacky default: 169224145Sdim return -EINVAL; 170204642Srdivacky } 171204642Srdivacky case IIO_CHAN_INFO_SCALE: 172204642Srdivacky switch (channel->type) { 173204642Srdivacky case IIO_PRESSURE: 174204642Srdivacky tmp = div_s64(125LL * st->info.fss * 24909 * 100, 175204642Srdivacky 1 << DLH_NUM_PR_BITS); 176204642Srdivacky tmp = div_s64_rem(tmp, 1000000000LL, &rem); 177204642Srdivacky *value = tmp; 178204642Srdivacky *value2 = rem; 179204642Srdivacky return IIO_VAL_INT_PLUS_NANO; 180204642Srdivacky 181204642Srdivacky case IIO_TEMP: 182204642Srdivacky *value = 125 * 1000; 183204642Srdivacky *value2 = DLH_NUM_TEMP_BITS; 184204642Srdivacky return IIO_VAL_FRACTIONAL_LOG2; 185204642Srdivacky 186204642Srdivacky default: 187204642Srdivacky return -EINVAL; 188204642Srdivacky } 189204642Srdivacky case IIO_CHAN_INFO_OFFSET: 190204642Srdivacky switch (channel->type) { 191204642Srdivacky case IIO_PRESSURE: 192204642Srdivacky *value = -125 * st->info.fss * 24909; 193204642Srdivacky *value2 = 100 * st->info.osdig * 100000; 194204642Srdivacky return IIO_VAL_FRACTIONAL; 195204642Srdivacky 196204642Srdivacky case IIO_TEMP: 197204642Srdivacky *value = -40 * 1000; 198218893Sdim return IIO_VAL_INT; 199218893Sdim 200218893Sdim default: 201218893Sdim return -EINVAL; 202204642Srdivacky } 203204642Srdivacky } 204210299Sed 205210299Sed return -EINVAL; 206218893Sdim} 207218893Sdim 208218893Sdimstatic const struct iio_info dlh_info = { 209218893Sdim .read_raw = dlh_read_raw, 210218893Sdim}; 211210299Sed 212210299Sedstatic const struct iio_chan_spec dlh_channels[] = { 213210299Sed { 214210299Sed .type = IIO_PRESSURE, 215204642Srdivacky .indexed = 1, 216204642Srdivacky .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), 217204642Srdivacky .info_mask_shared_by_type = 218208599Srdivacky BIT(IIO_CHAN_INFO_SCALE) | 219208599Srdivacky BIT(IIO_CHAN_INFO_OFFSET), 220204642Srdivacky .scan_index = 0, 221204642Srdivacky .scan_type = { 222204642Srdivacky .sign = 'u', 223204642Srdivacky .realbits = DLH_NUM_PR_BITS, 224208599Srdivacky .storagebits = 32, 225208599Srdivacky .shift = 8, 226204642Srdivacky .endianness = IIO_BE, 227204642Srdivacky }, 228204642Srdivacky }, { 229204642Srdivacky .type = IIO_TEMP, 230204642Srdivacky .indexed = 1, 231204642Srdivacky .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), 232204642Srdivacky .info_mask_shared_by_type = 233204642Srdivacky BIT(IIO_CHAN_INFO_SCALE) | 234204642Srdivacky BIT(IIO_CHAN_INFO_OFFSET), 235204642Srdivacky .scan_index = 1, 236204642Srdivacky .scan_type = { 237204642Srdivacky .sign = 'u', 238204642Srdivacky .realbits = DLH_NUM_TEMP_BITS, 239204642Srdivacky .storagebits = 32, 240204642Srdivacky .shift = 8, 241 .endianness = IIO_BE, 242 }, 243 } 244}; 245 246static irqreturn_t dlh_trigger_handler(int irq, void *private) 247{ 248 struct iio_poll_func *pf = private; 249 struct iio_dev *indio_dev = pf->indio_dev; 250 struct dlh_state *st = iio_priv(indio_dev); 251 int ret; 252 unsigned int chn, i = 0; 253 __be32 tmp_buf[2] = { }; 254 255 ret = dlh_start_capture_and_read(st); 256 if (ret) 257 goto out; 258 259 for_each_set_bit(chn, indio_dev->active_scan_mask, 260 indio_dev->masklength) { 261 memcpy(&tmp_buf[i++], 262 &st->rx_buf[1] + chn * DLH_NUM_DATA_BYTES, 263 DLH_NUM_DATA_BYTES); 264 } 265 266 iio_push_to_buffers(indio_dev, tmp_buf); 267 268out: 269 iio_trigger_notify_done(indio_dev->trig); 270 271 return IRQ_HANDLED; 272} 273 274static irqreturn_t dlh_interrupt(int irq, void *private) 275{ 276 struct iio_dev *indio_dev = private; 277 struct dlh_state *st = iio_priv(indio_dev); 278 279 complete(&st->completion); 280 281 return IRQ_HANDLED; 282}; 283 284static int dlh_probe(struct i2c_client *client) 285{ 286 const struct i2c_device_id *id = i2c_client_get_device_id(client); 287 struct dlh_state *st; 288 struct iio_dev *indio_dev; 289 int ret; 290 291 if (!i2c_check_functionality(client->adapter, 292 I2C_FUNC_I2C | I2C_FUNC_SMBUS_WRITE_BYTE)) { 293 dev_err(&client->dev, 294 "adapter doesn't support required i2c functionality\n"); 295 return -EOPNOTSUPP; 296 } 297 298 indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*st)); 299 if (!indio_dev) { 300 dev_err(&client->dev, "failed to allocate iio device\n"); 301 return -ENOMEM; 302 } 303 304 i2c_set_clientdata(client, indio_dev); 305 306 st = iio_priv(indio_dev); 307 st->info = dlh_info_tbl[id->driver_data]; 308 st->client = client; 309 st->use_interrupt = false; 310 311 indio_dev->name = id->name; 312 indio_dev->info = &dlh_info; 313 indio_dev->modes = INDIO_DIRECT_MODE; 314 indio_dev->channels = dlh_channels; 315 indio_dev->num_channels = ARRAY_SIZE(dlh_channels); 316 317 if (client->irq > 0) { 318 ret = devm_request_threaded_irq(&client->dev, client->irq, 319 dlh_interrupt, NULL, 320 IRQF_TRIGGER_RISING | IRQF_ONESHOT, 321 id->name, indio_dev); 322 if (ret) { 323 dev_err(&client->dev, "failed to allocate threaded irq"); 324 return ret; 325 } 326 327 st->use_interrupt = true; 328 init_completion(&st->completion); 329 } 330 331 ret = devm_iio_triggered_buffer_setup(&client->dev, indio_dev, 332 NULL, &dlh_trigger_handler, NULL); 333 if (ret) { 334 dev_err(&client->dev, "failed to setup iio buffer\n"); 335 return ret; 336 } 337 338 ret = devm_iio_device_register(&client->dev, indio_dev); 339 if (ret) 340 dev_err(&client->dev, "failed to register iio device\n"); 341 342 return ret; 343} 344 345static const struct of_device_id dlh_of_match[] = { 346 { .compatible = "asc,dlhl60d" }, 347 { .compatible = "asc,dlhl60g" }, 348 {} 349}; 350MODULE_DEVICE_TABLE(of, dlh_of_match); 351 352static const struct i2c_device_id dlh_id[] = { 353 { "dlhl60d", dlhl60d }, 354 { "dlhl60g", dlhl60g }, 355 {} 356}; 357MODULE_DEVICE_TABLE(i2c, dlh_id); 358 359static struct i2c_driver dlh_driver = { 360 .driver = { 361 .name = "dlhl60d", 362 .of_match_table = dlh_of_match, 363 }, 364 .probe = dlh_probe, 365 .id_table = dlh_id, 366}; 367module_i2c_driver(dlh_driver); 368 369MODULE_AUTHOR("Tomislav Denis <tomislav.denis@avl.com>"); 370MODULE_DESCRIPTION("Driver for All Sensors DLH series pressure sensors"); 371MODULE_LICENSE("GPL v2"); 372