linux/drivers/iio/pressure/hp03.c
<<
>>
Prefs
   1/*
   2 * Copyright (c) 2016 Marek Vasut <marex@denx.de>
   3 *
   4 * Driver for Hope RF HP03 digital temperature and pressure sensor.
   5 *
   6 * This program is free software; you can redistribute it and/or modify
   7 * it under the terms of the GNU General Public License version 2 as
   8 * published by the Free Software Foundation.
   9 */
  10
  11#define pr_fmt(fmt) "hp03: " fmt
  12
  13#include <linux/module.h>
  14#include <linux/delay.h>
  15#include <linux/gpio/consumer.h>
  16#include <linux/i2c.h>
  17#include <linux/regmap.h>
  18#include <linux/iio/iio.h>
  19#include <linux/iio/sysfs.h>
  20
  21/*
  22 * The HP03 sensor occupies two fixed I2C addresses:
  23 *  0x50 ... read-only EEPROM with calibration data
  24 *  0x77 ... read-write ADC for pressure and temperature
  25 */
  26#define HP03_EEPROM_ADDR                0x50
  27#define HP03_ADC_ADDR                   0x77
  28
  29#define HP03_EEPROM_CX_OFFSET           0x10
  30#define HP03_EEPROM_AB_OFFSET           0x1e
  31#define HP03_EEPROM_CD_OFFSET           0x20
  32
  33#define HP03_ADC_WRITE_REG              0xff
  34#define HP03_ADC_READ_REG               0xfd
  35#define HP03_ADC_READ_PRESSURE          0xf0    /* D1 in datasheet */
  36#define HP03_ADC_READ_TEMP              0xe8    /* D2 in datasheet */
  37
  38struct hp03_priv {
  39        struct i2c_client       *client;
  40        struct mutex            lock;
  41        struct gpio_desc        *xclr_gpio;
  42
  43        struct i2c_client       *eeprom_client;
  44        struct regmap           *eeprom_regmap;
  45
  46        s32                     pressure;       /* kPa */
  47        s32                     temp;           /* Deg. C */
  48};
  49
  50static const struct iio_chan_spec hp03_channels[] = {
  51        {
  52                .type = IIO_PRESSURE,
  53                .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
  54                .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
  55        },
  56        {
  57                .type = IIO_TEMP,
  58                .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
  59                .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
  60        },
  61};
  62
  63static bool hp03_is_writeable_reg(struct device *dev, unsigned int reg)
  64{
  65        return false;
  66}
  67
  68static bool hp03_is_volatile_reg(struct device *dev, unsigned int reg)
  69{
  70        return false;
  71}
  72
  73static const struct regmap_config hp03_regmap_config = {
  74        .reg_bits       = 8,
  75        .val_bits       = 8,
  76
  77        .max_register   = HP03_EEPROM_CD_OFFSET + 1,
  78        .cache_type     = REGCACHE_RBTREE,
  79
  80        .writeable_reg  = hp03_is_writeable_reg,
  81        .volatile_reg   = hp03_is_volatile_reg,
  82};
  83
  84static int hp03_get_temp_pressure(struct hp03_priv *priv, const u8 reg)
  85{
  86        int ret;
  87
  88        ret = i2c_smbus_write_byte_data(priv->client, HP03_ADC_WRITE_REG, reg);
  89        if (ret < 0)
  90                return ret;
  91
  92        msleep(50);     /* Wait for conversion to finish */
  93
  94        return i2c_smbus_read_word_data(priv->client, HP03_ADC_READ_REG);
  95}
  96
  97static int hp03_update_temp_pressure(struct hp03_priv *priv)
  98{
  99        struct device *dev = &priv->client->dev;
 100        u8 coefs[18];
 101        u16 cx_val[7];
 102        int ab_val, d1_val, d2_val, diff_val, dut, off, sens, x;
 103        int i, ret;
 104
 105        /* Sample coefficients from EEPROM */
 106        ret = regmap_bulk_read(priv->eeprom_regmap, HP03_EEPROM_CX_OFFSET,
 107                               coefs, sizeof(coefs));
 108        if (ret < 0) {
 109                dev_err(dev, "Failed to read EEPROM (reg=%02x)\n",
 110                        HP03_EEPROM_CX_OFFSET);
 111                return ret;
 112        }
 113
 114        /* Sample Temperature and Pressure */
 115        gpiod_set_value_cansleep(priv->xclr_gpio, 1);
 116
 117        ret = hp03_get_temp_pressure(priv, HP03_ADC_READ_PRESSURE);
 118        if (ret < 0) {
 119                dev_err(dev, "Failed to read pressure\n");
 120                goto err_adc;
 121        }
 122        d1_val = ret;
 123
 124        ret = hp03_get_temp_pressure(priv, HP03_ADC_READ_TEMP);
 125        if (ret < 0) {
 126                dev_err(dev, "Failed to read temperature\n");
 127                goto err_adc;
 128        }
 129        d2_val = ret;
 130
 131        gpiod_set_value_cansleep(priv->xclr_gpio, 0);
 132
 133        /* The Cx coefficients and Temp/Pressure values are MSB first. */
 134        for (i = 0; i < 7; i++)
 135                cx_val[i] = (coefs[2 * i] << 8) | (coefs[(2 * i) + 1] << 0);
 136        d1_val = ((d1_val >> 8) & 0xff) | ((d1_val & 0xff) << 8);
 137        d2_val = ((d2_val >> 8) & 0xff) | ((d2_val & 0xff) << 8);
 138
 139        /* Coefficient voodoo from the HP03 datasheet. */
 140        if (d2_val >= cx_val[4])
 141                ab_val = coefs[14];     /* A-value */
 142        else
 143                ab_val = coefs[15];     /* B-value */
 144
 145        diff_val = d2_val - cx_val[4];
 146        dut = (ab_val * (diff_val >> 7) * (diff_val >> 7)) >> coefs[16];
 147        dut = diff_val - dut;
 148
 149        off = (cx_val[1] + (((cx_val[3] - 1024) * dut) >> 14)) * 4;
 150        sens = cx_val[0] + ((cx_val[2] * dut) >> 10);
 151        x = ((sens * (d1_val - 7168)) >> 14) - off;
 152
 153        priv->pressure = ((x * 100) >> 5) + (cx_val[6] * 10);
 154        priv->temp = 250 + ((dut * cx_val[5]) >> 16) - (dut >> coefs[17]);
 155
 156        return 0;
 157
 158err_adc:
 159        gpiod_set_value_cansleep(priv->xclr_gpio, 0);
 160        return ret;
 161}
 162
 163static int hp03_read_raw(struct iio_dev *indio_dev,
 164                         struct iio_chan_spec const *chan,
 165                         int *val, int *val2, long mask)
 166{
 167        struct hp03_priv *priv = iio_priv(indio_dev);
 168        int ret;
 169
 170        mutex_lock(&priv->lock);
 171        ret = hp03_update_temp_pressure(priv);
 172        mutex_unlock(&priv->lock);
 173
 174        if (ret)
 175                return ret;
 176
 177        switch (mask) {
 178        case IIO_CHAN_INFO_RAW:
 179                switch (chan->type) {
 180                case IIO_PRESSURE:
 181                        *val = priv->pressure;
 182                        return IIO_VAL_INT;
 183                case IIO_TEMP:
 184                        *val = priv->temp;
 185                        return IIO_VAL_INT;
 186                default:
 187                        return -EINVAL;
 188                }
 189                break;
 190        case IIO_CHAN_INFO_SCALE:
 191                switch (chan->type) {
 192                case IIO_PRESSURE:
 193                        *val = 0;
 194                        *val2 = 1000;
 195                        return IIO_VAL_INT_PLUS_MICRO;
 196                case IIO_TEMP:
 197                        *val = 10;
 198                        return IIO_VAL_INT;
 199                default:
 200                        return -EINVAL;
 201                }
 202                break;
 203        default:
 204                return -EINVAL;
 205        }
 206
 207        return -EINVAL;
 208}
 209
 210static const struct iio_info hp03_info = {
 211        .read_raw       = &hp03_read_raw,
 212};
 213
 214static int hp03_probe(struct i2c_client *client,
 215                      const struct i2c_device_id *id)
 216{
 217        struct device *dev = &client->dev;
 218        struct iio_dev *indio_dev;
 219        struct hp03_priv *priv;
 220        int ret;
 221
 222        indio_dev = devm_iio_device_alloc(dev, sizeof(*priv));
 223        if (!indio_dev)
 224                return -ENOMEM;
 225
 226        priv = iio_priv(indio_dev);
 227        priv->client = client;
 228        mutex_init(&priv->lock);
 229
 230        indio_dev->dev.parent = dev;
 231        indio_dev->name = id->name;
 232        indio_dev->channels = hp03_channels;
 233        indio_dev->num_channels = ARRAY_SIZE(hp03_channels);
 234        indio_dev->info = &hp03_info;
 235        indio_dev->modes = INDIO_DIRECT_MODE;
 236
 237        priv->xclr_gpio = devm_gpiod_get_index(dev, "xclr", 0, GPIOD_OUT_HIGH);
 238        if (IS_ERR(priv->xclr_gpio)) {
 239                dev_err(dev, "Failed to claim XCLR GPIO\n");
 240                ret = PTR_ERR(priv->xclr_gpio);
 241                return ret;
 242        }
 243
 244        /*
 245         * Allocate another device for the on-sensor EEPROM,
 246         * which has it's dedicated I2C address and contains
 247         * the calibration constants for the sensor.
 248         */
 249        priv->eeprom_client = i2c_new_dummy(client->adapter, HP03_EEPROM_ADDR);
 250        if (!priv->eeprom_client) {
 251                dev_err(dev, "New EEPROM I2C device failed\n");
 252                return -ENODEV;
 253        }
 254
 255        priv->eeprom_regmap = regmap_init_i2c(priv->eeprom_client,
 256                                              &hp03_regmap_config);
 257        if (IS_ERR(priv->eeprom_regmap)) {
 258                dev_err(dev, "Failed to allocate EEPROM regmap\n");
 259                ret = PTR_ERR(priv->eeprom_regmap);
 260                goto err_cleanup_eeprom_client;
 261        }
 262
 263        ret = iio_device_register(indio_dev);
 264        if (ret) {
 265                dev_err(dev, "Failed to register IIO device\n");
 266                goto err_cleanup_eeprom_regmap;
 267        }
 268
 269        i2c_set_clientdata(client, indio_dev);
 270
 271        return 0;
 272
 273err_cleanup_eeprom_regmap:
 274        regmap_exit(priv->eeprom_regmap);
 275
 276err_cleanup_eeprom_client:
 277        i2c_unregister_device(priv->eeprom_client);
 278        return ret;
 279}
 280
 281static int hp03_remove(struct i2c_client *client)
 282{
 283        struct iio_dev *indio_dev = i2c_get_clientdata(client);
 284        struct hp03_priv *priv = iio_priv(indio_dev);
 285
 286        iio_device_unregister(indio_dev);
 287        regmap_exit(priv->eeprom_regmap);
 288        i2c_unregister_device(priv->eeprom_client);
 289
 290        return 0;
 291}
 292
 293static const struct i2c_device_id hp03_id[] = {
 294        { "hp03", 0 },
 295        { },
 296};
 297MODULE_DEVICE_TABLE(i2c, hp03_id);
 298
 299static const struct of_device_id hp03_of_match[] = {
 300        { .compatible = "hoperf,hp03" },
 301        { },
 302};
 303MODULE_DEVICE_TABLE(of, hp03_of_match);
 304
 305static struct i2c_driver hp03_driver = {
 306        .driver = {
 307                .name   = "hp03",
 308                .of_match_table = hp03_of_match,
 309        },
 310        .probe          = hp03_probe,
 311        .remove         = hp03_remove,
 312        .id_table       = hp03_id,
 313};
 314module_i2c_driver(hp03_driver);
 315
 316MODULE_AUTHOR("Marek Vasut <marex@denx.de>");
 317MODULE_DESCRIPTION("Driver for Hope RF HP03 pressure and temperature sensor");
 318MODULE_LICENSE("GPL v2");
 319