linux/drivers/iio/adc/axp288_adc.c
Greg Kroah-Hartman 61e331202f Second set of new driver, functionality and cleanups for IIO in the 4.2 cycle.
Core functionality
 * i and q modifiers from quadrature channels.
 * IIO_CHAN_INFO_OVERSAMPLING_RATIO added.
 * High pass filter attributes added to mirror the existing low pass filter
   ones.
 
 Core cleanups
 * Make IIO tools building more cross compiler friendly.
 * Substantial rework of the function __iio_update_buffers to greatly simplify
   a hideously evolved function.
 
 New drivers and support
 * ACPI0008 ambient light sensor driver. This one has been around a long time to
   will be good to finally get it into mainline.
 * Berlin SOC ADC support.
 * BMC150 magnetometer.  The accelerometer in the same package has been supported
   for quite some time, so good to have this half as well.
 * m62332 DAC driver
 * MEMSIC MMC35420 magnetometer.
 * ROHM BH1710 and similar ambient light sensors.
 * Sensortek STK3310 light sensor.
 * Sensortek STK8312 accelerometer.
 * Sensortek STK8BA50 accelerometer.
 * ti-adc128s052 gains support form the adc122s021 2 channel ADC.
 
 Driver cleanups and functionality.
 * Allow various drivers to compile with !GPIOLIB if COMPILE_TEST enabled.
 * bmc150 - decouple trigger from buffer to allow other triggers to be used.
 * bmg160 - decouple trigger from buffer to allow other triggers to be used.
   Fix a trivial unused field.
 * Constify a load of platform_device_id structures.
 * inv_mpu6050 - device tree bindings.
 * hid-sensors - fix a memory leak during probe if certain errors occur.
 * ltr501 - illuminance channel derived (in an non obvious fashion) from the
   intensity channels.
 * ltr501 - fix a boundary check on the proximity threshold.
 * mlx90614 - drop a pointless return.
 * mma8452 - Debugfs register access and fix a bug that had no effect (by
   coincidence)
 * ti_am335x_adc - add device tree bindings for sample-delay, open-delay and
   averaging.  The ideal settings for these tend to be board design specific.
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v2
 
 iQIcBAABCAAGBQJVYfYpAAoJEFSFNJnE9BaI1ScQAIJ2jsFZdf8fcVnWeq0bYx9I
 VUPfz/bJ/kLQRGm/LDgpMPc5o1mOE+rupwFpp/iQf15vVUN86+CRLt0qd5I/cAEg
 I3qbaieS1H9Qyd2dLTgAcZAh6tH7ZvFJm/hB6T5xQAYFGY2IMq/n3qA4//W37tUb
 2bKTRb67LWbGivOvwbxdSpEkBLtVcUw3UNn9nfqjB8BEAHIesh88gJkVKAAuRYqk
 Tm8AzQ7EGsosz2R7mIvukSBwXBcvRyxyOxCdLBPIWSESeLwMiiat0zCfv3MxrYiD
 FVpdlywoReIjDG6z9ALOm4VMtRF2m2VrjPHclQ3kYgYSgyf0fRmoiyGowv7hkeya
 Z+p9ltOZ8qdis+yH1ci9Ch695HURa1m0seirX4exqiv0Crx8UF+iNIvs9Ai84Rv8
 NNVlscoeEyijUaqoBb1YvG/Fryh2IEiGXTkF4Eld+EhW8AKkFFNIqR+Gwvs1YegT
 02A8kHxD0GyMYJo1uEwd+TnKwCBglwsie8omkxOXqsY860DRtBr7jOxyb/RzkSVi
 jGtq1Y4nxVv7q3nkn+vQDRNgAQTbH1EJfrDilpIxIWK+9onNmKMnhKnSTVNAdld/
 Hhn9g/MHptQtmA+DwMcJ3Aqn0xMUdgoE9GIkMGoKSZku9H0DhWHLdxTa2lxdJFUo
 OiWVvP0eJuvu0E0h4eA2
 =uX2k
 -----END PGP SIGNATURE-----

Merge tag 'iio-for-v4.2b' of git://git.kernel.org/pub/scm/linux/kernel/git/jic23/iio into staging-next

Jonathan writes:

Second set of new driver, functionality and cleanups for IIO in the 4.2 cycle.

Core functionality
* i and q modifiers from quadrature channels.
* IIO_CHAN_INFO_OVERSAMPLING_RATIO added.
* High pass filter attributes added to mirror the existing low pass filter
  ones.

Core cleanups
* Make IIO tools building more cross compiler friendly.
* Substantial rework of the function __iio_update_buffers to greatly simplify
  a hideously evolved function.

New drivers and support
* ACPI0008 ambient light sensor driver. This one has been around a long time to
  will be good to finally get it into mainline.
* Berlin SOC ADC support.
* BMC150 magnetometer.  The accelerometer in the same package has been supported
  for quite some time, so good to have this half as well.
* m62332 DAC driver
* MEMSIC MMC35420 magnetometer.
* ROHM BH1710 and similar ambient light sensors.
* Sensortek STK3310 light sensor.
* Sensortek STK8312 accelerometer.
* Sensortek STK8BA50 accelerometer.
* ti-adc128s052 gains support form the adc122s021 2 channel ADC.

Driver cleanups and functionality.
* Allow various drivers to compile with !GPIOLIB if COMPILE_TEST enabled.
* bmc150 - decouple trigger from buffer to allow other triggers to be used.
* bmg160 - decouple trigger from buffer to allow other triggers to be used.
  Fix a trivial unused field.
* Constify a load of platform_device_id structures.
* inv_mpu6050 - device tree bindings.
* hid-sensors - fix a memory leak during probe if certain errors occur.
* ltr501 - illuminance channel derived (in an non obvious fashion) from the
  intensity channels.
* ltr501 - fix a boundary check on the proximity threshold.
* mlx90614 - drop a pointless return.
* mma8452 - Debugfs register access and fix a bug that had no effect (by
  coincidence)
* ti_am335x_adc - add device tree bindings for sample-delay, open-delay and
  averaging.  The ideal settings for these tend to be board design specific.
2015-05-24 11:45:21 -07:00

262 lines
6.6 KiB
C

/*
* axp288_adc.c - X-Powers AXP288 PMIC ADC Driver
*
* Copyright (C) 2014 Intel Corporation
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/device.h>
#include <linux/regmap.h>
#include <linux/mfd/axp20x.h>
#include <linux/platform_device.h>
#include <linux/iio/iio.h>
#include <linux/iio/machine.h>
#include <linux/iio/driver.h>
#define AXP288_ADC_EN_MASK 0xF1
#define AXP288_ADC_TS_PIN_GPADC 0xF2
#define AXP288_ADC_TS_PIN_ON 0xF3
enum axp288_adc_id {
AXP288_ADC_TS,
AXP288_ADC_PMIC,
AXP288_ADC_GP,
AXP288_ADC_BATT_CHRG_I,
AXP288_ADC_BATT_DISCHRG_I,
AXP288_ADC_BATT_V,
AXP288_ADC_NR_CHAN,
};
struct axp288_adc_info {
int irq;
struct regmap *regmap;
};
static const struct iio_chan_spec const axp288_adc_channels[] = {
{
.indexed = 1,
.type = IIO_TEMP,
.channel = 0,
.address = AXP288_TS_ADC_H,
.datasheet_name = "TS_PIN",
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
}, {
.indexed = 1,
.type = IIO_TEMP,
.channel = 1,
.address = AXP288_PMIC_ADC_H,
.datasheet_name = "PMIC_TEMP",
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
}, {
.indexed = 1,
.type = IIO_TEMP,
.channel = 2,
.address = AXP288_GP_ADC_H,
.datasheet_name = "GPADC",
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
}, {
.indexed = 1,
.type = IIO_CURRENT,
.channel = 3,
.address = AXP20X_BATT_CHRG_I_H,
.datasheet_name = "BATT_CHG_I",
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
}, {
.indexed = 1,
.type = IIO_CURRENT,
.channel = 4,
.address = AXP20X_BATT_DISCHRG_I_H,
.datasheet_name = "BATT_DISCHRG_I",
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
}, {
.indexed = 1,
.type = IIO_VOLTAGE,
.channel = 5,
.address = AXP20X_BATT_V_H,
.datasheet_name = "BATT_V",
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
},
};
#define AXP288_ADC_MAP(_adc_channel_label, _consumer_dev_name, \
_consumer_channel) \
{ \
.adc_channel_label = _adc_channel_label, \
.consumer_dev_name = _consumer_dev_name, \
.consumer_channel = _consumer_channel, \
}
/* for consumer drivers */
static struct iio_map axp288_adc_default_maps[] = {
AXP288_ADC_MAP("TS_PIN", "axp288-batt", "axp288-batt-temp"),
AXP288_ADC_MAP("PMIC_TEMP", "axp288-pmic", "axp288-pmic-temp"),
AXP288_ADC_MAP("GPADC", "axp288-gpadc", "axp288-system-temp"),
AXP288_ADC_MAP("BATT_CHG_I", "axp288-chrg", "axp288-chrg-curr"),
AXP288_ADC_MAP("BATT_DISCHRG_I", "axp288-chrg", "axp288-chrg-d-curr"),
AXP288_ADC_MAP("BATT_V", "axp288-batt", "axp288-batt-volt"),
{},
};
static int axp288_adc_read_channel(int *val, unsigned long address,
struct regmap *regmap)
{
u8 buf[2];
if (regmap_bulk_read(regmap, address, buf, 2))
return -EIO;
*val = (buf[0] << 4) + ((buf[1] >> 4) & 0x0F);
return IIO_VAL_INT;
}
static int axp288_adc_set_ts(struct regmap *regmap, unsigned int mode,
unsigned long address)
{
/* channels other than GPADC do not need to switch TS pin */
if (address != AXP288_GP_ADC_H)
return 0;
return regmap_write(regmap, AXP288_ADC_TS_PIN_CTRL, mode);
}
static int axp288_adc_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int *val, int *val2, long mask)
{
int ret;
struct axp288_adc_info *info = iio_priv(indio_dev);
mutex_lock(&indio_dev->mlock);
switch (mask) {
case IIO_CHAN_INFO_RAW:
if (axp288_adc_set_ts(info->regmap, AXP288_ADC_TS_PIN_GPADC,
chan->address)) {
dev_err(&indio_dev->dev, "GPADC mode\n");
ret = -EINVAL;
break;
}
ret = axp288_adc_read_channel(val, chan->address, info->regmap);
if (axp288_adc_set_ts(info->regmap, AXP288_ADC_TS_PIN_ON,
chan->address))
dev_err(&indio_dev->dev, "TS pin restore\n");
break;
default:
ret = -EINVAL;
}
mutex_unlock(&indio_dev->mlock);
return ret;
}
static int axp288_adc_set_state(struct regmap *regmap)
{
/* ADC should be always enabled for internal FG to function */
if (regmap_write(regmap, AXP288_ADC_TS_PIN_CTRL, AXP288_ADC_TS_PIN_ON))
return -EIO;
return regmap_write(regmap, AXP20X_ADC_EN1, AXP288_ADC_EN_MASK);
}
static const struct iio_info axp288_adc_iio_info = {
.read_raw = &axp288_adc_read_raw,
.driver_module = THIS_MODULE,
};
static int axp288_adc_probe(struct platform_device *pdev)
{
int ret;
struct axp288_adc_info *info;
struct iio_dev *indio_dev;
struct axp20x_dev *axp20x = dev_get_drvdata(pdev->dev.parent);
indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*info));
if (!indio_dev)
return -ENOMEM;
info = iio_priv(indio_dev);
info->irq = platform_get_irq(pdev, 0);
if (info->irq < 0) {
dev_err(&pdev->dev, "no irq resource?\n");
return info->irq;
}
platform_set_drvdata(pdev, indio_dev);
info->regmap = axp20x->regmap;
/*
* Set ADC to enabled state at all time, including system suspend.
* otherwise internal fuel gauge functionality may be affected.
*/
ret = axp288_adc_set_state(axp20x->regmap);
if (ret) {
dev_err(&pdev->dev, "unable to enable ADC device\n");
return ret;
}
indio_dev->dev.parent = &pdev->dev;
indio_dev->name = pdev->name;
indio_dev->channels = axp288_adc_channels;
indio_dev->num_channels = ARRAY_SIZE(axp288_adc_channels);
indio_dev->info = &axp288_adc_iio_info;
indio_dev->modes = INDIO_DIRECT_MODE;
ret = iio_map_array_register(indio_dev, axp288_adc_default_maps);
if (ret < 0)
return ret;
ret = iio_device_register(indio_dev);
if (ret < 0) {
dev_err(&pdev->dev, "unable to register iio device\n");
goto err_array_unregister;
}
return 0;
err_array_unregister:
iio_map_array_unregister(indio_dev);
return ret;
}
static int axp288_adc_remove(struct platform_device *pdev)
{
struct iio_dev *indio_dev = platform_get_drvdata(pdev);
iio_device_unregister(indio_dev);
iio_map_array_unregister(indio_dev);
return 0;
}
static const struct platform_device_id axp288_adc_id_table[] = {
{ .name = "axp288_adc" },
{},
};
static struct platform_driver axp288_adc_driver = {
.probe = axp288_adc_probe,
.remove = axp288_adc_remove,
.id_table = axp288_adc_id_table,
.driver = {
.name = "axp288_adc",
},
};
MODULE_DEVICE_TABLE(platform, axp288_adc_id_table);
module_platform_driver(axp288_adc_driver);
MODULE_AUTHOR("Jacob Pan <jacob.jun.pan@linux.intel.com>");
MODULE_DESCRIPTION("X-Powers AXP288 ADC Driver");
MODULE_LICENSE("GPL");