371 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * All Sensors DLH series low voltage digital pressure sensors4 *5 * Copyright (c) 2019 AVL DiTEST GmbH6 * Tomislav Denis <tomislav.denis@avl.com>7 *8 * Datasheet: https://www.allsensors.com/cad/DS-0355_Rev_B.PDF9 */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 <linux/unaligned.h>19 20/* Commands */21#define DLH_START_SINGLE 0xAA22 23/* Status bits */24#define DLH_STATUS_OK 0x4025 26/* DLH data format */27#define DLH_NUM_READ_BYTES 728#define DLH_NUM_DATA_BYTES 329#define DLH_NUM_PR_BITS 2430#define DLH_NUM_TEMP_BITS 2431 32/* DLH timings */33#define DLH_SINGLE_DUT_MS 534 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];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_be24(&st->rx_buf[1]);133 *temperature = get_unaligned_be24(&st->rx_buf[4]);134 135 return 0;136}137 138static int dlh_read_raw(struct iio_dev *indio_dev,139 struct iio_chan_spec const *channel, int *value,140 int *value2, long mask)141{142 struct dlh_state *st = iio_priv(indio_dev);143 unsigned int pressure, temperature;144 int ret;145 s64 tmp;146 s32 rem;147 148 switch (mask) {149 case IIO_CHAN_INFO_RAW:150 ret = iio_device_claim_direct_mode(indio_dev);151 if (ret)152 return ret;153 154 ret = dlh_read_direct(st, &pressure, &temperature);155 iio_device_release_direct_mode(indio_dev);156 if (ret)157 return ret;158 159 switch (channel->type) {160 case IIO_PRESSURE:161 *value = pressure;162 return IIO_VAL_INT;163 164 case IIO_TEMP:165 *value = temperature;166 return IIO_VAL_INT;167 168 default:169 return -EINVAL;170 }171 case IIO_CHAN_INFO_SCALE:172 switch (channel->type) {173 case IIO_PRESSURE:174 tmp = div_s64(125LL * st->info.fss * 24909 * 100,175 1 << DLH_NUM_PR_BITS);176 tmp = div_s64_rem(tmp, 1000000000LL, &rem);177 *value = tmp;178 *value2 = rem;179 return IIO_VAL_INT_PLUS_NANO;180 181 case IIO_TEMP:182 *value = 125 * 1000;183 *value2 = DLH_NUM_TEMP_BITS;184 return IIO_VAL_FRACTIONAL_LOG2;185 186 default:187 return -EINVAL;188 }189 case IIO_CHAN_INFO_OFFSET:190 switch (channel->type) {191 case IIO_PRESSURE:192 *value = -125 * st->info.fss * 24909;193 *value2 = 100 * st->info.osdig * 100000;194 return IIO_VAL_FRACTIONAL;195 196 case IIO_TEMP:197 *value = -40 * 1000;198 return IIO_VAL_INT;199 200 default:201 return -EINVAL;202 }203 }204 205 return -EINVAL;206}207 208static const struct iio_info dlh_info = {209 .read_raw = dlh_read_raw,210};211 212static const struct iio_chan_spec dlh_channels[] = {213 {214 .type = IIO_PRESSURE,215 .indexed = 1,216 .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),217 .info_mask_shared_by_type =218 BIT(IIO_CHAN_INFO_SCALE) |219 BIT(IIO_CHAN_INFO_OFFSET),220 .scan_index = 0,221 .scan_type = {222 .sign = 'u',223 .realbits = DLH_NUM_PR_BITS,224 .storagebits = 32,225 .shift = 8,226 .endianness = IIO_BE,227 },228 }, {229 .type = IIO_TEMP,230 .indexed = 1,231 .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),232 .info_mask_shared_by_type =233 BIT(IIO_CHAN_INFO_SCALE) |234 BIT(IIO_CHAN_INFO_OFFSET),235 .scan_index = 1,236 .scan_type = {237 .sign = 'u',238 .realbits = DLH_NUM_TEMP_BITS,239 .storagebits = 32,240 .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 iio_for_each_active_channel(indio_dev, chn) {260 memcpy(&tmp_buf[i++],261 &st->rx_buf[1] + chn * DLH_NUM_DATA_BYTES,262 DLH_NUM_DATA_BYTES);263 }264 265 iio_push_to_buffers(indio_dev, tmp_buf);266 267out:268 iio_trigger_notify_done(indio_dev->trig);269 270 return IRQ_HANDLED;271}272 273static irqreturn_t dlh_interrupt(int irq, void *private)274{275 struct iio_dev *indio_dev = private;276 struct dlh_state *st = iio_priv(indio_dev);277 278 complete(&st->completion);279 280 return IRQ_HANDLED;281};282 283static int dlh_probe(struct i2c_client *client)284{285 const struct i2c_device_id *id = i2c_client_get_device_id(client);286 struct dlh_state *st;287 struct iio_dev *indio_dev;288 int ret;289 290 if (!i2c_check_functionality(client->adapter,291 I2C_FUNC_I2C | I2C_FUNC_SMBUS_WRITE_BYTE)) {292 dev_err(&client->dev,293 "adapter doesn't support required i2c functionality\n");294 return -EOPNOTSUPP;295 }296 297 indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*st));298 if (!indio_dev) {299 dev_err(&client->dev, "failed to allocate iio device\n");300 return -ENOMEM;301 }302 303 i2c_set_clientdata(client, indio_dev);304 305 st = iio_priv(indio_dev);306 st->info = dlh_info_tbl[id->driver_data];307 st->client = client;308 st->use_interrupt = false;309 310 indio_dev->name = id->name;311 indio_dev->info = &dlh_info;312 indio_dev->modes = INDIO_DIRECT_MODE;313 indio_dev->channels = dlh_channels;314 indio_dev->num_channels = ARRAY_SIZE(dlh_channels);315 316 if (client->irq > 0) {317 ret = devm_request_threaded_irq(&client->dev, client->irq,318 dlh_interrupt, NULL,319 IRQF_TRIGGER_RISING | IRQF_ONESHOT,320 id->name, indio_dev);321 if (ret) {322 dev_err(&client->dev, "failed to allocate threaded irq");323 return ret;324 }325 326 st->use_interrupt = true;327 init_completion(&st->completion);328 }329 330 ret = devm_iio_triggered_buffer_setup(&client->dev, indio_dev,331 NULL, &dlh_trigger_handler, NULL);332 if (ret) {333 dev_err(&client->dev, "failed to setup iio buffer\n");334 return ret;335 }336 337 ret = devm_iio_device_register(&client->dev, indio_dev);338 if (ret)339 dev_err(&client->dev, "failed to register iio device\n");340 341 return ret;342}343 344static const struct of_device_id dlh_of_match[] = {345 { .compatible = "asc,dlhl60d" },346 { .compatible = "asc,dlhl60g" },347 {}348};349MODULE_DEVICE_TABLE(of, dlh_of_match);350 351static const struct i2c_device_id dlh_id[] = {352 { "dlhl60d", dlhl60d },353 { "dlhl60g", dlhl60g },354 {}355};356MODULE_DEVICE_TABLE(i2c, dlh_id);357 358static struct i2c_driver dlh_driver = {359 .driver = {360 .name = "dlhl60d",361 .of_match_table = dlh_of_match,362 },363 .probe = dlh_probe,364 .id_table = dlh_id,365};366module_i2c_driver(dlh_driver);367 368MODULE_AUTHOR("Tomislav Denis <tomislav.denis@avl.com>");369MODULE_DESCRIPTION("Driver for All Sensors DLH series pressure sensors");370MODULE_LICENSE("GPL v2");371