linux/drivers/iio/adc/axp288_adc.c
<<
>>
Prefs
   1/*
   2 * axp288_adc.c - X-Powers AXP288 PMIC ADC Driver
   3 *
   4 * Copyright (C) 2014 Intel Corporation
   5 *
   6 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   7 *
   8 * This program is free software; you can redistribute it and/or modify
   9 * it under the terms of the GNU General Public License as published by
  10 * the Free Software Foundation; version 2 of the License.
  11 *
  12 * This program is distributed in the hope that it will be useful, but
  13 * WITHOUT ANY WARRANTY; without even the implied warranty of
  14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  15 * General Public License for more details.
  16 *
  17 */
  18
  19#include <linux/module.h>
  20#include <linux/kernel.h>
  21#include <linux/device.h>
  22#include <linux/regmap.h>
  23#include <linux/mfd/axp20x.h>
  24#include <linux/platform_device.h>
  25
  26#include <linux/iio/iio.h>
  27#include <linux/iio/machine.h>
  28#include <linux/iio/driver.h>
  29
  30#define AXP288_ADC_EN_MASK              0xF1
  31#define AXP288_ADC_TS_PIN_GPADC         0xF2
  32#define AXP288_ADC_TS_PIN_ON            0xF3
  33
  34enum axp288_adc_id {
  35        AXP288_ADC_TS,
  36        AXP288_ADC_PMIC,
  37        AXP288_ADC_GP,
  38        AXP288_ADC_BATT_CHRG_I,
  39        AXP288_ADC_BATT_DISCHRG_I,
  40        AXP288_ADC_BATT_V,
  41        AXP288_ADC_NR_CHAN,
  42};
  43
  44struct axp288_adc_info {
  45        int irq;
  46        struct regmap *regmap;
  47};
  48
  49static const struct iio_chan_spec axp288_adc_channels[] = {
  50        {
  51                .indexed = 1,
  52                .type = IIO_TEMP,
  53                .channel = 0,
  54                .address = AXP288_TS_ADC_H,
  55                .datasheet_name = "TS_PIN",
  56                .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
  57        }, {
  58                .indexed = 1,
  59                .type = IIO_TEMP,
  60                .channel = 1,
  61                .address = AXP288_PMIC_ADC_H,
  62                .datasheet_name = "PMIC_TEMP",
  63                .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
  64        }, {
  65                .indexed = 1,
  66                .type = IIO_TEMP,
  67                .channel = 2,
  68                .address = AXP288_GP_ADC_H,
  69                .datasheet_name = "GPADC",
  70                .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
  71        }, {
  72                .indexed = 1,
  73                .type = IIO_CURRENT,
  74                .channel = 3,
  75                .address = AXP20X_BATT_CHRG_I_H,
  76                .datasheet_name = "BATT_CHG_I",
  77                .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
  78        }, {
  79                .indexed = 1,
  80                .type = IIO_CURRENT,
  81                .channel = 4,
  82                .address = AXP20X_BATT_DISCHRG_I_H,
  83                .datasheet_name = "BATT_DISCHRG_I",
  84                .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
  85        }, {
  86                .indexed = 1,
  87                .type = IIO_VOLTAGE,
  88                .channel = 5,
  89                .address = AXP20X_BATT_V_H,
  90                .datasheet_name = "BATT_V",
  91                .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
  92        },
  93};
  94
  95#define AXP288_ADC_MAP(_adc_channel_label, _consumer_dev_name,  \
  96                _consumer_channel)                              \
  97        {                                                       \
  98                .adc_channel_label = _adc_channel_label,        \
  99                .consumer_dev_name = _consumer_dev_name,        \
 100                .consumer_channel = _consumer_channel,          \
 101        }
 102
 103/* for consumer drivers */
 104static struct iio_map axp288_adc_default_maps[] = {
 105        AXP288_ADC_MAP("TS_PIN", "axp288-batt", "axp288-batt-temp"),
 106        AXP288_ADC_MAP("PMIC_TEMP", "axp288-pmic", "axp288-pmic-temp"),
 107        AXP288_ADC_MAP("GPADC", "axp288-gpadc", "axp288-system-temp"),
 108        AXP288_ADC_MAP("BATT_CHG_I", "axp288-chrg", "axp288-chrg-curr"),
 109        AXP288_ADC_MAP("BATT_DISCHRG_I", "axp288-chrg", "axp288-chrg-d-curr"),
 110        AXP288_ADC_MAP("BATT_V", "axp288-batt", "axp288-batt-volt"),
 111        {},
 112};
 113
 114static int axp288_adc_read_channel(int *val, unsigned long address,
 115                                struct regmap *regmap)
 116{
 117        u8 buf[2];
 118
 119        if (regmap_bulk_read(regmap, address, buf, 2))
 120                return -EIO;
 121        *val = (buf[0] << 4) + ((buf[1] >> 4) & 0x0F);
 122
 123        return IIO_VAL_INT;
 124}
 125
 126static int axp288_adc_set_ts(struct regmap *regmap, unsigned int mode,
 127                                unsigned long address)
 128{
 129        /* channels other than GPADC do not need to switch TS pin */
 130        if (address != AXP288_GP_ADC_H)
 131                return 0;
 132
 133        return regmap_write(regmap, AXP288_ADC_TS_PIN_CTRL, mode);
 134}
 135
 136static int axp288_adc_read_raw(struct iio_dev *indio_dev,
 137                        struct iio_chan_spec const *chan,
 138                        int *val, int *val2, long mask)
 139{
 140        int ret;
 141        struct axp288_adc_info *info = iio_priv(indio_dev);
 142
 143        mutex_lock(&indio_dev->mlock);
 144        switch (mask) {
 145        case IIO_CHAN_INFO_RAW:
 146                if (axp288_adc_set_ts(info->regmap, AXP288_ADC_TS_PIN_GPADC,
 147                                        chan->address)) {
 148                        dev_err(&indio_dev->dev, "GPADC mode\n");
 149                        ret = -EINVAL;
 150                        break;
 151                }
 152                ret = axp288_adc_read_channel(val, chan->address, info->regmap);
 153                if (axp288_adc_set_ts(info->regmap, AXP288_ADC_TS_PIN_ON,
 154                                                chan->address))
 155                        dev_err(&indio_dev->dev, "TS pin restore\n");
 156                break;
 157        default:
 158                ret = -EINVAL;
 159        }
 160        mutex_unlock(&indio_dev->mlock);
 161
 162        return ret;
 163}
 164
 165static int axp288_adc_set_state(struct regmap *regmap)
 166{
 167        /* ADC should be always enabled for internal FG to function */
 168        if (regmap_write(regmap, AXP288_ADC_TS_PIN_CTRL, AXP288_ADC_TS_PIN_ON))
 169                return -EIO;
 170
 171        return regmap_write(regmap, AXP20X_ADC_EN1, AXP288_ADC_EN_MASK);
 172}
 173
 174static const struct iio_info axp288_adc_iio_info = {
 175        .read_raw = &axp288_adc_read_raw,
 176        .driver_module = THIS_MODULE,
 177};
 178
 179static int axp288_adc_probe(struct platform_device *pdev)
 180{
 181        int ret;
 182        struct axp288_adc_info *info;
 183        struct iio_dev *indio_dev;
 184        struct axp20x_dev *axp20x = dev_get_drvdata(pdev->dev.parent);
 185
 186        indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*info));
 187        if (!indio_dev)
 188                return -ENOMEM;
 189
 190        info = iio_priv(indio_dev);
 191        info->irq = platform_get_irq(pdev, 0);
 192        if (info->irq < 0) {
 193                dev_err(&pdev->dev, "no irq resource?\n");
 194                return info->irq;
 195        }
 196        platform_set_drvdata(pdev, indio_dev);
 197        info->regmap = axp20x->regmap;
 198        /*
 199         * Set ADC to enabled state at all time, including system suspend.
 200         * otherwise internal fuel gauge functionality may be affected.
 201         */
 202        ret = axp288_adc_set_state(axp20x->regmap);
 203        if (ret) {
 204                dev_err(&pdev->dev, "unable to enable ADC device\n");
 205                return ret;
 206        }
 207
 208        indio_dev->dev.parent = &pdev->dev;
 209        indio_dev->name = pdev->name;
 210        indio_dev->channels = axp288_adc_channels;
 211        indio_dev->num_channels = ARRAY_SIZE(axp288_adc_channels);
 212        indio_dev->info = &axp288_adc_iio_info;
 213        indio_dev->modes = INDIO_DIRECT_MODE;
 214        ret = iio_map_array_register(indio_dev, axp288_adc_default_maps);
 215        if (ret < 0)
 216                return ret;
 217
 218        ret = iio_device_register(indio_dev);
 219        if (ret < 0) {
 220                dev_err(&pdev->dev, "unable to register iio device\n");
 221                goto err_array_unregister;
 222        }
 223        return 0;
 224
 225err_array_unregister:
 226        iio_map_array_unregister(indio_dev);
 227
 228        return ret;
 229}
 230
 231static int axp288_adc_remove(struct platform_device *pdev)
 232{
 233        struct iio_dev *indio_dev = platform_get_drvdata(pdev);
 234
 235        iio_device_unregister(indio_dev);
 236        iio_map_array_unregister(indio_dev);
 237
 238        return 0;
 239}
 240
 241static const struct platform_device_id axp288_adc_id_table[] = {
 242        { .name = "axp288_adc" },
 243        {},
 244};
 245
 246static struct platform_driver axp288_adc_driver = {
 247        .probe = axp288_adc_probe,
 248        .remove = axp288_adc_remove,
 249        .id_table = axp288_adc_id_table,
 250        .driver = {
 251                .name = "axp288_adc",
 252        },
 253};
 254
 255MODULE_DEVICE_TABLE(platform, axp288_adc_id_table);
 256
 257module_platform_driver(axp288_adc_driver);
 258
 259MODULE_AUTHOR("Jacob Pan <jacob.jun.pan@linux.intel.com>");
 260MODULE_DESCRIPTION("X-Powers AXP288 ADC Driver");
 261MODULE_LICENSE("GPL");
 262