linux/drivers/iio/adc/intel_mrfld_adc.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * ADC driver for Basin Cove PMIC
   4 *
   5 * Copyright (C) 2012 Intel Corporation
   6 * Author: Bin Yang <bin.yang@intel.com>
   7 *
   8 * Rewritten for upstream by:
   9 *       Vincent Pelletier <plr.vincent@gmail.com>
  10 *       Andy Shevchenko <andriy.shevchenko@linux.intel.com>
  11 */
  12
  13#include <linux/bitops.h>
  14#include <linux/completion.h>
  15#include <linux/interrupt.h>
  16#include <linux/mfd/intel_soc_pmic.h>
  17#include <linux/mfd/intel_soc_pmic_mrfld.h>
  18#include <linux/module.h>
  19#include <linux/mutex.h>
  20#include <linux/platform_device.h>
  21#include <linux/regmap.h>
  22
  23#include <linux/iio/driver.h>
  24#include <linux/iio/iio.h>
  25#include <linux/iio/machine.h>
  26
  27#include <asm/unaligned.h>
  28
  29#define BCOVE_GPADCREQ                  0xDC
  30#define BCOVE_GPADCREQ_BUSY             BIT(0)
  31#define BCOVE_GPADCREQ_IRQEN            BIT(1)
  32
  33#define BCOVE_ADCIRQ_ALL (              \
  34        BCOVE_ADCIRQ_BATTEMP |          \
  35        BCOVE_ADCIRQ_SYSTEMP |          \
  36        BCOVE_ADCIRQ_BATTID |           \
  37        BCOVE_ADCIRQ_VIBATT |           \
  38        BCOVE_ADCIRQ_CCTICK)
  39
  40#define BCOVE_ADC_TIMEOUT               msecs_to_jiffies(1000)
  41
  42static const u8 mrfld_adc_requests[] = {
  43        BCOVE_ADCIRQ_VIBATT,
  44        BCOVE_ADCIRQ_BATTID,
  45        BCOVE_ADCIRQ_VIBATT,
  46        BCOVE_ADCIRQ_SYSTEMP,
  47        BCOVE_ADCIRQ_BATTEMP,
  48        BCOVE_ADCIRQ_BATTEMP,
  49        BCOVE_ADCIRQ_SYSTEMP,
  50        BCOVE_ADCIRQ_SYSTEMP,
  51        BCOVE_ADCIRQ_SYSTEMP,
  52};
  53
  54struct mrfld_adc {
  55        struct regmap *regmap;
  56        struct completion completion;
  57        /* Lock to protect the IPC transfers */
  58        struct mutex lock;
  59};
  60
  61static irqreturn_t mrfld_adc_thread_isr(int irq, void *data)
  62{
  63        struct iio_dev *indio_dev = data;
  64        struct mrfld_adc *adc = iio_priv(indio_dev);
  65
  66        complete(&adc->completion);
  67        return IRQ_HANDLED;
  68}
  69
  70static int mrfld_adc_single_conv(struct iio_dev *indio_dev,
  71                                 struct iio_chan_spec const *chan,
  72                                 int *result)
  73{
  74        struct mrfld_adc *adc = iio_priv(indio_dev);
  75        struct regmap *regmap = adc->regmap;
  76        unsigned int req;
  77        long timeout;
  78        u8 buf[2];
  79        int ret;
  80
  81        reinit_completion(&adc->completion);
  82
  83        regmap_update_bits(regmap, BCOVE_MADCIRQ, BCOVE_ADCIRQ_ALL, 0);
  84        regmap_update_bits(regmap, BCOVE_MIRQLVL1, BCOVE_LVL1_ADC, 0);
  85
  86        ret = regmap_read_poll_timeout(regmap, BCOVE_GPADCREQ, req,
  87                                       !(req & BCOVE_GPADCREQ_BUSY),
  88                                       2000, 1000000);
  89        if (ret)
  90                goto done;
  91
  92        req = mrfld_adc_requests[chan->channel];
  93        ret = regmap_write(regmap, BCOVE_GPADCREQ, BCOVE_GPADCREQ_IRQEN | req);
  94        if (ret)
  95                goto done;
  96
  97        timeout = wait_for_completion_interruptible_timeout(&adc->completion,
  98                                                            BCOVE_ADC_TIMEOUT);
  99        if (timeout < 0) {
 100                ret = timeout;
 101                goto done;
 102        }
 103        if (timeout == 0) {
 104                ret = -ETIMEDOUT;
 105                goto done;
 106        }
 107
 108        ret = regmap_bulk_read(regmap, chan->address, buf, 2);
 109        if (ret)
 110                goto done;
 111
 112        *result = get_unaligned_be16(buf);
 113        ret = IIO_VAL_INT;
 114
 115done:
 116        regmap_update_bits(regmap, BCOVE_MIRQLVL1, BCOVE_LVL1_ADC, 0xff);
 117        regmap_update_bits(regmap, BCOVE_MADCIRQ, BCOVE_ADCIRQ_ALL, 0xff);
 118
 119        return ret;
 120}
 121
 122static int mrfld_adc_read_raw(struct iio_dev *indio_dev,
 123                              struct iio_chan_spec const *chan,
 124                              int *val, int *val2, long mask)
 125{
 126        struct mrfld_adc *adc = iio_priv(indio_dev);
 127        int ret;
 128
 129        switch (mask) {
 130        case IIO_CHAN_INFO_RAW:
 131                mutex_lock(&adc->lock);
 132                ret = mrfld_adc_single_conv(indio_dev, chan, val);
 133                mutex_unlock(&adc->lock);
 134                return ret;
 135        default:
 136                return -EINVAL;
 137        }
 138}
 139
 140static const struct iio_info mrfld_adc_iio_info = {
 141        .read_raw = &mrfld_adc_read_raw,
 142};
 143
 144#define BCOVE_ADC_CHANNEL(_type, _channel, _datasheet_name, _address)   \
 145        {                                                               \
 146                .indexed = 1,                                           \
 147                .type = _type,                                          \
 148                .channel = _channel,                                    \
 149                .address = _address,                                    \
 150                .datasheet_name = _datasheet_name,                      \
 151                .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),           \
 152        }
 153
 154static const struct iio_chan_spec mrfld_adc_channels[] = {
 155        BCOVE_ADC_CHANNEL(IIO_VOLTAGE,    0, "CH0", 0xE9),
 156        BCOVE_ADC_CHANNEL(IIO_RESISTANCE, 1, "CH1", 0xEB),
 157        BCOVE_ADC_CHANNEL(IIO_CURRENT,    2, "CH2", 0xED),
 158        BCOVE_ADC_CHANNEL(IIO_TEMP,       3, "CH3", 0xCC),
 159        BCOVE_ADC_CHANNEL(IIO_TEMP,       4, "CH4", 0xC8),
 160        BCOVE_ADC_CHANNEL(IIO_TEMP,       5, "CH5", 0xCA),
 161        BCOVE_ADC_CHANNEL(IIO_TEMP,       6, "CH6", 0xC2),
 162        BCOVE_ADC_CHANNEL(IIO_TEMP,       7, "CH7", 0xC4),
 163        BCOVE_ADC_CHANNEL(IIO_TEMP,       8, "CH8", 0xC6),
 164};
 165
 166static struct iio_map iio_maps[] = {
 167        IIO_MAP("CH0", "bcove-battery", "VBATRSLT"),
 168        IIO_MAP("CH1", "bcove-battery", "BATTID"),
 169        IIO_MAP("CH2", "bcove-battery", "IBATRSLT"),
 170        IIO_MAP("CH3", "bcove-temp",    "PMICTEMP"),
 171        IIO_MAP("CH4", "bcove-temp",    "BATTEMP0"),
 172        IIO_MAP("CH5", "bcove-temp",    "BATTEMP1"),
 173        IIO_MAP("CH6", "bcove-temp",    "SYSTEMP0"),
 174        IIO_MAP("CH7", "bcove-temp",    "SYSTEMP1"),
 175        IIO_MAP("CH8", "bcove-temp",    "SYSTEMP2"),
 176        {}
 177};
 178
 179static int mrfld_adc_probe(struct platform_device *pdev)
 180{
 181        struct device *dev = &pdev->dev;
 182        struct intel_soc_pmic *pmic = dev_get_drvdata(dev->parent);
 183        struct iio_dev *indio_dev;
 184        struct mrfld_adc *adc;
 185        int irq;
 186        int ret;
 187
 188        indio_dev = devm_iio_device_alloc(dev, sizeof(struct mrfld_adc));
 189        if (!indio_dev)
 190                return -ENOMEM;
 191
 192        adc = iio_priv(indio_dev);
 193
 194        mutex_init(&adc->lock);
 195        init_completion(&adc->completion);
 196        adc->regmap = pmic->regmap;
 197
 198        irq = platform_get_irq(pdev, 0);
 199        if (irq < 0)
 200                return irq;
 201
 202        ret = devm_request_threaded_irq(dev, irq, NULL, mrfld_adc_thread_isr,
 203                                        IRQF_ONESHOT | IRQF_SHARED, pdev->name,
 204                                        indio_dev);
 205        if (ret)
 206                return ret;
 207
 208        platform_set_drvdata(pdev, indio_dev);
 209
 210        indio_dev->dev.parent = dev;
 211        indio_dev->name = pdev->name;
 212
 213        indio_dev->channels = mrfld_adc_channels;
 214        indio_dev->num_channels = ARRAY_SIZE(mrfld_adc_channels);
 215        indio_dev->info = &mrfld_adc_iio_info;
 216        indio_dev->modes = INDIO_DIRECT_MODE;
 217
 218        ret = iio_map_array_register(indio_dev, iio_maps);
 219        if (ret)
 220                return ret;
 221
 222        ret = devm_iio_device_register(dev, indio_dev);
 223        if (ret < 0)
 224                goto err_array_unregister;
 225
 226        return 0;
 227
 228err_array_unregister:
 229        iio_map_array_unregister(indio_dev);
 230        return ret;
 231}
 232
 233static int mrfld_adc_remove(struct platform_device *pdev)
 234{
 235        struct iio_dev *indio_dev = platform_get_drvdata(pdev);
 236
 237        iio_map_array_unregister(indio_dev);
 238
 239        return 0;
 240}
 241
 242static const struct platform_device_id mrfld_adc_id_table[] = {
 243        { .name = "mrfld_bcove_adc" },
 244        {}
 245};
 246MODULE_DEVICE_TABLE(platform, mrfld_adc_id_table);
 247
 248static struct platform_driver mrfld_adc_driver = {
 249        .driver = {
 250                .name = "mrfld_bcove_adc",
 251        },
 252        .probe = mrfld_adc_probe,
 253        .remove = mrfld_adc_remove,
 254        .id_table = mrfld_adc_id_table,
 255};
 256module_platform_driver(mrfld_adc_driver);
 257
 258MODULE_AUTHOR("Bin Yang <bin.yang@intel.com>");
 259MODULE_AUTHOR("Vincent Pelletier <plr.vincent@gmail.com>");
 260MODULE_AUTHOR("Andy Shevchenko <andriy.shevchenko@linux.intel.com>");
 261MODULE_DESCRIPTION("ADC driver for Basin Cove PMIC");
 262MODULE_LICENSE("GPL v2");
 263