linux/drivers/iio/adc/lp8788_adc.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * TI LP8788 MFD - ADC driver
   4 *
   5 * Copyright 2012 Texas Instruments
   6 *
   7 * Author: Milo(Woogyom) Kim <milo.kim@ti.com>
   8 */
   9
  10#include <linux/delay.h>
  11#include <linux/iio/iio.h>
  12#include <linux/iio/driver.h>
  13#include <linux/iio/machine.h>
  14#include <linux/mfd/lp8788.h>
  15#include <linux/module.h>
  16#include <linux/mutex.h>
  17#include <linux/platform_device.h>
  18#include <linux/slab.h>
  19
  20/* register address */
  21#define LP8788_ADC_CONF                 0x60
  22#define LP8788_ADC_RAW                  0x61
  23#define LP8788_ADC_DONE                 0x63
  24
  25#define ADC_CONV_START                  1
  26
  27struct lp8788_adc {
  28        struct lp8788 *lp;
  29        struct iio_map *map;
  30        struct mutex lock;
  31};
  32
  33static const int lp8788_scale[LPADC_MAX] = {
  34        [LPADC_VBATT_5P5] = 1343101,
  35        [LPADC_VIN_CHG]   = 3052503,
  36        [LPADC_IBATT]     = 610500,
  37        [LPADC_IC_TEMP]   = 61050,
  38        [LPADC_VBATT_6P0] = 1465201,
  39        [LPADC_VBATT_5P0] = 1221001,
  40        [LPADC_ADC1]      = 610500,
  41        [LPADC_ADC2]      = 610500,
  42        [LPADC_VDD]       = 1025641,
  43        [LPADC_VCOIN]     = 757020,
  44        [LPADC_ADC3]      = 610500,
  45        [LPADC_ADC4]      = 610500,
  46};
  47
  48static int lp8788_get_adc_result(struct lp8788_adc *adc, enum lp8788_adc_id id,
  49                                int *val)
  50{
  51        unsigned int msb;
  52        unsigned int lsb;
  53        unsigned int result;
  54        u8 data;
  55        u8 rawdata[2];
  56        int size = ARRAY_SIZE(rawdata);
  57        int retry = 5;
  58        int ret;
  59
  60        data = (id << 1) | ADC_CONV_START;
  61        ret = lp8788_write_byte(adc->lp, LP8788_ADC_CONF, data);
  62        if (ret)
  63                goto err_io;
  64
  65        /* retry until adc conversion is done */
  66        data = 0;
  67        while (retry--) {
  68                usleep_range(100, 200);
  69
  70                ret = lp8788_read_byte(adc->lp, LP8788_ADC_DONE, &data);
  71                if (ret)
  72                        goto err_io;
  73
  74                /* conversion done */
  75                if (data)
  76                        break;
  77        }
  78
  79        ret = lp8788_read_multi_bytes(adc->lp, LP8788_ADC_RAW, rawdata, size);
  80        if (ret)
  81                goto err_io;
  82
  83        msb = (rawdata[0] << 4) & 0x00000ff0;
  84        lsb = (rawdata[1] >> 4) & 0x0000000f;
  85        result = msb | lsb;
  86        *val = result;
  87
  88        return 0;
  89
  90err_io:
  91        return ret;
  92}
  93
  94static int lp8788_adc_read_raw(struct iio_dev *indio_dev,
  95                        struct iio_chan_spec const *chan,
  96                        int *val, int *val2, long mask)
  97{
  98        struct lp8788_adc *adc = iio_priv(indio_dev);
  99        enum lp8788_adc_id id = chan->channel;
 100        int ret;
 101
 102        mutex_lock(&adc->lock);
 103
 104        switch (mask) {
 105        case IIO_CHAN_INFO_RAW:
 106                ret = lp8788_get_adc_result(adc, id, val) ? -EIO : IIO_VAL_INT;
 107                break;
 108        case IIO_CHAN_INFO_SCALE:
 109                *val = lp8788_scale[id] / 1000000;
 110                *val2 = lp8788_scale[id] % 1000000;
 111                ret = IIO_VAL_INT_PLUS_MICRO;
 112                break;
 113        default:
 114                ret = -EINVAL;
 115                break;
 116        }
 117
 118        mutex_unlock(&adc->lock);
 119
 120        return ret;
 121}
 122
 123static const struct iio_info lp8788_adc_info = {
 124        .read_raw = &lp8788_adc_read_raw,
 125};
 126
 127#define LP8788_CHAN(_id, _type) {                               \
 128                .type = _type,                                  \
 129                .indexed = 1,                                   \
 130                .channel = LPADC_##_id,                         \
 131                .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |  \
 132                        BIT(IIO_CHAN_INFO_SCALE),               \
 133                .datasheet_name = #_id,                         \
 134}
 135
 136static const struct iio_chan_spec lp8788_adc_channels[] = {
 137        [LPADC_VBATT_5P5] = LP8788_CHAN(VBATT_5P5, IIO_VOLTAGE),
 138        [LPADC_VIN_CHG]   = LP8788_CHAN(VIN_CHG, IIO_VOLTAGE),
 139        [LPADC_IBATT]     = LP8788_CHAN(IBATT, IIO_CURRENT),
 140        [LPADC_IC_TEMP]   = LP8788_CHAN(IC_TEMP, IIO_TEMP),
 141        [LPADC_VBATT_6P0] = LP8788_CHAN(VBATT_6P0, IIO_VOLTAGE),
 142        [LPADC_VBATT_5P0] = LP8788_CHAN(VBATT_5P0, IIO_VOLTAGE),
 143        [LPADC_ADC1]      = LP8788_CHAN(ADC1, IIO_VOLTAGE),
 144        [LPADC_ADC2]      = LP8788_CHAN(ADC2, IIO_VOLTAGE),
 145        [LPADC_VDD]       = LP8788_CHAN(VDD, IIO_VOLTAGE),
 146        [LPADC_VCOIN]     = LP8788_CHAN(VCOIN, IIO_VOLTAGE),
 147        [LPADC_ADC3]      = LP8788_CHAN(ADC3, IIO_VOLTAGE),
 148        [LPADC_ADC4]      = LP8788_CHAN(ADC4, IIO_VOLTAGE),
 149};
 150
 151/* default maps used by iio consumer (lp8788-charger driver) */
 152static struct iio_map lp8788_default_iio_maps[] = {
 153        {
 154                .consumer_dev_name = "lp8788-charger",
 155                .consumer_channel = "lp8788_vbatt_5p0",
 156                .adc_channel_label = "VBATT_5P0",
 157        },
 158        {
 159                .consumer_dev_name = "lp8788-charger",
 160                .consumer_channel = "lp8788_adc1",
 161                .adc_channel_label = "ADC1",
 162        },
 163        { }
 164};
 165
 166static int lp8788_iio_map_register(struct iio_dev *indio_dev,
 167                                struct lp8788_platform_data *pdata,
 168                                struct lp8788_adc *adc)
 169{
 170        struct iio_map *map;
 171        int ret;
 172
 173        map = (!pdata || !pdata->adc_pdata) ?
 174                lp8788_default_iio_maps : pdata->adc_pdata;
 175
 176        ret = iio_map_array_register(indio_dev, map);
 177        if (ret) {
 178                dev_err(&indio_dev->dev, "iio map err: %d\n", ret);
 179                return ret;
 180        }
 181
 182        adc->map = map;
 183        return 0;
 184}
 185
 186static int lp8788_adc_probe(struct platform_device *pdev)
 187{
 188        struct lp8788 *lp = dev_get_drvdata(pdev->dev.parent);
 189        struct iio_dev *indio_dev;
 190        struct lp8788_adc *adc;
 191        int ret;
 192
 193        indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*adc));
 194        if (!indio_dev)
 195                return -ENOMEM;
 196
 197        adc = iio_priv(indio_dev);
 198        adc->lp = lp;
 199        platform_set_drvdata(pdev, indio_dev);
 200
 201        ret = lp8788_iio_map_register(indio_dev, lp->pdata, adc);
 202        if (ret)
 203                return ret;
 204
 205        mutex_init(&adc->lock);
 206
 207        indio_dev->name = pdev->name;
 208        indio_dev->modes = INDIO_DIRECT_MODE;
 209        indio_dev->info = &lp8788_adc_info;
 210        indio_dev->channels = lp8788_adc_channels;
 211        indio_dev->num_channels = ARRAY_SIZE(lp8788_adc_channels);
 212
 213        ret = iio_device_register(indio_dev);
 214        if (ret) {
 215                dev_err(&pdev->dev, "iio dev register err: %d\n", ret);
 216                goto err_iio_device;
 217        }
 218
 219        return 0;
 220
 221err_iio_device:
 222        iio_map_array_unregister(indio_dev);
 223        return ret;
 224}
 225
 226static int lp8788_adc_remove(struct platform_device *pdev)
 227{
 228        struct iio_dev *indio_dev = platform_get_drvdata(pdev);
 229
 230        iio_device_unregister(indio_dev);
 231        iio_map_array_unregister(indio_dev);
 232
 233        return 0;
 234}
 235
 236static struct platform_driver lp8788_adc_driver = {
 237        .probe = lp8788_adc_probe,
 238        .remove = lp8788_adc_remove,
 239        .driver = {
 240                .name = LP8788_DEV_ADC,
 241        },
 242};
 243module_platform_driver(lp8788_adc_driver);
 244
 245MODULE_DESCRIPTION("Texas Instruments LP8788 ADC Driver");
 246MODULE_AUTHOR("Milo Kim");
 247MODULE_LICENSE("GPL");
 248MODULE_ALIAS("platform:lp8788-adc");
 249
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.