dlhl60d.c (7952B)
1// SPDX-License-Identifier: GPL-2.0 2/* 3 * All Sensors DLH series low voltage digital pressure sensors 4 * 5 * Copyright (c) 2019 AVL DiTEST GmbH 6 * Tomislav Denis <tomislav.denis@avl.com> 7 * 8 * Datasheet: https://www.allsensors.com/cad/DS-0355_Rev_B.PDF 9 */ 10 11#include <linux/module.h> 12#include <linux/delay.h> 13#include <linux/i2c.h> 14#include <linux/iio/iio.h> 15#include <linux/iio/buffer.h> 16#include <linux/iio/trigger_consumer.h> 17#include <linux/iio/triggered_buffer.h> 18#include <asm/unaligned.h> 19 20/* Commands */ 21#define DLH_START_SINGLE 0xAA 22 23/* Status bits */ 24#define DLH_STATUS_OK 0x40 25 26/* DLH data format */ 27#define DLH_NUM_READ_BYTES 7 28#define DLH_NUM_DATA_BYTES 3 29#define DLH_NUM_PR_BITS 24 30#define DLH_NUM_TEMP_BITS 24 31 32/* DLH timings */ 33#define DLH_SINGLE_DUT_MS 5 34 35enum dhl_ids { 36 dlhl60d, 37 dlhl60g, 38}; 39 40struct dlh_info { 41 u8 osdig; /* digital offset factor */ 42 unsigned int fss; /* full scale span (inch H2O) */ 43}; 44 45struct dlh_state { 46 struct i2c_client *client; 47 struct dlh_info info; 48 bool use_interrupt; 49 struct completion completion; 50 u8 rx_buf[DLH_NUM_READ_BYTES] ____cacheline_aligned; 51}; 52 53static struct dlh_info dlh_info_tbl[] = { 54 [dlhl60d] = { 55 .osdig = 2, 56 .fss = 120, 57 }, 58 [dlhl60g] = { 59 .osdig = 10, 60 .fss = 60, 61 }, 62}; 63 64 65static int dlh_cmd_start_single(struct dlh_state *st) 66{ 67 int ret; 68 69 ret = i2c_smbus_write_byte(st->client, DLH_START_SINGLE); 70 if (ret) 71 dev_err(&st->client->dev, 72 "%s: I2C write byte failed\n", __func__); 73 74 return ret; 75} 76 77static int dlh_cmd_read_data(struct dlh_state *st) 78{ 79 int ret; 80 81 ret = i2c_master_recv(st->client, st->rx_buf, DLH_NUM_READ_BYTES); 82 if (ret < 0) { 83 dev_err(&st->client->dev, 84 "%s: I2C read block failed\n", __func__); 85 return ret; 86 } 87 88 if (st->rx_buf[0] != DLH_STATUS_OK) { 89 dev_err(&st->client->dev, 90 "%s: invalid status 0x%02x\n", __func__, st->rx_buf[0]); 91 return -EBUSY; 92 } 93 94 return 0; 95} 96 97static int dlh_start_capture_and_read(struct dlh_state *st) 98{ 99 int ret; 100 101 if (st->use_interrupt) 102 reinit_completion(&st->completion); 103 104 ret = dlh_cmd_start_single(st); 105 if (ret) 106 return ret; 107 108 if (st->use_interrupt) { 109 ret = wait_for_completion_timeout(&st->completion, 110 msecs_to_jiffies(DLH_SINGLE_DUT_MS)); 111 if (!ret) { 112 dev_err(&st->client->dev, 113 "%s: conversion timed out\n", __func__); 114 return -ETIMEDOUT; 115 } 116 } else { 117 mdelay(DLH_SINGLE_DUT_MS); 118 } 119 120 return dlh_cmd_read_data(st); 121} 122 123static int dlh_read_direct(struct dlh_state *st, 124 unsigned int *pressure, unsigned int *temperature) 125{ 126 int ret; 127 128 ret = dlh_start_capture_and_read(st); 129 if (ret) 130 return ret; 131 132 *pressure = get_unaligned_be32(&st->rx_buf[1]) >> 8; 133 *temperature = get_unaligned_be32(&st->rx_buf[3]) & 134 GENMASK(DLH_NUM_TEMP_BITS - 1, 0); 135 136 return 0; 137} 138 139static int dlh_read_raw(struct iio_dev *indio_dev, 140 struct iio_chan_spec const *channel, int *value, 141 int *value2, long mask) 142{ 143 struct dlh_state *st = iio_priv(indio_dev); 144 unsigned int pressure, temperature; 145 int ret; 146 s64 tmp; 147 s32 rem; 148 149 switch (mask) { 150 case IIO_CHAN_INFO_RAW: 151 ret = iio_device_claim_direct_mode(indio_dev); 152 if (ret) 153 return ret; 154 155 ret = dlh_read_direct(st, &pressure, &temperature); 156 iio_device_release_direct_mode(indio_dev); 157 if (ret) 158 return ret; 159 160 switch (channel->type) { 161 case IIO_PRESSURE: 162 *value = pressure; 163 return IIO_VAL_INT; 164 165 case IIO_TEMP: 166 *value = temperature; 167 return IIO_VAL_INT; 168 169 default: 170 return -EINVAL; 171 } 172 case IIO_CHAN_INFO_SCALE: 173 switch (channel->type) { 174 case IIO_PRESSURE: 175 tmp = div_s64(125LL * st->info.fss * 24909 * 100, 176 1 << DLH_NUM_PR_BITS); 177 tmp = div_s64_rem(tmp, 1000000000LL, &rem); 178 *value = tmp; 179 *value2 = rem; 180 return IIO_VAL_INT_PLUS_NANO; 181 182 case IIO_TEMP: 183 *value = 125 * 1000; 184 *value2 = DLH_NUM_TEMP_BITS; 185 return IIO_VAL_FRACTIONAL_LOG2; 186 187 default: 188 return -EINVAL; 189 } 190 case IIO_CHAN_INFO_OFFSET: 191 switch (channel->type) { 192 case IIO_PRESSURE: 193 *value = -125 * st->info.fss * 24909; 194 *value2 = 100 * st->info.osdig * 100000; 195 return IIO_VAL_FRACTIONAL; 196 197 case IIO_TEMP: 198 *value = -40 * 1000; 199 return IIO_VAL_INT; 200 201 default: 202 return -EINVAL; 203 } 204 } 205 206 return -EINVAL; 207} 208 209static const struct iio_info dlh_info = { 210 .read_raw = dlh_read_raw, 211}; 212 213static const struct iio_chan_spec dlh_channels[] = { 214 { 215 .type = IIO_PRESSURE, 216 .indexed = 1, 217 .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), 218 .info_mask_shared_by_type = 219 BIT(IIO_CHAN_INFO_SCALE) | 220 BIT(IIO_CHAN_INFO_OFFSET), 221 .scan_index = 0, 222 .scan_type = { 223 .sign = 'u', 224 .realbits = DLH_NUM_PR_BITS, 225 .storagebits = 32, 226 .shift = 8, 227 .endianness = IIO_BE, 228 }, 229 }, { 230 .type = IIO_TEMP, 231 .indexed = 1, 232 .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), 233 .info_mask_shared_by_type = 234 BIT(IIO_CHAN_INFO_SCALE) | 235 BIT(IIO_CHAN_INFO_OFFSET), 236 .scan_index = 1, 237 .scan_type = { 238 .sign = 'u', 239 .realbits = DLH_NUM_TEMP_BITS, 240 .storagebits = 32, 241 .shift = 8, 242 .endianness = IIO_BE, 243 }, 244 } 245}; 246 247static irqreturn_t dlh_trigger_handler(int irq, void *private) 248{ 249 struct iio_poll_func *pf = private; 250 struct iio_dev *indio_dev = pf->indio_dev; 251 struct dlh_state *st = iio_priv(indio_dev); 252 int ret; 253 unsigned int chn, i = 0; 254 __be32 tmp_buf[2]; 255 256 ret = dlh_start_capture_and_read(st); 257 if (ret) 258 goto out; 259 260 for_each_set_bit(chn, indio_dev->active_scan_mask, 261 indio_dev->masklength) { 262 memcpy(tmp_buf + i, 263 &st->rx_buf[1] + chn * DLH_NUM_DATA_BYTES, 264 DLH_NUM_DATA_BYTES); 265 i++; 266 } 267 268 iio_push_to_buffers(indio_dev, tmp_buf); 269 270out: 271 iio_trigger_notify_done(indio_dev->trig); 272 273 return IRQ_HANDLED; 274} 275 276static irqreturn_t dlh_interrupt(int irq, void *private) 277{ 278 struct iio_dev *indio_dev = private; 279 struct dlh_state *st = iio_priv(indio_dev); 280 281 complete(&st->completion); 282 283 return IRQ_HANDLED; 284}; 285 286static int dlh_probe(struct i2c_client *client, 287 const struct i2c_device_id *id) 288{ 289 struct dlh_state *st; 290 struct iio_dev *indio_dev; 291 int ret; 292 293 if (!i2c_check_functionality(client->adapter, 294 I2C_FUNC_I2C | I2C_FUNC_SMBUS_WRITE_BYTE)) { 295 dev_err(&client->dev, 296 "adapter doesn't support required i2c functionality\n"); 297 return -EOPNOTSUPP; 298 } 299 300 indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*st)); 301 if (!indio_dev) { 302 dev_err(&client->dev, "failed to allocate iio device\n"); 303 return -ENOMEM; 304 } 305 306 i2c_set_clientdata(client, indio_dev); 307 308 st = iio_priv(indio_dev); 309 st->info = dlh_info_tbl[id->driver_data]; 310 st->client = client; 311 st->use_interrupt = false; 312 313 indio_dev->name = id->name; 314 indio_dev->info = &dlh_info; 315 indio_dev->modes = INDIO_DIRECT_MODE; 316 indio_dev->channels = dlh_channels; 317 indio_dev->num_channels = ARRAY_SIZE(dlh_channels); 318 319 if (client->irq > 0) { 320 ret = devm_request_threaded_irq(&client->dev, client->irq, 321 dlh_interrupt, NULL, 322 IRQF_TRIGGER_RISING | IRQF_ONESHOT, 323 id->name, indio_dev); 324 if (ret) { 325 dev_err(&client->dev, "failed to allocate threaded irq"); 326 return ret; 327 } 328 329 st->use_interrupt = true; 330 init_completion(&st->completion); 331 } 332 333 ret = devm_iio_triggered_buffer_setup(&client->dev, indio_dev, 334 NULL, &dlh_trigger_handler, NULL); 335 if (ret) { 336 dev_err(&client->dev, "failed to setup iio buffer\n"); 337 return ret; 338 } 339 340 ret = devm_iio_device_register(&client->dev, indio_dev); 341 if (ret) 342 dev_err(&client->dev, "failed to register iio device\n"); 343 344 return ret; 345} 346 347static const struct of_device_id dlh_of_match[] = { 348 { .compatible = "asc,dlhl60d" }, 349 { .compatible = "asc,dlhl60g" }, 350 {} 351}; 352MODULE_DEVICE_TABLE(of, dlh_of_match); 353 354static const struct i2c_device_id dlh_id[] = { 355 { "dlhl60d", dlhl60d }, 356 { "dlhl60g", dlhl60g }, 357 {} 358}; 359MODULE_DEVICE_TABLE(i2c, dlh_id); 360 361static struct i2c_driver dlh_driver = { 362 .driver = { 363 .name = "dlhl60d", 364 .of_match_table = dlh_of_match, 365 }, 366 .probe = dlh_probe, 367 .id_table = dlh_id, 368}; 369module_i2c_driver(dlh_driver); 370 371MODULE_AUTHOR("Tomislav Denis <tomislav.denis@avl.com>"); 372MODULE_DESCRIPTION("Driver for All Sensors DLH series pressure sensors"); 373MODULE_LICENSE("GPL v2");