root/drivers/iio/adc/axp288_adc.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. axp288_adc_read_channel
  2. axp288_adc_set_ts
  3. axp288_adc_read_raw
  4. axp288_adc_initialize
  5. axp288_adc_probe
  6. axp288_adc_remove

   1 // SPDX-License-Identifier: GPL-2.0-only
   2 /*
   3  * axp288_adc.c - X-Powers AXP288 PMIC ADC Driver
   4  *
   5  * Copyright (C) 2014 Intel Corporation
   6  *
   7  * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   8  */
   9 
  10 #include <linux/dmi.h>
  11 #include <linux/module.h>
  12 #include <linux/kernel.h>
  13 #include <linux/device.h>
  14 #include <linux/regmap.h>
  15 #include <linux/mfd/axp20x.h>
  16 #include <linux/platform_device.h>
  17 
  18 #include <linux/iio/iio.h>
  19 #include <linux/iio/machine.h>
  20 #include <linux/iio/driver.h>
  21 
  22 /*
  23  * This mask enables all ADCs except for the battery temp-sensor (TS), that is
  24  * left as-is to avoid breaking charging on devices without a temp-sensor.
  25  */
  26 #define AXP288_ADC_EN_MASK                              0xF0
  27 #define AXP288_ADC_TS_ENABLE                            0x01
  28 
  29 #define AXP288_ADC_TS_BIAS_MASK                         GENMASK(5, 4)
  30 #define AXP288_ADC_TS_BIAS_20UA                         (0 << 4)
  31 #define AXP288_ADC_TS_BIAS_40UA                         (1 << 4)
  32 #define AXP288_ADC_TS_BIAS_60UA                         (2 << 4)
  33 #define AXP288_ADC_TS_BIAS_80UA                         (3 << 4)
  34 #define AXP288_ADC_TS_CURRENT_ON_OFF_MASK               GENMASK(1, 0)
  35 #define AXP288_ADC_TS_CURRENT_OFF                       (0 << 0)
  36 #define AXP288_ADC_TS_CURRENT_ON_WHEN_CHARGING          (1 << 0)
  37 #define AXP288_ADC_TS_CURRENT_ON_ONDEMAND               (2 << 0)
  38 #define AXP288_ADC_TS_CURRENT_ON                        (3 << 0)
  39 
  40 enum axp288_adc_id {
  41         AXP288_ADC_TS,
  42         AXP288_ADC_PMIC,
  43         AXP288_ADC_GP,
  44         AXP288_ADC_BATT_CHRG_I,
  45         AXP288_ADC_BATT_DISCHRG_I,
  46         AXP288_ADC_BATT_V,
  47         AXP288_ADC_NR_CHAN,
  48 };
  49 
  50 struct axp288_adc_info {
  51         int irq;
  52         struct regmap *regmap;
  53         bool ts_enabled;
  54 };
  55 
  56 static const struct iio_chan_spec axp288_adc_channels[] = {
  57         {
  58                 .indexed = 1,
  59                 .type = IIO_TEMP,
  60                 .channel = 0,
  61                 .address = AXP288_TS_ADC_H,
  62                 .datasheet_name = "TS_PIN",
  63                 .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
  64         }, {
  65                 .indexed = 1,
  66                 .type = IIO_TEMP,
  67                 .channel = 1,
  68                 .address = AXP288_PMIC_ADC_H,
  69                 .datasheet_name = "PMIC_TEMP",
  70                 .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
  71         }, {
  72                 .indexed = 1,
  73                 .type = IIO_TEMP,
  74                 .channel = 2,
  75                 .address = AXP288_GP_ADC_H,
  76                 .datasheet_name = "GPADC",
  77                 .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
  78         }, {
  79                 .indexed = 1,
  80                 .type = IIO_CURRENT,
  81                 .channel = 3,
  82                 .address = AXP20X_BATT_CHRG_I_H,
  83                 .datasheet_name = "BATT_CHG_I",
  84                 .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
  85         }, {
  86                 .indexed = 1,
  87                 .type = IIO_CURRENT,
  88                 .channel = 4,
  89                 .address = AXP20X_BATT_DISCHRG_I_H,
  90                 .datasheet_name = "BATT_DISCHRG_I",
  91                 .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
  92         }, {
  93                 .indexed = 1,
  94                 .type = IIO_VOLTAGE,
  95                 .channel = 5,
  96                 .address = AXP20X_BATT_V_H,
  97                 .datasheet_name = "BATT_V",
  98                 .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
  99         },
 100 };
 101 
 102 /* for consumer drivers */
 103 static struct iio_map axp288_adc_default_maps[] = {
 104         IIO_MAP("TS_PIN", "axp288-batt", "axp288-batt-temp"),
 105         IIO_MAP("PMIC_TEMP", "axp288-pmic", "axp288-pmic-temp"),
 106         IIO_MAP("GPADC", "axp288-gpadc", "axp288-system-temp"),
 107         IIO_MAP("BATT_CHG_I", "axp288-chrg", "axp288-chrg-curr"),
 108         IIO_MAP("BATT_DISCHRG_I", "axp288-chrg", "axp288-chrg-d-curr"),
 109         IIO_MAP("BATT_V", "axp288-batt", "axp288-batt-volt"),
 110         {},
 111 };
 112 
 113 static int axp288_adc_read_channel(int *val, unsigned long address,
 114                                 struct regmap *regmap)
 115 {
 116         u8 buf[2];
 117 
 118         if (regmap_bulk_read(regmap, address, buf, 2))
 119                 return -EIO;
 120         *val = (buf[0] << 4) + ((buf[1] >> 4) & 0x0F);
 121 
 122         return IIO_VAL_INT;
 123 }
 124 
 125 /*
 126  * The current-source used for the battery temp-sensor (TS) is shared
 127  * with the GPADC. For proper fuel-gauge and charger operation the TS
 128  * current-source needs to be permanently on. But to read the GPADC we
 129  * need to temporary switch the TS current-source to ondemand, so that
 130  * the GPADC can use it, otherwise we will always read an all 0 value.
 131  */
 132 static int axp288_adc_set_ts(struct axp288_adc_info *info,
 133                              unsigned int mode, unsigned long address)
 134 {
 135         int ret;
 136 
 137         /* No need to switch the current-source if the TS pin is disabled */
 138         if (!info->ts_enabled)
 139                 return 0;
 140 
 141         /* Channels other than GPADC do not need the current source */
 142         if (address != AXP288_GP_ADC_H)
 143                 return 0;
 144 
 145         ret = regmap_update_bits(info->regmap, AXP288_ADC_TS_PIN_CTRL,
 146                                  AXP288_ADC_TS_CURRENT_ON_OFF_MASK, mode);
 147         if (ret)
 148                 return ret;
 149 
 150         /* When switching to the GPADC pin give things some time to settle */
 151         if (mode == AXP288_ADC_TS_CURRENT_ON_ONDEMAND)
 152                 usleep_range(6000, 10000);
 153 
 154         return 0;
 155 }
 156 
 157 static int axp288_adc_read_raw(struct iio_dev *indio_dev,
 158                         struct iio_chan_spec const *chan,
 159                         int *val, int *val2, long mask)
 160 {
 161         int ret;
 162         struct axp288_adc_info *info = iio_priv(indio_dev);
 163 
 164         mutex_lock(&indio_dev->mlock);
 165         switch (mask) {
 166         case IIO_CHAN_INFO_RAW:
 167                 if (axp288_adc_set_ts(info, AXP288_ADC_TS_CURRENT_ON_ONDEMAND,
 168                                         chan->address)) {
 169                         dev_err(&indio_dev->dev, "GPADC mode\n");
 170                         ret = -EINVAL;
 171                         break;
 172                 }
 173                 ret = axp288_adc_read_channel(val, chan->address, info->regmap);
 174                 if (axp288_adc_set_ts(info, AXP288_ADC_TS_CURRENT_ON,
 175                                                 chan->address))
 176                         dev_err(&indio_dev->dev, "TS pin restore\n");
 177                 break;
 178         default:
 179                 ret = -EINVAL;
 180         }
 181         mutex_unlock(&indio_dev->mlock);
 182 
 183         return ret;
 184 }
 185 
 186 /*
 187  * We rely on the machine's firmware to correctly setup the TS pin bias current
 188  * at boot. This lists systems with broken fw where we need to set it ourselves.
 189  */
 190 static const struct dmi_system_id axp288_adc_ts_bias_override[] = {
 191         {
 192                 /* Lenovo Ideapad 100S (11 inch) */
 193                 .matches = {
 194                   DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
 195                   DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo ideapad 100S-11IBY"),
 196                 },
 197                 .driver_data = (void *)(uintptr_t)AXP288_ADC_TS_BIAS_80UA,
 198         },
 199         {}
 200 };
 201 
 202 static int axp288_adc_initialize(struct axp288_adc_info *info)
 203 {
 204         const struct dmi_system_id *bias_override;
 205         int ret, adc_enable_val;
 206 
 207         bias_override = dmi_first_match(axp288_adc_ts_bias_override);
 208         if (bias_override) {
 209                 ret = regmap_update_bits(info->regmap, AXP288_ADC_TS_PIN_CTRL,
 210                                          AXP288_ADC_TS_BIAS_MASK,
 211                                          (uintptr_t)bias_override->driver_data);
 212                 if (ret)
 213                         return ret;
 214         }
 215 
 216         /*
 217          * Determine if the TS pin is enabled and set the TS current-source
 218          * accordingly.
 219          */
 220         ret = regmap_read(info->regmap, AXP20X_ADC_EN1, &adc_enable_val);
 221         if (ret)
 222                 return ret;
 223 
 224         if (adc_enable_val & AXP288_ADC_TS_ENABLE) {
 225                 info->ts_enabled = true;
 226                 ret = regmap_update_bits(info->regmap, AXP288_ADC_TS_PIN_CTRL,
 227                                          AXP288_ADC_TS_CURRENT_ON_OFF_MASK,
 228                                          AXP288_ADC_TS_CURRENT_ON);
 229         } else {
 230                 info->ts_enabled = false;
 231                 ret = regmap_update_bits(info->regmap, AXP288_ADC_TS_PIN_CTRL,
 232                                          AXP288_ADC_TS_CURRENT_ON_OFF_MASK,
 233                                          AXP288_ADC_TS_CURRENT_OFF);
 234         }
 235         if (ret)
 236                 return ret;
 237 
 238         /* Turn on the ADC for all channels except TS, leave TS as is */
 239         return regmap_update_bits(info->regmap, AXP20X_ADC_EN1,
 240                                   AXP288_ADC_EN_MASK, AXP288_ADC_EN_MASK);
 241 }
 242 
 243 static const struct iio_info axp288_adc_iio_info = {
 244         .read_raw = &axp288_adc_read_raw,
 245 };
 246 
 247 static int axp288_adc_probe(struct platform_device *pdev)
 248 {
 249         int ret;
 250         struct axp288_adc_info *info;
 251         struct iio_dev *indio_dev;
 252         struct axp20x_dev *axp20x = dev_get_drvdata(pdev->dev.parent);
 253 
 254         indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*info));
 255         if (!indio_dev)
 256                 return -ENOMEM;
 257 
 258         info = iio_priv(indio_dev);
 259         info->irq = platform_get_irq(pdev, 0);
 260         if (info->irq < 0)
 261                 return info->irq;
 262         platform_set_drvdata(pdev, indio_dev);
 263         info->regmap = axp20x->regmap;
 264         /*
 265          * Set ADC to enabled state at all time, including system suspend.
 266          * otherwise internal fuel gauge functionality may be affected.
 267          */
 268         ret = axp288_adc_initialize(info);
 269         if (ret) {
 270                 dev_err(&pdev->dev, "unable to enable ADC device\n");
 271                 return ret;
 272         }
 273 
 274         indio_dev->dev.parent = &pdev->dev;
 275         indio_dev->name = pdev->name;
 276         indio_dev->channels = axp288_adc_channels;
 277         indio_dev->num_channels = ARRAY_SIZE(axp288_adc_channels);
 278         indio_dev->info = &axp288_adc_iio_info;
 279         indio_dev->modes = INDIO_DIRECT_MODE;
 280         ret = iio_map_array_register(indio_dev, axp288_adc_default_maps);
 281         if (ret < 0)
 282                 return ret;
 283 
 284         ret = iio_device_register(indio_dev);
 285         if (ret < 0) {
 286                 dev_err(&pdev->dev, "unable to register iio device\n");
 287                 goto err_array_unregister;
 288         }
 289         return 0;
 290 
 291 err_array_unregister:
 292         iio_map_array_unregister(indio_dev);
 293 
 294         return ret;
 295 }
 296 
 297 static int axp288_adc_remove(struct platform_device *pdev)
 298 {
 299         struct iio_dev *indio_dev = platform_get_drvdata(pdev);
 300 
 301         iio_device_unregister(indio_dev);
 302         iio_map_array_unregister(indio_dev);
 303 
 304         return 0;
 305 }
 306 
 307 static const struct platform_device_id axp288_adc_id_table[] = {
 308         { .name = "axp288_adc" },
 309         {},
 310 };
 311 
 312 static struct platform_driver axp288_adc_driver = {
 313         .probe = axp288_adc_probe,
 314         .remove = axp288_adc_remove,
 315         .id_table = axp288_adc_id_table,
 316         .driver = {
 317                 .name = "axp288_adc",
 318         },
 319 };
 320 
 321 MODULE_DEVICE_TABLE(platform, axp288_adc_id_table);
 322 
 323 module_platform_driver(axp288_adc_driver);
 324 
 325 MODULE_AUTHOR("Jacob Pan <jacob.jun.pan@linux.intel.com>");
 326 MODULE_DESCRIPTION("X-Powers AXP288 ADC Driver");
 327 MODULE_LICENSE("GPL");

/* [<][>][^][v][top][bottom][index][help] */