Merge tag 'staging-4.20-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging
Pull staging/IIO driver updates from Greg KH: "Here is the big staging and IIO driver pull request for 4.20-rc1. There are lots of things here, we ended up adding more lines than removing, thanks to a large influx of Comedi National Instrument device support. Someday soon we need to get comedi out of staging... Other than the comedi drivers, the "big" things here are: - new iio drivers - delete dgnc driver (no one used it and no one had the hardware anymore) - vbox driver updates and fixes - erofs fixes - tons and tons of tiny checkpatch fixes for almost all staging drivers All of these have been in linux-next, with the last few happening a bit "late" due to them getting stuck on my laptop during travel to the Mantainers summit" * tag 'staging-4.20-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging: (690 commits) staging: gasket: Fix sparse "incorrect type in assignment" warnings. staging: gasket: remove debug logs for callback invocation staging: gasket: remove debug logs in page table mapping calls staging: rtl8188eu: core: Use sizeof(*p) instead of sizeof(struct P) for memory allocation staging: ks7010: Remove extra blank line staging: gasket: Remove extra blank line staging: media: davinci_vpfe: Fix spelling mistake in enum staging: speakup: Add a pair of braces staging: wlan-ng: Replace long int with long staging: MAINTAINERS: remove obsolete IPX staging directory staging: MAINTAINERS: remove NCP filesystem entry staging: rtl8188eu: cleanup comparsions to false staging: gasket: Update device virtual address comment staging: gasket: sysfs: fix attribute release comment staging: gasket: apex: fix sysfs_show staging: gasket: page_table: simplify gasket_components_to_dev_address staging: gasket: page_table: fix comment in components_to_dev_address staging: gasket: page table: fixup error path allocating coherent mem staging: gasket: page_table: rearrange gasket_page_table_entry staging: gasket: page_table: remove unnecessary PTE status set to free ...
This commit is contained in:
@@ -501,6 +501,16 @@ config MCP3422
|
||||
This driver can also be built as a module. If so, the module will be
|
||||
called mcp3422.
|
||||
|
||||
config MCP3911
|
||||
tristate "Microchip Technology MCP3911 driver"
|
||||
depends on SPI
|
||||
help
|
||||
Say yes here to build support for Microchip Technology's MCP3911
|
||||
analog to digital converter.
|
||||
|
||||
This driver can also be built as a module. If so, the module will be
|
||||
called mcp3911.
|
||||
|
||||
config MEDIATEK_MT6577_AUXADC
|
||||
tristate "MediaTek AUXADC driver"
|
||||
depends on ARCH_MEDIATEK || COMPILE_TEST
|
||||
@@ -596,6 +606,26 @@ config QCOM_SPMI_VADC
|
||||
To compile this driver as a module, choose M here: the module will
|
||||
be called qcom-spmi-vadc.
|
||||
|
||||
config QCOM_SPMI_ADC5
|
||||
tristate "Qualcomm Technologies Inc. SPMI PMIC5 ADC"
|
||||
depends on SPMI
|
||||
select REGMAP_SPMI
|
||||
select QCOM_VADC_COMMON
|
||||
help
|
||||
This is the IIO Voltage PMIC5 ADC driver for Qualcomm Technologies Inc.
|
||||
|
||||
The driver supports multiple channels read. The ADC is a 16-bit
|
||||
sigma-delta ADC. The hardware supports calibrated results for
|
||||
conversion requests and clients include reading voltage phone
|
||||
power, on board system thermistors connected to the PMIC ADC,
|
||||
PMIC die temperature, charger temperature, battery current, USB voltage
|
||||
input, voltage signals connected to supported PMIC GPIO inputs. The
|
||||
hardware supports internal pull-up for thermistors and can choose between
|
||||
a 100k, 30k and 400k pull up using the ADC channels.
|
||||
|
||||
To compile this driver as a module, choose M here: the module will
|
||||
be called qcom-spmi-adc5.
|
||||
|
||||
config RCAR_GYRO_ADC
|
||||
tristate "Renesas R-Car GyroADC driver"
|
||||
depends on ARCH_RCAR_GEN2 || COMPILE_TEST
|
||||
|
@@ -47,12 +47,14 @@ obj-$(CONFIG_MAX1363) += max1363.o
|
||||
obj-$(CONFIG_MAX9611) += max9611.o
|
||||
obj-$(CONFIG_MCP320X) += mcp320x.o
|
||||
obj-$(CONFIG_MCP3422) += mcp3422.o
|
||||
obj-$(CONFIG_MCP3911) += mcp3911.o
|
||||
obj-$(CONFIG_MEDIATEK_MT6577_AUXADC) += mt6577_auxadc.o
|
||||
obj-$(CONFIG_MEN_Z188_ADC) += men_z188_adc.o
|
||||
obj-$(CONFIG_MESON_SARADC) += meson_saradc.o
|
||||
obj-$(CONFIG_MXS_LRADC_ADC) += mxs-lradc-adc.o
|
||||
obj-$(CONFIG_NAU7802) += nau7802.o
|
||||
obj-$(CONFIG_PALMAS_GPADC) += palmas_gpadc.o
|
||||
obj-$(CONFIG_QCOM_SPMI_ADC5) += qcom-spmi-adc5.o
|
||||
obj-$(CONFIG_QCOM_SPMI_IADC) += qcom-spmi-iadc.o
|
||||
obj-$(CONFIG_QCOM_VADC_COMMON) += qcom-vadc-common.o
|
||||
obj-$(CONFIG_QCOM_SPMI_VADC) += qcom-spmi-vadc.o
|
||||
|
@@ -385,6 +385,6 @@ static struct spi_driver ad7298_driver = {
|
||||
};
|
||||
module_spi_driver(ad7298_driver);
|
||||
|
||||
MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
|
||||
MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>");
|
||||
MODULE_DESCRIPTION("Analog Devices AD7298 ADC");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
@@ -328,6 +328,6 @@ static struct spi_driver ad7476_driver = {
|
||||
};
|
||||
module_spi_driver(ad7476_driver);
|
||||
|
||||
MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
|
||||
MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>");
|
||||
MODULE_DESCRIPTION("Analog Devices AD7476 and similar 1-channel ADCs");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
@@ -822,6 +822,6 @@ static struct spi_driver ad7793_driver = {
|
||||
};
|
||||
module_spi_driver(ad7793_driver);
|
||||
|
||||
MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
|
||||
MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>");
|
||||
MODULE_DESCRIPTION("Analog Devices AD7793 and similar ADCs");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
@@ -362,6 +362,6 @@ static struct spi_driver ad7887_driver = {
|
||||
};
|
||||
module_spi_driver(ad7887_driver);
|
||||
|
||||
MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
|
||||
MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>");
|
||||
MODULE_DESCRIPTION("Analog Devices AD7887 ADC");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
@@ -363,7 +363,7 @@ static struct spi_driver ad7923_driver = {
|
||||
};
|
||||
module_spi_driver(ad7923_driver);
|
||||
|
||||
MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
|
||||
MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>");
|
||||
MODULE_AUTHOR("Patrick Vasseur <patrick.vasseur@c-s.fr>");
|
||||
MODULE_DESCRIPTION("Analog Devices AD7904/AD7914/AD7923/AD7924 ADC");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
@@ -892,6 +892,6 @@ static struct i2c_driver ad799x_driver = {
|
||||
};
|
||||
module_i2c_driver(ad799x_driver);
|
||||
|
||||
MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
|
||||
MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>");
|
||||
MODULE_DESCRIPTION("Analog Devices AD799x ADC");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
@@ -248,12 +248,14 @@ static irqreturn_t at91_adc_trigger_handler(int irq, void *p)
|
||||
struct iio_poll_func *pf = p;
|
||||
struct iio_dev *idev = pf->indio_dev;
|
||||
struct at91_adc_state *st = iio_priv(idev);
|
||||
struct iio_chan_spec const *chan;
|
||||
int i, j = 0;
|
||||
|
||||
for (i = 0; i < idev->masklength; i++) {
|
||||
if (!test_bit(i, idev->active_scan_mask))
|
||||
continue;
|
||||
st->buffer[j] = at91_adc_readl(st, AT91_ADC_CHAN(st, i));
|
||||
chan = idev->channels + i;
|
||||
st->buffer[j] = at91_adc_readl(st, AT91_ADC_CHAN(st, chan->channel));
|
||||
j++;
|
||||
}
|
||||
|
||||
@@ -279,6 +281,8 @@ static void handle_adc_eoc_trigger(int irq, struct iio_dev *idev)
|
||||
iio_trigger_poll(idev->trig);
|
||||
} else {
|
||||
st->last_value = at91_adc_readl(st, AT91_ADC_CHAN(st, st->chnb));
|
||||
/* Needed to ACK the DRDY interruption */
|
||||
at91_adc_readl(st, AT91_ADC_LCDR);
|
||||
st->done = true;
|
||||
wake_up_interruptible(&st->wq_data_avail);
|
||||
}
|
||||
|
@@ -1,13 +1,10 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Driver for an envelope detector using a DAC and a comparator
|
||||
*
|
||||
* Copyright (C) 2016 Axentia Technologies AB
|
||||
*
|
||||
* Author: Peter Rosin <peda@axentia.se>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
/*
|
||||
|
@@ -209,12 +209,14 @@ static int mx25_gcq_setup_cfgs(struct platform_device *pdev,
|
||||
ret = of_property_read_u32(child, "reg", ®);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to get reg property\n");
|
||||
of_node_put(child);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (reg >= MX25_NUM_CFGS) {
|
||||
dev_err(dev,
|
||||
"reg value is greater than the number of available configuration registers\n");
|
||||
of_node_put(child);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
@@ -228,6 +230,7 @@ static int mx25_gcq_setup_cfgs(struct platform_device *pdev,
|
||||
if (IS_ERR(priv->vref[refp])) {
|
||||
dev_err(dev, "Error, trying to use external voltage reference without a vref-%s regulator.",
|
||||
mx25_gcq_refp_names[refp]);
|
||||
of_node_put(child);
|
||||
return PTR_ERR(priv->vref[refp]);
|
||||
}
|
||||
priv->channel_vref_mv[reg] =
|
||||
@@ -240,6 +243,7 @@ static int mx25_gcq_setup_cfgs(struct platform_device *pdev,
|
||||
break;
|
||||
default:
|
||||
dev_err(dev, "Invalid positive reference %d\n", refp);
|
||||
of_node_put(child);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
@@ -254,10 +258,12 @@ static int mx25_gcq_setup_cfgs(struct platform_device *pdev,
|
||||
|
||||
if ((refp & MX25_ADCQ_CFG_REFP_MASK) != refp) {
|
||||
dev_err(dev, "Invalid fsl,adc-refp property value\n");
|
||||
of_node_put(child);
|
||||
return -EINVAL;
|
||||
}
|
||||
if ((refn & MX25_ADCQ_CFG_REFN_MASK) != refn) {
|
||||
dev_err(dev, "Invalid fsl,adc-refn property value\n");
|
||||
of_node_put(child);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
|
@@ -289,7 +289,7 @@ static int max9611_read_csa_voltage(struct max9611_dev *max9611,
|
||||
return ret;
|
||||
|
||||
if (*adc_raw > 0) {
|
||||
*csa_gain = gain_selectors[i];
|
||||
*csa_gain = (enum max9611_csa_gain)gain_selectors[i];
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
363
drivers/iio/adc/mcp3911.c
Normal file
363
drivers/iio/adc/mcp3911.c
Normal file
@@ -0,0 +1,363 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Driver for Microchip MCP3911, Two-channel Analog Front End
|
||||
*
|
||||
* Copyright (C) 2018 Marcus Folkesson <marcus.folkesson@gmail.com>
|
||||
* Copyright (C) 2018 Kent Gustavsson <kent@minoris.se>
|
||||
*/
|
||||
#include <linux/clk.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/spi/spi.h>
|
||||
|
||||
#define MCP3911_REG_CHANNEL0 0x00
|
||||
#define MCP3911_REG_CHANNEL1 0x03
|
||||
#define MCP3911_REG_MOD 0x06
|
||||
#define MCP3911_REG_PHASE 0x07
|
||||
#define MCP3911_REG_GAIN 0x09
|
||||
|
||||
#define MCP3911_REG_STATUSCOM 0x0a
|
||||
#define MCP3911_STATUSCOM_CH1_24WIDTH BIT(4)
|
||||
#define MCP3911_STATUSCOM_CH0_24WIDTH BIT(3)
|
||||
#define MCP3911_STATUSCOM_EN_OFFCAL BIT(2)
|
||||
#define MCP3911_STATUSCOM_EN_GAINCAL BIT(1)
|
||||
|
||||
#define MCP3911_REG_CONFIG 0x0c
|
||||
#define MCP3911_CONFIG_CLKEXT BIT(1)
|
||||
#define MCP3911_CONFIG_VREFEXT BIT(2)
|
||||
|
||||
#define MCP3911_REG_OFFCAL_CH0 0x0e
|
||||
#define MCP3911_REG_GAINCAL_CH0 0x11
|
||||
#define MCP3911_REG_OFFCAL_CH1 0x14
|
||||
#define MCP3911_REG_GAINCAL_CH1 0x17
|
||||
#define MCP3911_REG_VREFCAL 0x1a
|
||||
|
||||
#define MCP3911_CHANNEL(x) (MCP3911_REG_CHANNEL0 + x * 3)
|
||||
#define MCP3911_OFFCAL(x) (MCP3911_REG_OFFCAL_CH0 + x * 6)
|
||||
|
||||
/* Internal voltage reference in uV */
|
||||
#define MCP3911_INT_VREF_UV 1200000
|
||||
|
||||
#define MCP3911_REG_READ(reg, id) ((((reg) << 1) | ((id) << 5) | (1 << 0)) & 0xff)
|
||||
#define MCP3911_REG_WRITE(reg, id) ((((reg) << 1) | ((id) << 5) | (0 << 0)) & 0xff)
|
||||
|
||||
#define MCP3911_NUM_CHANNELS 2
|
||||
|
||||
struct mcp3911 {
|
||||
struct spi_device *spi;
|
||||
struct mutex lock;
|
||||
struct regulator *vref;
|
||||
struct clk *clki;
|
||||
u32 dev_addr;
|
||||
};
|
||||
|
||||
static int mcp3911_read(struct mcp3911 *adc, u8 reg, u32 *val, u8 len)
|
||||
{
|
||||
int ret;
|
||||
|
||||
reg = MCP3911_REG_READ(reg, adc->dev_addr);
|
||||
ret = spi_write_then_read(adc->spi, ®, 1, val, len);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
be32_to_cpus(val);
|
||||
*val >>= ((4 - len) * 8);
|
||||
dev_dbg(&adc->spi->dev, "reading 0x%x from register 0x%x\n", *val,
|
||||
reg >> 1);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mcp3911_write(struct mcp3911 *adc, u8 reg, u32 val, u8 len)
|
||||
{
|
||||
dev_dbg(&adc->spi->dev, "writing 0x%x to register 0x%x\n", val, reg);
|
||||
|
||||
val <<= (3 - len) * 8;
|
||||
cpu_to_be32s(&val);
|
||||
val |= MCP3911_REG_WRITE(reg, adc->dev_addr);
|
||||
|
||||
return spi_write(adc->spi, &val, len + 1);
|
||||
}
|
||||
|
||||
static int mcp3911_update(struct mcp3911 *adc, u8 reg, u32 mask,
|
||||
u32 val, u8 len)
|
||||
{
|
||||
u32 tmp;
|
||||
int ret;
|
||||
|
||||
ret = mcp3911_read(adc, reg, &tmp, len);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
val &= mask;
|
||||
val |= tmp & ~mask;
|
||||
return mcp3911_write(adc, reg, val, len);
|
||||
}
|
||||
|
||||
static int mcp3911_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *channel, int *val,
|
||||
int *val2, long mask)
|
||||
{
|
||||
struct mcp3911 *adc = iio_priv(indio_dev);
|
||||
int ret = -EINVAL;
|
||||
|
||||
mutex_lock(&adc->lock);
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
ret = mcp3911_read(adc,
|
||||
MCP3911_CHANNEL(channel->channel), val, 3);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
ret = IIO_VAL_INT;
|
||||
break;
|
||||
|
||||
case IIO_CHAN_INFO_OFFSET:
|
||||
ret = mcp3911_read(adc,
|
||||
MCP3911_OFFCAL(channel->channel), val, 3);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
ret = IIO_VAL_INT;
|
||||
break;
|
||||
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
if (adc->vref) {
|
||||
ret = regulator_get_voltage(adc->vref);
|
||||
if (ret < 0) {
|
||||
dev_err(indio_dev->dev.parent,
|
||||
"failed to get vref voltage: %d\n",
|
||||
ret);
|
||||
goto out;
|
||||
}
|
||||
|
||||
*val = ret / 1000;
|
||||
} else {
|
||||
*val = MCP3911_INT_VREF_UV;
|
||||
}
|
||||
|
||||
*val2 = 24;
|
||||
ret = IIO_VAL_FRACTIONAL_LOG2;
|
||||
break;
|
||||
}
|
||||
|
||||
out:
|
||||
mutex_unlock(&adc->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mcp3911_write_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *channel, int val,
|
||||
int val2, long mask)
|
||||
{
|
||||
struct mcp3911 *adc = iio_priv(indio_dev);
|
||||
int ret = -EINVAL;
|
||||
|
||||
mutex_lock(&adc->lock);
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_OFFSET:
|
||||
if (val2 != 0) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Write offset */
|
||||
ret = mcp3911_write(adc, MCP3911_OFFCAL(channel->channel), val,
|
||||
3);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
/* Enable offset*/
|
||||
ret = mcp3911_update(adc, MCP3911_REG_STATUSCOM,
|
||||
MCP3911_STATUSCOM_EN_OFFCAL,
|
||||
MCP3911_STATUSCOM_EN_OFFCAL, 2);
|
||||
break;
|
||||
}
|
||||
|
||||
out:
|
||||
mutex_unlock(&adc->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
#define MCP3911_CHAN(idx) { \
|
||||
.type = IIO_VOLTAGE, \
|
||||
.indexed = 1, \
|
||||
.channel = idx, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
|
||||
BIT(IIO_CHAN_INFO_OFFSET) | \
|
||||
BIT(IIO_CHAN_INFO_SCALE), \
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec mcp3911_channels[] = {
|
||||
MCP3911_CHAN(0),
|
||||
MCP3911_CHAN(1),
|
||||
};
|
||||
|
||||
static const struct iio_info mcp3911_info = {
|
||||
.read_raw = mcp3911_read_raw,
|
||||
.write_raw = mcp3911_write_raw,
|
||||
};
|
||||
|
||||
static int mcp3911_config(struct mcp3911 *adc, struct device_node *of_node)
|
||||
{
|
||||
u32 configreg;
|
||||
int ret;
|
||||
|
||||
of_property_read_u32(of_node, "device-addr", &adc->dev_addr);
|
||||
if (adc->dev_addr > 3) {
|
||||
dev_err(&adc->spi->dev,
|
||||
"invalid device address (%i). Must be in range 0-3.\n",
|
||||
adc->dev_addr);
|
||||
return -EINVAL;
|
||||
}
|
||||
dev_dbg(&adc->spi->dev, "use device address %i\n", adc->dev_addr);
|
||||
|
||||
ret = mcp3911_read(adc, MCP3911_REG_CONFIG, &configreg, 2);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (adc->vref) {
|
||||
dev_dbg(&adc->spi->dev, "use external voltage reference\n");
|
||||
configreg |= MCP3911_CONFIG_VREFEXT;
|
||||
} else {
|
||||
dev_dbg(&adc->spi->dev,
|
||||
"use internal voltage reference (1.2V)\n");
|
||||
configreg &= ~MCP3911_CONFIG_VREFEXT;
|
||||
}
|
||||
|
||||
if (adc->clki) {
|
||||
dev_dbg(&adc->spi->dev, "use external clock as clocksource\n");
|
||||
configreg |= MCP3911_CONFIG_CLKEXT;
|
||||
} else {
|
||||
dev_dbg(&adc->spi->dev,
|
||||
"use crystal oscillator as clocksource\n");
|
||||
configreg &= ~MCP3911_CONFIG_CLKEXT;
|
||||
}
|
||||
|
||||
return mcp3911_write(adc, MCP3911_REG_CONFIG, configreg, 2);
|
||||
}
|
||||
|
||||
static int mcp3911_probe(struct spi_device *spi)
|
||||
{
|
||||
struct iio_dev *indio_dev;
|
||||
struct mcp3911 *adc;
|
||||
int ret;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*adc));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
adc = iio_priv(indio_dev);
|
||||
adc->spi = spi;
|
||||
|
||||
adc->vref = devm_regulator_get_optional(&adc->spi->dev, "vref");
|
||||
if (IS_ERR(adc->vref)) {
|
||||
if (PTR_ERR(adc->vref) == -ENODEV) {
|
||||
adc->vref = NULL;
|
||||
} else {
|
||||
dev_err(&adc->spi->dev,
|
||||
"failed to get regulator (%ld)\n",
|
||||
PTR_ERR(adc->vref));
|
||||
return PTR_ERR(adc->vref);
|
||||
}
|
||||
|
||||
} else {
|
||||
ret = regulator_enable(adc->vref);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
adc->clki = devm_clk_get(&adc->spi->dev, NULL);
|
||||
if (IS_ERR(adc->clki)) {
|
||||
if (PTR_ERR(adc->clki) == -ENOENT) {
|
||||
adc->clki = NULL;
|
||||
} else {
|
||||
dev_err(&adc->spi->dev,
|
||||
"failed to get adc clk (%ld)\n",
|
||||
PTR_ERR(adc->clki));
|
||||
ret = PTR_ERR(adc->clki);
|
||||
goto reg_disable;
|
||||
}
|
||||
} else {
|
||||
ret = clk_prepare_enable(adc->clki);
|
||||
if (ret < 0) {
|
||||
dev_err(&adc->spi->dev,
|
||||
"Failed to enable clki: %d\n", ret);
|
||||
goto reg_disable;
|
||||
}
|
||||
}
|
||||
|
||||
ret = mcp3911_config(adc, spi->dev.of_node);
|
||||
if (ret)
|
||||
goto clk_disable;
|
||||
|
||||
indio_dev->dev.parent = &spi->dev;
|
||||
indio_dev->dev.of_node = spi->dev.of_node;
|
||||
indio_dev->name = spi_get_device_id(spi)->name;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->info = &mcp3911_info;
|
||||
spi_set_drvdata(spi, indio_dev);
|
||||
|
||||
indio_dev->channels = mcp3911_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(mcp3911_channels);
|
||||
|
||||
mutex_init(&adc->lock);
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret)
|
||||
goto clk_disable;
|
||||
|
||||
return ret;
|
||||
|
||||
clk_disable:
|
||||
clk_disable_unprepare(adc->clki);
|
||||
reg_disable:
|
||||
if (adc->vref)
|
||||
regulator_disable(adc->vref);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mcp3911_remove(struct spi_device *spi)
|
||||
{
|
||||
struct iio_dev *indio_dev = spi_get_drvdata(spi);
|
||||
struct mcp3911 *adc = iio_priv(indio_dev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
|
||||
clk_disable_unprepare(adc->clki);
|
||||
if (adc->vref)
|
||||
regulator_disable(adc->vref);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id mcp3911_dt_ids[] = {
|
||||
{ .compatible = "microchip,mcp3911" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, mcp3911_dt_ids);
|
||||
|
||||
static const struct spi_device_id mcp3911_id[] = {
|
||||
{ "mcp3911", 0 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, mcp3911_id);
|
||||
|
||||
static struct spi_driver mcp3911_driver = {
|
||||
.driver = {
|
||||
.name = "mcp3911",
|
||||
.of_match_table = mcp3911_dt_ids,
|
||||
},
|
||||
.probe = mcp3911_probe,
|
||||
.remove = mcp3911_remove,
|
||||
.id_table = mcp3911_id,
|
||||
};
|
||||
module_spi_driver(mcp3911_driver);
|
||||
|
||||
MODULE_AUTHOR("Marcus Folkesson <marcus.folkesson@gmail.com>");
|
||||
MODULE_AUTHOR("Kent Gustavsson <kent@minoris.se>");
|
||||
MODULE_DESCRIPTION("Microchip Technology MCP3911");
|
||||
MODULE_LICENSE("GPL v2");
|
@@ -148,7 +148,6 @@
|
||||
#define MESON_SAR_ADC_DELTA_10_TS_REVE1 BIT(26)
|
||||
#define MESON_SAR_ADC_DELTA_10_CHAN1_DELTA_VALUE_MASK GENMASK(25, 16)
|
||||
#define MESON_SAR_ADC_DELTA_10_TS_REVE0 BIT(15)
|
||||
#define MESON_SAR_ADC_DELTA_10_TS_C_SHIFT 11
|
||||
#define MESON_SAR_ADC_DELTA_10_TS_C_MASK GENMASK(14, 11)
|
||||
#define MESON_SAR_ADC_DELTA_10_TS_VBG_EN BIT(10)
|
||||
#define MESON_SAR_ADC_DELTA_10_CHAN0_DELTA_VALUE_MASK GENMASK(9, 0)
|
||||
@@ -173,6 +172,7 @@
|
||||
.type = IIO_VOLTAGE, \
|
||||
.indexed = 1, \
|
||||
.channel = _chan, \
|
||||
.address = _chan, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
|
||||
BIT(IIO_CHAN_INFO_AVERAGE_RAW), \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \
|
||||
@@ -235,7 +235,7 @@ struct meson_sar_adc_data {
|
||||
struct meson_sar_adc_priv {
|
||||
struct regmap *regmap;
|
||||
struct regulator *vref;
|
||||
const struct meson_sar_adc_data *data;
|
||||
const struct meson_sar_adc_param *param;
|
||||
struct clk *clkin;
|
||||
struct clk *core_clk;
|
||||
struct clk *adc_sel_clk;
|
||||
@@ -280,7 +280,7 @@ static int meson_sar_adc_calib_val(struct iio_dev *indio_dev, int val)
|
||||
/* use val_calib = scale * val_raw + offset calibration function */
|
||||
tmp = div_s64((s64)val * priv->calibscale, MILLION) + priv->calibbias;
|
||||
|
||||
return clamp(tmp, 0, (1 << priv->data->param->resolution) - 1);
|
||||
return clamp(tmp, 0, (1 << priv->param->resolution) - 1);
|
||||
}
|
||||
|
||||
static int meson_sar_adc_wait_busy_clear(struct iio_dev *indio_dev)
|
||||
@@ -324,15 +324,15 @@ static int meson_sar_adc_read_raw_sample(struct iio_dev *indio_dev,
|
||||
|
||||
regmap_read(priv->regmap, MESON_SAR_ADC_FIFO_RD, ®val);
|
||||
fifo_chan = FIELD_GET(MESON_SAR_ADC_FIFO_RD_CHAN_ID_MASK, regval);
|
||||
if (fifo_chan != chan->channel) {
|
||||
if (fifo_chan != chan->address) {
|
||||
dev_err(&indio_dev->dev,
|
||||
"ADC FIFO entry belongs to channel %d instead of %d\n",
|
||||
fifo_chan, chan->channel);
|
||||
"ADC FIFO entry belongs to channel %d instead of %lu\n",
|
||||
fifo_chan, chan->address);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
fifo_val = FIELD_GET(MESON_SAR_ADC_FIFO_RD_SAMPLE_VALUE_MASK, regval);
|
||||
fifo_val &= GENMASK(priv->data->param->resolution - 1, 0);
|
||||
fifo_val &= GENMASK(priv->param->resolution - 1, 0);
|
||||
*val = meson_sar_adc_calib_val(indio_dev, fifo_val);
|
||||
|
||||
return 0;
|
||||
@@ -344,16 +344,16 @@ static void meson_sar_adc_set_averaging(struct iio_dev *indio_dev,
|
||||
enum meson_sar_adc_num_samples samples)
|
||||
{
|
||||
struct meson_sar_adc_priv *priv = iio_priv(indio_dev);
|
||||
int val, channel = chan->channel;
|
||||
int val, address = chan->address;
|
||||
|
||||
val = samples << MESON_SAR_ADC_AVG_CNTL_NUM_SAMPLES_SHIFT(channel);
|
||||
val = samples << MESON_SAR_ADC_AVG_CNTL_NUM_SAMPLES_SHIFT(address);
|
||||
regmap_update_bits(priv->regmap, MESON_SAR_ADC_AVG_CNTL,
|
||||
MESON_SAR_ADC_AVG_CNTL_NUM_SAMPLES_MASK(channel),
|
||||
MESON_SAR_ADC_AVG_CNTL_NUM_SAMPLES_MASK(address),
|
||||
val);
|
||||
|
||||
val = mode << MESON_SAR_ADC_AVG_CNTL_AVG_MODE_SHIFT(channel);
|
||||
val = mode << MESON_SAR_ADC_AVG_CNTL_AVG_MODE_SHIFT(address);
|
||||
regmap_update_bits(priv->regmap, MESON_SAR_ADC_AVG_CNTL,
|
||||
MESON_SAR_ADC_AVG_CNTL_AVG_MODE_MASK(channel), val);
|
||||
MESON_SAR_ADC_AVG_CNTL_AVG_MODE_MASK(address), val);
|
||||
}
|
||||
|
||||
static void meson_sar_adc_enable_channel(struct iio_dev *indio_dev,
|
||||
@@ -373,23 +373,23 @@ static void meson_sar_adc_enable_channel(struct iio_dev *indio_dev,
|
||||
|
||||
/* map channel index 0 to the channel which we want to read */
|
||||
regval = FIELD_PREP(MESON_SAR_ADC_CHAN_LIST_ENTRY_MASK(0),
|
||||
chan->channel);
|
||||
chan->address);
|
||||
regmap_update_bits(priv->regmap, MESON_SAR_ADC_CHAN_LIST,
|
||||
MESON_SAR_ADC_CHAN_LIST_ENTRY_MASK(0), regval);
|
||||
|
||||
regval = FIELD_PREP(MESON_SAR_ADC_DETECT_IDLE_SW_DETECT_MUX_MASK,
|
||||
chan->channel);
|
||||
chan->address);
|
||||
regmap_update_bits(priv->regmap, MESON_SAR_ADC_DETECT_IDLE_SW,
|
||||
MESON_SAR_ADC_DETECT_IDLE_SW_DETECT_MUX_MASK,
|
||||
regval);
|
||||
|
||||
regval = FIELD_PREP(MESON_SAR_ADC_DETECT_IDLE_SW_IDLE_MUX_SEL_MASK,
|
||||
chan->channel);
|
||||
chan->address);
|
||||
regmap_update_bits(priv->regmap, MESON_SAR_ADC_DETECT_IDLE_SW,
|
||||
MESON_SAR_ADC_DETECT_IDLE_SW_IDLE_MUX_SEL_MASK,
|
||||
regval);
|
||||
|
||||
if (chan->channel == 6)
|
||||
if (chan->address == 6)
|
||||
regmap_update_bits(priv->regmap, MESON_SAR_ADC_DELTA_10,
|
||||
MESON_SAR_ADC_DELTA_10_TEMP_SEL, 0);
|
||||
}
|
||||
@@ -451,7 +451,7 @@ static int meson_sar_adc_lock(struct iio_dev *indio_dev)
|
||||
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
|
||||
if (priv->data->param->has_bl30_integration) {
|
||||
if (priv->param->has_bl30_integration) {
|
||||
/* prevent BL30 from using the SAR ADC while we are using it */
|
||||
regmap_update_bits(priv->regmap, MESON_SAR_ADC_DELAY,
|
||||
MESON_SAR_ADC_DELAY_KERNEL_BUSY,
|
||||
@@ -479,7 +479,7 @@ static void meson_sar_adc_unlock(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct meson_sar_adc_priv *priv = iio_priv(indio_dev);
|
||||
|
||||
if (priv->data->param->has_bl30_integration)
|
||||
if (priv->param->has_bl30_integration)
|
||||
/* allow BL30 to use the SAR ADC again */
|
||||
regmap_update_bits(priv->regmap, MESON_SAR_ADC_DELAY,
|
||||
MESON_SAR_ADC_DELAY_KERNEL_BUSY, 0);
|
||||
@@ -527,8 +527,8 @@ static int meson_sar_adc_get_sample(struct iio_dev *indio_dev,
|
||||
|
||||
if (ret) {
|
||||
dev_warn(indio_dev->dev.parent,
|
||||
"failed to read sample for channel %d: %d\n",
|
||||
chan->channel, ret);
|
||||
"failed to read sample for channel %lu: %d\n",
|
||||
chan->address, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -563,7 +563,7 @@ static int meson_sar_adc_iio_info_read_raw(struct iio_dev *indio_dev,
|
||||
}
|
||||
|
||||
*val = ret / 1000;
|
||||
*val2 = priv->data->param->resolution;
|
||||
*val2 = priv->param->resolution;
|
||||
return IIO_VAL_FRACTIONAL_LOG2;
|
||||
|
||||
case IIO_CHAN_INFO_CALIBBIAS:
|
||||
@@ -636,7 +636,7 @@ static int meson_sar_adc_init(struct iio_dev *indio_dev)
|
||||
*/
|
||||
meson_sar_adc_set_chan7_mux(indio_dev, CHAN7_MUX_CH7_INPUT);
|
||||
|
||||
if (priv->data->param->has_bl30_integration) {
|
||||
if (priv->param->has_bl30_integration) {
|
||||
/*
|
||||
* leave sampling delay and the input clocks as configured by
|
||||
* BL30 to make sure BL30 gets the values it expects when
|
||||
@@ -716,7 +716,7 @@ static int meson_sar_adc_init(struct iio_dev *indio_dev)
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = clk_set_rate(priv->adc_clk, priv->data->param->clock_rate);
|
||||
ret = clk_set_rate(priv->adc_clk, priv->param->clock_rate);
|
||||
if (ret) {
|
||||
dev_err(indio_dev->dev.parent,
|
||||
"failed to set adc clock rate\n");
|
||||
@@ -729,7 +729,7 @@ static int meson_sar_adc_init(struct iio_dev *indio_dev)
|
||||
static void meson_sar_adc_set_bandgap(struct iio_dev *indio_dev, bool on_off)
|
||||
{
|
||||
struct meson_sar_adc_priv *priv = iio_priv(indio_dev);
|
||||
const struct meson_sar_adc_param *param = priv->data->param;
|
||||
const struct meson_sar_adc_param *param = priv->param;
|
||||
u32 enable_mask;
|
||||
|
||||
if (param->bandgap_reg == MESON_SAR_ADC_REG11)
|
||||
@@ -849,13 +849,13 @@ static int meson_sar_adc_calib(struct iio_dev *indio_dev)
|
||||
int ret, nominal0, nominal1, value0, value1;
|
||||
|
||||
/* use points 25% and 75% for calibration */
|
||||
nominal0 = (1 << priv->data->param->resolution) / 4;
|
||||
nominal1 = (1 << priv->data->param->resolution) * 3 / 4;
|
||||
nominal0 = (1 << priv->param->resolution) / 4;
|
||||
nominal1 = (1 << priv->param->resolution) * 3 / 4;
|
||||
|
||||
meson_sar_adc_set_chan7_mux(indio_dev, CHAN7_MUX_VDD_DIV4);
|
||||
usleep_range(10, 20);
|
||||
ret = meson_sar_adc_get_sample(indio_dev,
|
||||
&meson_sar_adc_iio_channels[7],
|
||||
&indio_dev->channels[7],
|
||||
MEAN_AVERAGING, EIGHT_SAMPLES, &value0);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
@@ -863,7 +863,7 @@ static int meson_sar_adc_calib(struct iio_dev *indio_dev)
|
||||
meson_sar_adc_set_chan7_mux(indio_dev, CHAN7_MUX_VDD_MUL3_DIV4);
|
||||
usleep_range(10, 20);
|
||||
ret = meson_sar_adc_get_sample(indio_dev,
|
||||
&meson_sar_adc_iio_channels[7],
|
||||
&indio_dev->channels[7],
|
||||
MEAN_AVERAGING, EIGHT_SAMPLES, &value1);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
@@ -979,11 +979,11 @@ MODULE_DEVICE_TABLE(of, meson_sar_adc_of_match);
|
||||
|
||||
static int meson_sar_adc_probe(struct platform_device *pdev)
|
||||
{
|
||||
const struct meson_sar_adc_data *match_data;
|
||||
struct meson_sar_adc_priv *priv;
|
||||
struct iio_dev *indio_dev;
|
||||
struct resource *res;
|
||||
void __iomem *base;
|
||||
const struct of_device_id *match;
|
||||
int irq, ret;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*priv));
|
||||
@@ -995,15 +995,15 @@ static int meson_sar_adc_probe(struct platform_device *pdev)
|
||||
priv = iio_priv(indio_dev);
|
||||
init_completion(&priv->done);
|
||||
|
||||
match = of_match_device(meson_sar_adc_of_match, &pdev->dev);
|
||||
if (!match) {
|
||||
dev_err(&pdev->dev, "failed to match device\n");
|
||||
match_data = of_device_get_match_data(&pdev->dev);
|
||||
if (!match_data) {
|
||||
dev_err(&pdev->dev, "failed to get match data\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
priv->data = match->data;
|
||||
priv->param = match_data->param;
|
||||
|
||||
indio_dev->name = priv->data->name;
|
||||
indio_dev->name = match_data->name;
|
||||
indio_dev->dev.parent = &pdev->dev;
|
||||
indio_dev->dev.of_node = pdev->dev.of_node;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
@@ -1027,7 +1027,7 @@ static int meson_sar_adc_probe(struct platform_device *pdev)
|
||||
return ret;
|
||||
|
||||
priv->regmap = devm_regmap_init_mmio(&pdev->dev, base,
|
||||
priv->data->param->regmap_config);
|
||||
priv->param->regmap_config);
|
||||
if (IS_ERR(priv->regmap))
|
||||
return PTR_ERR(priv->regmap);
|
||||
|
||||
|
@@ -708,8 +708,8 @@ static int pm8xxx_of_xlate(struct iio_dev *indio_dev,
|
||||
* mux.
|
||||
*/
|
||||
if (iiospec->args_count != 2) {
|
||||
dev_err(&indio_dev->dev, "wrong number of arguments for %s need 2 got %d\n",
|
||||
iiospec->np->name,
|
||||
dev_err(&indio_dev->dev, "wrong number of arguments for %pOFn need 2 got %d\n",
|
||||
iiospec->np,
|
||||
iiospec->args_count);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
793
drivers/iio/adc/qcom-spmi-adc5.c
Normal file
793
drivers/iio/adc/qcom-spmi-adc5.c
Normal file
@@ -0,0 +1,793 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright (c) 2018, The Linux Foundation. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/completion.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/log2.h>
|
||||
#include <linux/math64.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include <dt-bindings/iio/qcom,spmi-vadc.h>
|
||||
#include "qcom-vadc-common.h"
|
||||
|
||||
#define ADC5_USR_REVISION1 0x0
|
||||
#define ADC5_USR_STATUS1 0x8
|
||||
#define ADC5_USR_STATUS1_REQ_STS BIT(1)
|
||||
#define ADC5_USR_STATUS1_EOC BIT(0)
|
||||
#define ADC5_USR_STATUS1_REQ_STS_EOC_MASK 0x3
|
||||
|
||||
#define ADC5_USR_STATUS2 0x9
|
||||
#define ADC5_USR_STATUS2_CONV_SEQ_MASK 0x70
|
||||
#define ADC5_USR_STATUS2_CONV_SEQ_MASK_SHIFT 0x5
|
||||
|
||||
#define ADC5_USR_IBAT_MEAS 0xf
|
||||
#define ADC5_USR_IBAT_MEAS_SUPPORTED BIT(0)
|
||||
|
||||
#define ADC5_USR_DIG_PARAM 0x42
|
||||
#define ADC5_USR_DIG_PARAM_CAL_VAL BIT(6)
|
||||
#define ADC5_USR_DIG_PARAM_CAL_VAL_SHIFT 6
|
||||
#define ADC5_USR_DIG_PARAM_CAL_SEL 0x30
|
||||
#define ADC5_USR_DIG_PARAM_CAL_SEL_SHIFT 4
|
||||
#define ADC5_USR_DIG_PARAM_DEC_RATIO_SEL 0xc
|
||||
#define ADC5_USR_DIG_PARAM_DEC_RATIO_SEL_SHIFT 2
|
||||
|
||||
#define ADC5_USR_FAST_AVG_CTL 0x43
|
||||
#define ADC5_USR_FAST_AVG_CTL_EN BIT(7)
|
||||
#define ADC5_USR_FAST_AVG_CTL_SAMPLES_MASK 0x7
|
||||
|
||||
#define ADC5_USR_CH_SEL_CTL 0x44
|
||||
|
||||
#define ADC5_USR_DELAY_CTL 0x45
|
||||
#define ADC5_USR_HW_SETTLE_DELAY_MASK 0xf
|
||||
|
||||
#define ADC5_USR_EN_CTL1 0x46
|
||||
#define ADC5_USR_EN_CTL1_ADC_EN BIT(7)
|
||||
|
||||
#define ADC5_USR_CONV_REQ 0x47
|
||||
#define ADC5_USR_CONV_REQ_REQ BIT(7)
|
||||
|
||||
#define ADC5_USR_DATA0 0x50
|
||||
|
||||
#define ADC5_USR_DATA1 0x51
|
||||
|
||||
#define ADC5_USR_IBAT_DATA0 0x52
|
||||
|
||||
#define ADC5_USR_IBAT_DATA1 0x53
|
||||
|
||||
/*
|
||||
* Conversion time varies based on the decimation, clock rate, fast average
|
||||
* samples and measurements queued across different VADC peripherals.
|
||||
* Set the timeout to a max of 100ms.
|
||||
*/
|
||||
#define ADC5_CONV_TIME_MIN_US 263
|
||||
#define ADC5_CONV_TIME_MAX_US 264
|
||||
#define ADC5_CONV_TIME_RETRY 400
|
||||
#define ADC5_CONV_TIMEOUT msecs_to_jiffies(100)
|
||||
|
||||
/* Digital version >= 5.3 supports hw_settle_2 */
|
||||
#define ADC5_HW_SETTLE_DIFF_MINOR 3
|
||||
#define ADC5_HW_SETTLE_DIFF_MAJOR 5
|
||||
|
||||
enum adc5_cal_method {
|
||||
ADC5_NO_CAL = 0,
|
||||
ADC5_RATIOMETRIC_CAL,
|
||||
ADC5_ABSOLUTE_CAL
|
||||
};
|
||||
|
||||
enum adc5_cal_val {
|
||||
ADC5_TIMER_CAL = 0,
|
||||
ADC5_NEW_CAL
|
||||
};
|
||||
|
||||
/**
|
||||
* struct adc5_channel_prop - ADC channel property.
|
||||
* @channel: channel number, refer to the channel list.
|
||||
* @cal_method: calibration method.
|
||||
* @cal_val: calibration value
|
||||
* @decimation: sampling rate supported for the channel.
|
||||
* @prescale: channel scaling performed on the input signal.
|
||||
* @hw_settle_time: the time between AMUX being configured and the
|
||||
* start of conversion.
|
||||
* @avg_samples: ability to provide single result from the ADC
|
||||
* that is an average of multiple measurements.
|
||||
* @scale_fn_type: Represents the scaling function to convert voltage
|
||||
* physical units desired by the client for the channel.
|
||||
* @datasheet_name: Channel name used in device tree.
|
||||
*/
|
||||
struct adc5_channel_prop {
|
||||
unsigned int channel;
|
||||
enum adc5_cal_method cal_method;
|
||||
enum adc5_cal_val cal_val;
|
||||
unsigned int decimation;
|
||||
unsigned int prescale;
|
||||
unsigned int hw_settle_time;
|
||||
unsigned int avg_samples;
|
||||
enum vadc_scale_fn_type scale_fn_type;
|
||||
const char *datasheet_name;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct adc5_chip - ADC private structure.
|
||||
* @regmap: SPMI ADC5 peripheral register map field.
|
||||
* @dev: SPMI ADC5 device.
|
||||
* @base: base address for the ADC peripheral.
|
||||
* @nchannels: number of ADC channels.
|
||||
* @chan_props: array of ADC channel properties.
|
||||
* @iio_chans: array of IIO channels specification.
|
||||
* @poll_eoc: use polling instead of interrupt.
|
||||
* @complete: ADC result notification after interrupt is received.
|
||||
* @lock: ADC lock for access to the peripheral.
|
||||
* @data: software configuration data.
|
||||
*/
|
||||
struct adc5_chip {
|
||||
struct regmap *regmap;
|
||||
struct device *dev;
|
||||
u16 base;
|
||||
unsigned int nchannels;
|
||||
struct adc5_channel_prop *chan_props;
|
||||
struct iio_chan_spec *iio_chans;
|
||||
bool poll_eoc;
|
||||
struct completion complete;
|
||||
struct mutex lock;
|
||||
const struct adc5_data *data;
|
||||
};
|
||||
|
||||
static const struct vadc_prescale_ratio adc5_prescale_ratios[] = {
|
||||
{.num = 1, .den = 1},
|
||||
{.num = 1, .den = 3},
|
||||
{.num = 1, .den = 4},
|
||||
{.num = 1, .den = 6},
|
||||
{.num = 1, .den = 20},
|
||||
{.num = 1, .den = 8},
|
||||
{.num = 10, .den = 81},
|
||||
{.num = 1, .den = 10},
|
||||
{.num = 1, .den = 16}
|
||||
};
|
||||
|
||||
static int adc5_read(struct adc5_chip *adc, u16 offset, u8 *data, int len)
|
||||
{
|
||||
return regmap_bulk_read(adc->regmap, adc->base + offset, data, len);
|
||||
}
|
||||
|
||||
static int adc5_write(struct adc5_chip *adc, u16 offset, u8 *data, int len)
|
||||
{
|
||||
return regmap_bulk_write(adc->regmap, adc->base + offset, data, len);
|
||||
}
|
||||
|
||||
static int adc5_prescaling_from_dt(u32 num, u32 den)
|
||||
{
|
||||
unsigned int pre;
|
||||
|
||||
for (pre = 0; pre < ARRAY_SIZE(adc5_prescale_ratios); pre++)
|
||||
if (adc5_prescale_ratios[pre].num == num &&
|
||||
adc5_prescale_ratios[pre].den == den)
|
||||
break;
|
||||
|
||||
if (pre == ARRAY_SIZE(adc5_prescale_ratios))
|
||||
return -EINVAL;
|
||||
|
||||
return pre;
|
||||
}
|
||||
|
||||
static int adc5_hw_settle_time_from_dt(u32 value,
|
||||
const unsigned int *hw_settle)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < VADC_HW_SETTLE_SAMPLES_MAX; i++) {
|
||||
if (value == hw_settle[i])
|
||||
return i;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int adc5_avg_samples_from_dt(u32 value)
|
||||
{
|
||||
if (!is_power_of_2(value) || value > ADC5_AVG_SAMPLES_MAX)
|
||||
return -EINVAL;
|
||||
|
||||
return __ffs(value);
|
||||
}
|
||||
|
||||
static int adc5_decimation_from_dt(u32 value,
|
||||
const unsigned int *decimation)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < ADC5_DECIMATION_SAMPLES_MAX; i++) {
|
||||
if (value == decimation[i])
|
||||
return i;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int adc5_read_voltage_data(struct adc5_chip *adc, u16 *data)
|
||||
{
|
||||
int ret;
|
||||
u8 rslt_lsb, rslt_msb;
|
||||
|
||||
ret = adc5_read(adc, ADC5_USR_DATA0, &rslt_lsb, sizeof(rslt_lsb));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = adc5_read(adc, ADC5_USR_DATA1, &rslt_msb, sizeof(rslt_lsb));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
*data = (rslt_msb << 8) | rslt_lsb;
|
||||
|
||||
if (*data == ADC5_USR_DATA_CHECK) {
|
||||
pr_err("Invalid data:0x%x\n", *data);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
pr_debug("voltage raw code:0x%x\n", *data);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int adc5_poll_wait_eoc(struct adc5_chip *adc)
|
||||
{
|
||||
unsigned int count, retry = ADC5_CONV_TIME_RETRY;
|
||||
u8 status1;
|
||||
int ret;
|
||||
|
||||
for (count = 0; count < retry; count++) {
|
||||
ret = adc5_read(adc, ADC5_USR_STATUS1, &status1,
|
||||
sizeof(status1));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
status1 &= ADC5_USR_STATUS1_REQ_STS_EOC_MASK;
|
||||
if (status1 == ADC5_USR_STATUS1_EOC)
|
||||
return 0;
|
||||
|
||||
usleep_range(ADC5_CONV_TIME_MIN_US, ADC5_CONV_TIME_MAX_US);
|
||||
}
|
||||
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
static void adc5_update_dig_param(struct adc5_chip *adc,
|
||||
struct adc5_channel_prop *prop, u8 *data)
|
||||
{
|
||||
/* Update calibration value */
|
||||
*data &= ~ADC5_USR_DIG_PARAM_CAL_VAL;
|
||||
*data |= (prop->cal_val << ADC5_USR_DIG_PARAM_CAL_VAL_SHIFT);
|
||||
|
||||
/* Update calibration select */
|
||||
*data &= ~ADC5_USR_DIG_PARAM_CAL_SEL;
|
||||
*data |= (prop->cal_method << ADC5_USR_DIG_PARAM_CAL_SEL_SHIFT);
|
||||
|
||||
/* Update decimation ratio select */
|
||||
*data &= ~ADC5_USR_DIG_PARAM_DEC_RATIO_SEL;
|
||||
*data |= (prop->decimation << ADC5_USR_DIG_PARAM_DEC_RATIO_SEL_SHIFT);
|
||||
}
|
||||
|
||||
static int adc5_configure(struct adc5_chip *adc,
|
||||
struct adc5_channel_prop *prop)
|
||||
{
|
||||
int ret;
|
||||
u8 buf[6];
|
||||
|
||||
/* Read registers 0x42 through 0x46 */
|
||||
ret = adc5_read(adc, ADC5_USR_DIG_PARAM, buf, sizeof(buf));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* Digital param selection */
|
||||
adc5_update_dig_param(adc, prop, &buf[0]);
|
||||
|
||||
/* Update fast average sample value */
|
||||
buf[1] &= (u8) ~ADC5_USR_FAST_AVG_CTL_SAMPLES_MASK;
|
||||
buf[1] |= prop->avg_samples;
|
||||
|
||||
/* Select ADC channel */
|
||||
buf[2] = prop->channel;
|
||||
|
||||
/* Select HW settle delay for channel */
|
||||
buf[3] &= (u8) ~ADC5_USR_HW_SETTLE_DELAY_MASK;
|
||||
buf[3] |= prop->hw_settle_time;
|
||||
|
||||
/* Select ADC enable */
|
||||
buf[4] |= ADC5_USR_EN_CTL1_ADC_EN;
|
||||
|
||||
/* Select CONV request */
|
||||
buf[5] |= ADC5_USR_CONV_REQ_REQ;
|
||||
|
||||
if (!adc->poll_eoc)
|
||||
reinit_completion(&adc->complete);
|
||||
|
||||
return adc5_write(adc, ADC5_USR_DIG_PARAM, buf, sizeof(buf));
|
||||
}
|
||||
|
||||
static int adc5_do_conversion(struct adc5_chip *adc,
|
||||
struct adc5_channel_prop *prop,
|
||||
struct iio_chan_spec const *chan,
|
||||
u16 *data_volt, u16 *data_cur)
|
||||
{
|
||||
int ret;
|
||||
|
||||
mutex_lock(&adc->lock);
|
||||
|
||||
ret = adc5_configure(adc, prop);
|
||||
if (ret) {
|
||||
pr_err("ADC configure failed with %d\n", ret);
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
if (adc->poll_eoc) {
|
||||
ret = adc5_poll_wait_eoc(adc);
|
||||
if (ret < 0) {
|
||||
pr_err("EOC bit not set\n");
|
||||
goto unlock;
|
||||
}
|
||||
} else {
|
||||
ret = wait_for_completion_timeout(&adc->complete,
|
||||
ADC5_CONV_TIMEOUT);
|
||||
if (!ret) {
|
||||
pr_debug("Did not get completion timeout.\n");
|
||||
ret = adc5_poll_wait_eoc(adc);
|
||||
if (ret < 0) {
|
||||
pr_err("EOC bit not set\n");
|
||||
goto unlock;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ret = adc5_read_voltage_data(adc, data_volt);
|
||||
unlock:
|
||||
mutex_unlock(&adc->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static irqreturn_t adc5_isr(int irq, void *dev_id)
|
||||
{
|
||||
struct adc5_chip *adc = dev_id;
|
||||
|
||||
complete(&adc->complete);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int adc5_of_xlate(struct iio_dev *indio_dev,
|
||||
const struct of_phandle_args *iiospec)
|
||||
{
|
||||
struct adc5_chip *adc = iio_priv(indio_dev);
|
||||
int i;
|
||||
|
||||
for (i = 0; i < adc->nchannels; i++)
|
||||
if (adc->chan_props[i].channel == iiospec->args[0])
|
||||
return i;
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int adc5_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan, int *val, int *val2,
|
||||
long mask)
|
||||
{
|
||||
struct adc5_chip *adc = iio_priv(indio_dev);
|
||||
struct adc5_channel_prop *prop;
|
||||
u16 adc_code_volt, adc_code_cur;
|
||||
int ret;
|
||||
|
||||
prop = &adc->chan_props[chan->address];
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_PROCESSED:
|
||||
ret = adc5_do_conversion(adc, prop, chan,
|
||||
&adc_code_volt, &adc_code_cur);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = qcom_adc5_hw_scale(prop->scale_fn_type,
|
||||
&adc5_prescale_ratios[prop->prescale],
|
||||
adc->data,
|
||||
adc_code_volt, val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return IIO_VAL_INT;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct iio_info adc5_info = {
|
||||
.read_raw = adc5_read_raw,
|
||||
.of_xlate = adc5_of_xlate,
|
||||
};
|
||||
|
||||
struct adc5_channels {
|
||||
const char *datasheet_name;
|
||||
unsigned int prescale_index;
|
||||
enum iio_chan_type type;
|
||||
long info_mask;
|
||||
enum vadc_scale_fn_type scale_fn_type;
|
||||
};
|
||||
|
||||
#define ADC5_CHAN(_dname, _type, _mask, _pre, _scale) \
|
||||
{ \
|
||||
.datasheet_name = _dname, \
|
||||
.prescale_index = _pre, \
|
||||
.type = _type, \
|
||||
.info_mask = _mask, \
|
||||
.scale_fn_type = _scale, \
|
||||
}, \
|
||||
|
||||
#define ADC5_CHAN_TEMP(_dname, _pre, _scale) \
|
||||
ADC5_CHAN(_dname, IIO_TEMP, \
|
||||
BIT(IIO_CHAN_INFO_PROCESSED), \
|
||||
_pre, _scale) \
|
||||
|
||||
#define ADC5_CHAN_VOLT(_dname, _pre, _scale) \
|
||||
ADC5_CHAN(_dname, IIO_VOLTAGE, \
|
||||
BIT(IIO_CHAN_INFO_PROCESSED), \
|
||||
_pre, _scale) \
|
||||
|
||||
static const struct adc5_channels adc5_chans_pmic[ADC5_MAX_CHANNEL] = {
|
||||
[ADC5_REF_GND] = ADC5_CHAN_VOLT("ref_gnd", 1,
|
||||
SCALE_HW_CALIB_DEFAULT)
|
||||
[ADC5_1P25VREF] = ADC5_CHAN_VOLT("vref_1p25", 1,
|
||||
SCALE_HW_CALIB_DEFAULT)
|
||||
[ADC5_VPH_PWR] = ADC5_CHAN_VOLT("vph_pwr", 3,
|
||||
SCALE_HW_CALIB_DEFAULT)
|
||||
[ADC5_VBAT_SNS] = ADC5_CHAN_VOLT("vbat_sns", 3,
|
||||
SCALE_HW_CALIB_DEFAULT)
|
||||
[ADC5_DIE_TEMP] = ADC5_CHAN_TEMP("die_temp", 1,
|
||||
SCALE_HW_CALIB_PMIC_THERM)
|
||||
[ADC5_USB_IN_I] = ADC5_CHAN_VOLT("usb_in_i_uv", 1,
|
||||
SCALE_HW_CALIB_DEFAULT)
|
||||
[ADC5_USB_IN_V_16] = ADC5_CHAN_VOLT("usb_in_v_div_16", 16,
|
||||
SCALE_HW_CALIB_DEFAULT)
|
||||
[ADC5_CHG_TEMP] = ADC5_CHAN_TEMP("chg_temp", 1,
|
||||
SCALE_HW_CALIB_PM5_CHG_TEMP)
|
||||
/* Charger prescales SBUx and MID_CHG to fit within 1.8V upper unit */
|
||||
[ADC5_SBUx] = ADC5_CHAN_VOLT("chg_sbux", 3,
|
||||
SCALE_HW_CALIB_DEFAULT)
|
||||
[ADC5_MID_CHG_DIV6] = ADC5_CHAN_VOLT("chg_mid_chg", 6,
|
||||
SCALE_HW_CALIB_DEFAULT)
|
||||
[ADC5_XO_THERM_100K_PU] = ADC5_CHAN_TEMP("xo_therm", 1,
|
||||
SCALE_HW_CALIB_XOTHERM)
|
||||
[ADC5_AMUX_THM1_100K_PU] = ADC5_CHAN_TEMP("amux_thm1_100k_pu", 1,
|
||||
SCALE_HW_CALIB_THERM_100K_PULLUP)
|
||||
[ADC5_AMUX_THM2_100K_PU] = ADC5_CHAN_TEMP("amux_thm2_100k_pu", 1,
|
||||
SCALE_HW_CALIB_THERM_100K_PULLUP)
|
||||
[ADC5_AMUX_THM3_100K_PU] = ADC5_CHAN_TEMP("amux_thm3_100k_pu", 1,
|
||||
SCALE_HW_CALIB_THERM_100K_PULLUP)
|
||||
[ADC5_AMUX_THM2] = ADC5_CHAN_TEMP("amux_thm2", 1,
|
||||
SCALE_HW_CALIB_PM5_SMB_TEMP)
|
||||
};
|
||||
|
||||
static const struct adc5_channels adc5_chans_rev2[ADC5_MAX_CHANNEL] = {
|
||||
[ADC5_REF_GND] = ADC5_CHAN_VOLT("ref_gnd", 1,
|
||||
SCALE_HW_CALIB_DEFAULT)
|
||||
[ADC5_1P25VREF] = ADC5_CHAN_VOLT("vref_1p25", 1,
|
||||
SCALE_HW_CALIB_DEFAULT)
|
||||
[ADC5_VPH_PWR] = ADC5_CHAN_VOLT("vph_pwr", 3,
|
||||
SCALE_HW_CALIB_DEFAULT)
|
||||
[ADC5_VBAT_SNS] = ADC5_CHAN_VOLT("vbat_sns", 3,
|
||||
SCALE_HW_CALIB_DEFAULT)
|
||||
[ADC5_VCOIN] = ADC5_CHAN_VOLT("vcoin", 3,
|
||||
SCALE_HW_CALIB_DEFAULT)
|
||||
[ADC5_DIE_TEMP] = ADC5_CHAN_TEMP("die_temp", 1,
|
||||
SCALE_HW_CALIB_PMIC_THERM)
|
||||
[ADC5_AMUX_THM1_100K_PU] = ADC5_CHAN_TEMP("amux_thm1_100k_pu", 1,
|
||||
SCALE_HW_CALIB_THERM_100K_PULLUP)
|
||||
[ADC5_AMUX_THM2_100K_PU] = ADC5_CHAN_TEMP("amux_thm2_100k_pu", 1,
|
||||
SCALE_HW_CALIB_THERM_100K_PULLUP)
|
||||
[ADC5_AMUX_THM3_100K_PU] = ADC5_CHAN_TEMP("amux_thm3_100k_pu", 1,
|
||||
SCALE_HW_CALIB_THERM_100K_PULLUP)
|
||||
[ADC5_AMUX_THM4_100K_PU] = ADC5_CHAN_TEMP("amux_thm4_100k_pu", 1,
|
||||
SCALE_HW_CALIB_THERM_100K_PULLUP)
|
||||
[ADC5_AMUX_THM5_100K_PU] = ADC5_CHAN_TEMP("amux_thm5_100k_pu", 1,
|
||||
SCALE_HW_CALIB_THERM_100K_PULLUP)
|
||||
[ADC5_XO_THERM_100K_PU] = ADC5_CHAN_TEMP("xo_therm_100k_pu", 1,
|
||||
SCALE_HW_CALIB_THERM_100K_PULLUP)
|
||||
};
|
||||
|
||||
static int adc5_get_dt_channel_data(struct adc5_chip *adc,
|
||||
struct adc5_channel_prop *prop,
|
||||
struct device_node *node,
|
||||
const struct adc5_data *data)
|
||||
{
|
||||
const char *name = node->name, *channel_name;
|
||||
u32 chan, value, varr[2];
|
||||
int ret;
|
||||
struct device *dev = adc->dev;
|
||||
|
||||
ret = of_property_read_u32(node, "reg", &chan);
|
||||
if (ret) {
|
||||
dev_err(dev, "invalid channel number %s\n", name);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (chan > ADC5_PARALLEL_ISENSE_VBAT_IDATA ||
|
||||
!data->adc_chans[chan].datasheet_name) {
|
||||
dev_err(dev, "%s invalid channel number %d\n", name, chan);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* the channel has DT description */
|
||||
prop->channel = chan;
|
||||
|
||||
channel_name = of_get_property(node,
|
||||
"label", NULL) ? : node->name;
|
||||
if (!channel_name) {
|
||||
pr_err("Invalid channel name\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
prop->datasheet_name = channel_name;
|
||||
|
||||
ret = of_property_read_u32(node, "qcom,decimation", &value);
|
||||
if (!ret) {
|
||||
ret = adc5_decimation_from_dt(value, data->decimation);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "%02x invalid decimation %d\n",
|
||||
chan, value);
|
||||
return ret;
|
||||
}
|
||||
prop->decimation = ret;
|
||||
} else {
|
||||
prop->decimation = ADC5_DECIMATION_DEFAULT;
|
||||
}
|
||||
|
||||
ret = of_property_read_u32_array(node, "qcom,pre-scaling", varr, 2);
|
||||
if (!ret) {
|
||||
ret = adc5_prescaling_from_dt(varr[0], varr[1]);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "%02x invalid pre-scaling <%d %d>\n",
|
||||
chan, varr[0], varr[1]);
|
||||
return ret;
|
||||
}
|
||||
prop->prescale = ret;
|
||||
}
|
||||
|
||||
ret = of_property_read_u32(node, "qcom,hw-settle-time", &value);
|
||||
if (!ret) {
|
||||
u8 dig_version[2];
|
||||
|
||||
ret = adc5_read(adc, ADC5_USR_REVISION1, dig_version,
|
||||
sizeof(dig_version));
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "Invalid dig version read %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
pr_debug("dig_ver:minor:%d, major:%d\n", dig_version[0],
|
||||
dig_version[1]);
|
||||
/* Digital controller >= 5.3 have hw_settle_2 option */
|
||||
if (dig_version[0] >= ADC5_HW_SETTLE_DIFF_MINOR &&
|
||||
dig_version[1] >= ADC5_HW_SETTLE_DIFF_MAJOR)
|
||||
ret = adc5_hw_settle_time_from_dt(value,
|
||||
data->hw_settle_2);
|
||||
else
|
||||
ret = adc5_hw_settle_time_from_dt(value,
|
||||
data->hw_settle_1);
|
||||
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "%02x invalid hw-settle-time %d us\n",
|
||||
chan, value);
|
||||
return ret;
|
||||
}
|
||||
prop->hw_settle_time = ret;
|
||||
} else {
|
||||
prop->hw_settle_time = VADC_DEF_HW_SETTLE_TIME;
|
||||
}
|
||||
|
||||
ret = of_property_read_u32(node, "qcom,avg-samples", &value);
|
||||
if (!ret) {
|
||||
ret = adc5_avg_samples_from_dt(value);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "%02x invalid avg-samples %d\n",
|
||||
chan, value);
|
||||
return ret;
|
||||
}
|
||||
prop->avg_samples = ret;
|
||||
} else {
|
||||
prop->avg_samples = VADC_DEF_AVG_SAMPLES;
|
||||
}
|
||||
|
||||
if (of_property_read_bool(node, "qcom,ratiometric"))
|
||||
prop->cal_method = ADC5_RATIOMETRIC_CAL;
|
||||
else
|
||||
prop->cal_method = ADC5_ABSOLUTE_CAL;
|
||||
|
||||
/*
|
||||
* Default to using timer calibration. Using a fresh calibration value
|
||||
* for every conversion will increase the overall time for a request.
|
||||
*/
|
||||
prop->cal_val = ADC5_TIMER_CAL;
|
||||
|
||||
dev_dbg(dev, "%02x name %s\n", chan, name);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct adc5_data adc5_data_pmic = {
|
||||
.full_scale_code_volt = 0x70e4,
|
||||
.full_scale_code_cur = 0x2710,
|
||||
.adc_chans = adc5_chans_pmic,
|
||||
.decimation = (unsigned int [ADC5_DECIMATION_SAMPLES_MAX])
|
||||
{250, 420, 840},
|
||||
.hw_settle_1 = (unsigned int [VADC_HW_SETTLE_SAMPLES_MAX])
|
||||
{15, 100, 200, 300, 400, 500, 600, 700,
|
||||
800, 900, 1, 2, 4, 6, 8, 10},
|
||||
.hw_settle_2 = (unsigned int [VADC_HW_SETTLE_SAMPLES_MAX])
|
||||
{15, 100, 200, 300, 400, 500, 600, 700,
|
||||
1, 2, 4, 8, 16, 32, 64, 128},
|
||||
};
|
||||
|
||||
static const struct adc5_data adc5_data_pmic_rev2 = {
|
||||
.full_scale_code_volt = 0x4000,
|
||||
.full_scale_code_cur = 0x1800,
|
||||
.adc_chans = adc5_chans_rev2,
|
||||
.decimation = (unsigned int [ADC5_DECIMATION_SAMPLES_MAX])
|
||||
{256, 512, 1024},
|
||||
.hw_settle_1 = (unsigned int [VADC_HW_SETTLE_SAMPLES_MAX])
|
||||
{0, 100, 200, 300, 400, 500, 600, 700,
|
||||
800, 900, 1, 2, 4, 6, 8, 10},
|
||||
.hw_settle_2 = (unsigned int [VADC_HW_SETTLE_SAMPLES_MAX])
|
||||
{15, 100, 200, 300, 400, 500, 600, 700,
|
||||
1, 2, 4, 8, 16, 32, 64, 128},
|
||||
};
|
||||
|
||||
static const struct of_device_id adc5_match_table[] = {
|
||||
{
|
||||
.compatible = "qcom,spmi-adc5",
|
||||
.data = &adc5_data_pmic,
|
||||
},
|
||||
{
|
||||
.compatible = "qcom,spmi-adc-rev2",
|
||||
.data = &adc5_data_pmic_rev2,
|
||||
},
|
||||
{ }
|
||||
};
|
||||
|
||||
static int adc5_get_dt_data(struct adc5_chip *adc, struct device_node *node)
|
||||
{
|
||||
const struct adc5_channels *adc_chan;
|
||||
struct iio_chan_spec *iio_chan;
|
||||
struct adc5_channel_prop prop, *chan_props;
|
||||
struct device_node *child;
|
||||
unsigned int index = 0;
|
||||
const struct of_device_id *id;
|
||||
const struct adc5_data *data;
|
||||
int ret;
|
||||
|
||||
adc->nchannels = of_get_available_child_count(node);
|
||||
if (!adc->nchannels)
|
||||
return -EINVAL;
|
||||
|
||||
adc->iio_chans = devm_kcalloc(adc->dev, adc->nchannels,
|
||||
sizeof(*adc->iio_chans), GFP_KERNEL);
|
||||
if (!adc->iio_chans)
|
||||
return -ENOMEM;
|
||||
|
||||
adc->chan_props = devm_kcalloc(adc->dev, adc->nchannels,
|
||||
sizeof(*adc->chan_props), GFP_KERNEL);
|
||||
if (!adc->chan_props)
|
||||
return -ENOMEM;
|
||||
|
||||
chan_props = adc->chan_props;
|
||||
iio_chan = adc->iio_chans;
|
||||
id = of_match_node(adc5_match_table, node);
|
||||
if (id)
|
||||
data = id->data;
|
||||
else
|
||||
data = &adc5_data_pmic;
|
||||
adc->data = data;
|
||||
|
||||
for_each_available_child_of_node(node, child) {
|
||||
ret = adc5_get_dt_channel_data(adc, &prop, child, data);
|
||||
if (ret) {
|
||||
of_node_put(child);
|
||||
return ret;
|
||||
}
|
||||
|
||||
prop.scale_fn_type =
|
||||
data->adc_chans[prop.channel].scale_fn_type;
|
||||
*chan_props = prop;
|
||||
adc_chan = &data->adc_chans[prop.channel];
|
||||
|
||||
iio_chan->channel = prop.channel;
|
||||
iio_chan->datasheet_name = prop.datasheet_name;
|
||||
iio_chan->extend_name = prop.datasheet_name;
|
||||
iio_chan->info_mask_separate = adc_chan->info_mask;
|
||||
iio_chan->type = adc_chan->type;
|
||||
iio_chan->address = index;
|
||||
iio_chan++;
|
||||
chan_props++;
|
||||
index++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int adc5_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device_node *node = pdev->dev.of_node;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct iio_dev *indio_dev;
|
||||
struct adc5_chip *adc;
|
||||
struct regmap *regmap;
|
||||
int ret, irq_eoc;
|
||||
u32 reg;
|
||||
|
||||
regmap = dev_get_regmap(dev->parent, NULL);
|
||||
if (!regmap)
|
||||
return -ENODEV;
|
||||
|
||||
ret = of_property_read_u32(node, "reg", ®);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(dev, sizeof(*adc));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
adc = iio_priv(indio_dev);
|
||||
adc->regmap = regmap;
|
||||
adc->dev = dev;
|
||||
adc->base = reg;
|
||||
init_completion(&adc->complete);
|
||||
mutex_init(&adc->lock);
|
||||
|
||||
ret = adc5_get_dt_data(adc, node);
|
||||
if (ret) {
|
||||
pr_err("adc get dt data failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
irq_eoc = platform_get_irq(pdev, 0);
|
||||
if (irq_eoc < 0) {
|
||||
if (irq_eoc == -EPROBE_DEFER || irq_eoc == -EINVAL)
|
||||
return irq_eoc;
|
||||
adc->poll_eoc = true;
|
||||
} else {
|
||||
ret = devm_request_irq(dev, irq_eoc, adc5_isr, 0,
|
||||
"pm-adc5", adc);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
indio_dev->dev.parent = dev;
|
||||
indio_dev->dev.of_node = node;
|
||||
indio_dev->name = pdev->name;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->info = &adc5_info;
|
||||
indio_dev->channels = adc->iio_chans;
|
||||
indio_dev->num_channels = adc->nchannels;
|
||||
|
||||
return devm_iio_device_register(dev, indio_dev);
|
||||
}
|
||||
|
||||
static struct platform_driver adc5_driver = {
|
||||
.driver = {
|
||||
.name = "qcom-spmi-adc5.c",
|
||||
.of_match_table = adc5_match_table,
|
||||
},
|
||||
.probe = adc5_probe,
|
||||
};
|
||||
module_platform_driver(adc5_driver);
|
||||
|
||||
MODULE_ALIAS("platform:qcom-spmi-adc5");
|
||||
MODULE_DESCRIPTION("Qualcomm Technologies Inc. PMIC5 ADC driver");
|
||||
MODULE_LICENSE("GPL v2");
|
@@ -47,8 +47,79 @@ static const struct vadc_map_pt adcmap_100k_104ef_104fb[] = {
|
||||
{44, 125}
|
||||
};
|
||||
|
||||
/*
|
||||
* Voltage to temperature table for 100k pull up for NTCG104EF104 with
|
||||
* 1.875V reference.
|
||||
*/
|
||||
static const struct vadc_map_pt adcmap_100k_104ef_104fb_1875_vref[] = {
|
||||
{ 1831, -40000 },
|
||||
{ 1814, -35000 },
|
||||
{ 1791, -30000 },
|
||||
{ 1761, -25000 },
|
||||
{ 1723, -20000 },
|
||||
{ 1675, -15000 },
|
||||
{ 1616, -10000 },
|
||||
{ 1545, -5000 },
|
||||
{ 1463, 0 },
|
||||
{ 1370, 5000 },
|
||||
{ 1268, 10000 },
|
||||
{ 1160, 15000 },
|
||||
{ 1049, 20000 },
|
||||
{ 937, 25000 },
|
||||
{ 828, 30000 },
|
||||
{ 726, 35000 },
|
||||
{ 630, 40000 },
|
||||
{ 544, 45000 },
|
||||
{ 467, 50000 },
|
||||
{ 399, 55000 },
|
||||
{ 340, 60000 },
|
||||
{ 290, 65000 },
|
||||
{ 247, 70000 },
|
||||
{ 209, 75000 },
|
||||
{ 179, 80000 },
|
||||
{ 153, 85000 },
|
||||
{ 130, 90000 },
|
||||
{ 112, 95000 },
|
||||
{ 96, 100000 },
|
||||
{ 82, 105000 },
|
||||
{ 71, 110000 },
|
||||
{ 62, 115000 },
|
||||
{ 53, 120000 },
|
||||
{ 46, 125000 },
|
||||
};
|
||||
|
||||
static int qcom_vadc_scale_hw_calib_volt(
|
||||
const struct vadc_prescale_ratio *prescale,
|
||||
const struct adc5_data *data,
|
||||
u16 adc_code, int *result_uv);
|
||||
static int qcom_vadc_scale_hw_calib_therm(
|
||||
const struct vadc_prescale_ratio *prescale,
|
||||
const struct adc5_data *data,
|
||||
u16 adc_code, int *result_mdec);
|
||||
static int qcom_vadc_scale_hw_smb_temp(
|
||||
const struct vadc_prescale_ratio *prescale,
|
||||
const struct adc5_data *data,
|
||||
u16 adc_code, int *result_mdec);
|
||||
static int qcom_vadc_scale_hw_chg5_temp(
|
||||
const struct vadc_prescale_ratio *prescale,
|
||||
const struct adc5_data *data,
|
||||
u16 adc_code, int *result_mdec);
|
||||
static int qcom_vadc_scale_hw_calib_die_temp(
|
||||
const struct vadc_prescale_ratio *prescale,
|
||||
const struct adc5_data *data,
|
||||
u16 adc_code, int *result_mdec);
|
||||
|
||||
static struct qcom_adc5_scale_type scale_adc5_fn[] = {
|
||||
[SCALE_HW_CALIB_DEFAULT] = {qcom_vadc_scale_hw_calib_volt},
|
||||
[SCALE_HW_CALIB_THERM_100K_PULLUP] = {qcom_vadc_scale_hw_calib_therm},
|
||||
[SCALE_HW_CALIB_XOTHERM] = {qcom_vadc_scale_hw_calib_therm},
|
||||
[SCALE_HW_CALIB_PMIC_THERM] = {qcom_vadc_scale_hw_calib_die_temp},
|
||||
[SCALE_HW_CALIB_PM5_CHG_TEMP] = {qcom_vadc_scale_hw_chg5_temp},
|
||||
[SCALE_HW_CALIB_PM5_SMB_TEMP] = {qcom_vadc_scale_hw_smb_temp},
|
||||
};
|
||||
|
||||
static int qcom_vadc_map_voltage_temp(const struct vadc_map_pt *pts,
|
||||
u32 tablesize, s32 input, s64 *output)
|
||||
u32 tablesize, s32 input, int *output)
|
||||
{
|
||||
bool descending = 1;
|
||||
u32 i = 0;
|
||||
@@ -128,7 +199,7 @@ static int qcom_vadc_scale_therm(const struct vadc_linear_graph *calib_graph,
|
||||
bool absolute, u16 adc_code,
|
||||
int *result_mdec)
|
||||
{
|
||||
s64 voltage = 0, result = 0;
|
||||
s64 voltage = 0;
|
||||
int ret;
|
||||
|
||||
qcom_vadc_scale_calib(calib_graph, adc_code, absolute, &voltage);
|
||||
@@ -138,12 +209,11 @@ static int qcom_vadc_scale_therm(const struct vadc_linear_graph *calib_graph,
|
||||
|
||||
ret = qcom_vadc_map_voltage_temp(adcmap_100k_104ef_104fb,
|
||||
ARRAY_SIZE(adcmap_100k_104ef_104fb),
|
||||
voltage, &result);
|
||||
voltage, result_mdec);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
result *= 1000;
|
||||
*result_mdec = result;
|
||||
*result_mdec *= 1000;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -191,6 +261,99 @@ static int qcom_vadc_scale_chg_temp(const struct vadc_linear_graph *calib_graph,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int qcom_vadc_scale_code_voltage_factor(u16 adc_code,
|
||||
const struct vadc_prescale_ratio *prescale,
|
||||
const struct adc5_data *data,
|
||||
unsigned int factor)
|
||||
{
|
||||
s64 voltage, temp, adc_vdd_ref_mv = 1875;
|
||||
|
||||
/*
|
||||
* The normal data range is between 0V to 1.875V. On cases where
|
||||
* we read low voltage values, the ADC code can go beyond the
|
||||
* range and the scale result is incorrect so we clamp the values
|
||||
* for the cases where the code represents a value below 0V
|
||||
*/
|
||||
if (adc_code > VADC5_MAX_CODE)
|
||||
adc_code = 0;
|
||||
|
||||
/* (ADC code * vref_vadc (1.875V)) / full_scale_code */
|
||||
voltage = (s64) adc_code * adc_vdd_ref_mv * 1000;
|
||||
voltage = div64_s64(voltage, data->full_scale_code_volt);
|
||||
if (voltage > 0) {
|
||||
voltage *= prescale->den;
|
||||
temp = prescale->num * factor;
|
||||
voltage = div64_s64(voltage, temp);
|
||||
} else {
|
||||
voltage = 0;
|
||||
}
|
||||
|
||||
return (int) voltage;
|
||||
}
|
||||
|
||||
static int qcom_vadc_scale_hw_calib_volt(
|
||||
const struct vadc_prescale_ratio *prescale,
|
||||
const struct adc5_data *data,
|
||||
u16 adc_code, int *result_uv)
|
||||
{
|
||||
*result_uv = qcom_vadc_scale_code_voltage_factor(adc_code,
|
||||
prescale, data, 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int qcom_vadc_scale_hw_calib_therm(
|
||||
const struct vadc_prescale_ratio *prescale,
|
||||
const struct adc5_data *data,
|
||||
u16 adc_code, int *result_mdec)
|
||||
{
|
||||
int voltage;
|
||||
|
||||
voltage = qcom_vadc_scale_code_voltage_factor(adc_code,
|
||||
prescale, data, 1000);
|
||||
|
||||
/* Map voltage to temperature from look-up table */
|
||||
return qcom_vadc_map_voltage_temp(adcmap_100k_104ef_104fb_1875_vref,
|
||||
ARRAY_SIZE(adcmap_100k_104ef_104fb_1875_vref),
|
||||
voltage, result_mdec);
|
||||
}
|
||||
|
||||
static int qcom_vadc_scale_hw_calib_die_temp(
|
||||
const struct vadc_prescale_ratio *prescale,
|
||||
const struct adc5_data *data,
|
||||
u16 adc_code, int *result_mdec)
|
||||
{
|
||||
*result_mdec = qcom_vadc_scale_code_voltage_factor(adc_code,
|
||||
prescale, data, 2);
|
||||
*result_mdec -= KELVINMIL_CELSIUSMIL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int qcom_vadc_scale_hw_smb_temp(
|
||||
const struct vadc_prescale_ratio *prescale,
|
||||
const struct adc5_data *data,
|
||||
u16 adc_code, int *result_mdec)
|
||||
{
|
||||
*result_mdec = qcom_vadc_scale_code_voltage_factor(adc_code * 100,
|
||||
prescale, data, PMIC5_SMB_TEMP_SCALE_FACTOR);
|
||||
*result_mdec = PMIC5_SMB_TEMP_CONSTANT - *result_mdec;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int qcom_vadc_scale_hw_chg5_temp(
|
||||
const struct vadc_prescale_ratio *prescale,
|
||||
const struct adc5_data *data,
|
||||
u16 adc_code, int *result_mdec)
|
||||
{
|
||||
*result_mdec = qcom_vadc_scale_code_voltage_factor(adc_code,
|
||||
prescale, data, 4);
|
||||
*result_mdec = PMIC5_CHG_TEMP_SCALE_FACTOR - *result_mdec;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int qcom_vadc_scale(enum vadc_scale_fn_type scaletype,
|
||||
const struct vadc_linear_graph *calib_graph,
|
||||
const struct vadc_prescale_ratio *prescale,
|
||||
@@ -221,6 +384,22 @@ int qcom_vadc_scale(enum vadc_scale_fn_type scaletype,
|
||||
}
|
||||
EXPORT_SYMBOL(qcom_vadc_scale);
|
||||
|
||||
int qcom_adc5_hw_scale(enum vadc_scale_fn_type scaletype,
|
||||
const struct vadc_prescale_ratio *prescale,
|
||||
const struct adc5_data *data,
|
||||
u16 adc_code, int *result)
|
||||
{
|
||||
if (!(scaletype >= SCALE_HW_CALIB_DEFAULT &&
|
||||
scaletype < SCALE_HW_CALIB_INVALID)) {
|
||||
pr_err("Invalid scale type %d\n", scaletype);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return scale_adc5_fn[scaletype].scale_fn(prescale, data,
|
||||
adc_code, result);
|
||||
}
|
||||
EXPORT_SYMBOL(qcom_adc5_hw_scale);
|
||||
|
||||
int qcom_vadc_decimation_from_dt(u32 value)
|
||||
{
|
||||
if (!is_power_of_2(value) || value < VADC_DECIMATION_MIN ||
|
||||
|
@@ -25,15 +25,31 @@
|
||||
|
||||
#define VADC_DECIMATION_MIN 512
|
||||
#define VADC_DECIMATION_MAX 4096
|
||||
#define ADC5_DEF_VBAT_PRESCALING 1 /* 1:3 */
|
||||
#define ADC5_DECIMATION_SHORT 250
|
||||
#define ADC5_DECIMATION_MEDIUM 420
|
||||
#define ADC5_DECIMATION_LONG 840
|
||||
/* Default decimation - 1024 for rev2, 840 for pmic5 */
|
||||
#define ADC5_DECIMATION_DEFAULT 2
|
||||
#define ADC5_DECIMATION_SAMPLES_MAX 3
|
||||
|
||||
#define VADC_HW_SETTLE_DELAY_MAX 10000
|
||||
#define VADC_HW_SETTLE_SAMPLES_MAX 16
|
||||
#define VADC_AVG_SAMPLES_MAX 512
|
||||
#define ADC5_AVG_SAMPLES_MAX 16
|
||||
|
||||
#define KELVINMIL_CELSIUSMIL 273150
|
||||
#define PMIC5_CHG_TEMP_SCALE_FACTOR 377500
|
||||
#define PMIC5_SMB_TEMP_CONSTANT 419400
|
||||
#define PMIC5_SMB_TEMP_SCALE_FACTOR 356
|
||||
|
||||
#define PMI_CHG_SCALE_1 -138890
|
||||
#define PMI_CHG_SCALE_2 391750000000LL
|
||||
|
||||
#define VADC5_MAX_CODE 0x7fff
|
||||
#define ADC5_FULL_SCALE_CODE 0x70e4
|
||||
#define ADC5_USR_DATA_CHECK 0x8000
|
||||
|
||||
/**
|
||||
* struct vadc_map_pt - Map the graph representation for ADC channel
|
||||
* @x: Represent the ADC digitized code.
|
||||
@@ -89,6 +105,18 @@ struct vadc_prescale_ratio {
|
||||
* SCALE_PMIC_THERM: Returns result in milli degree's Centigrade.
|
||||
* SCALE_XOTHERM: Returns XO thermistor voltage in millidegC.
|
||||
* SCALE_PMI_CHG_TEMP: Conversion for PMI CHG temp
|
||||
* SCALE_HW_CALIB_DEFAULT: Default scaling to convert raw adc code to
|
||||
* voltage (uV) with hardware applied offset/slope values to adc code.
|
||||
* SCALE_HW_CALIB_THERM_100K_PULLUP: Returns temperature in millidegC using
|
||||
* lookup table. The hardware applies offset/slope to adc code.
|
||||
* SCALE_HW_CALIB_XOTHERM: Returns XO thermistor voltage in millidegC using
|
||||
* 100k pullup. The hardware applies offset/slope to adc code.
|
||||
* SCALE_HW_CALIB_PMIC_THERM: Returns result in milli degree's Centigrade.
|
||||
* The hardware applies offset/slope to adc code.
|
||||
* SCALE_HW_CALIB_PM5_CHG_TEMP: Returns result in millidegrees for PMIC5
|
||||
* charger temperature.
|
||||
* SCALE_HW_CALIB_PM5_SMB_TEMP: Returns result in millidegrees for PMIC5
|
||||
* SMB1390 temperature.
|
||||
*/
|
||||
enum vadc_scale_fn_type {
|
||||
SCALE_DEFAULT = 0,
|
||||
@@ -96,6 +124,22 @@ enum vadc_scale_fn_type {
|
||||
SCALE_PMIC_THERM,
|
||||
SCALE_XOTHERM,
|
||||
SCALE_PMI_CHG_TEMP,
|
||||
SCALE_HW_CALIB_DEFAULT,
|
||||
SCALE_HW_CALIB_THERM_100K_PULLUP,
|
||||
SCALE_HW_CALIB_XOTHERM,
|
||||
SCALE_HW_CALIB_PMIC_THERM,
|
||||
SCALE_HW_CALIB_PM5_CHG_TEMP,
|
||||
SCALE_HW_CALIB_PM5_SMB_TEMP,
|
||||
SCALE_HW_CALIB_INVALID,
|
||||
};
|
||||
|
||||
struct adc5_data {
|
||||
const u32 full_scale_code_volt;
|
||||
const u32 full_scale_code_cur;
|
||||
const struct adc5_channels *adc_chans;
|
||||
unsigned int *decimation;
|
||||
unsigned int *hw_settle_1;
|
||||
unsigned int *hw_settle_2;
|
||||
};
|
||||
|
||||
int qcom_vadc_scale(enum vadc_scale_fn_type scaletype,
|
||||
@@ -104,6 +148,16 @@ int qcom_vadc_scale(enum vadc_scale_fn_type scaletype,
|
||||
bool absolute,
|
||||
u16 adc_code, int *result_mdec);
|
||||
|
||||
struct qcom_adc5_scale_type {
|
||||
int (*scale_fn)(const struct vadc_prescale_ratio *prescale,
|
||||
const struct adc5_data *data, u16 adc_code, int *result);
|
||||
};
|
||||
|
||||
int qcom_adc5_hw_scale(enum vadc_scale_fn_type scaletype,
|
||||
const struct vadc_prescale_ratio *prescale,
|
||||
const struct adc5_data *data,
|
||||
u16 adc_code, int *result_mdec);
|
||||
|
||||
int qcom_vadc_decimation_from_dt(u32 value);
|
||||
|
||||
#endif /* QCOM_VADC_COMMON_H */
|
||||
|
@@ -343,8 +343,8 @@ static int rcar_gyroadc_parse_subdevs(struct iio_dev *indio_dev)
|
||||
for_each_child_of_node(np, child) {
|
||||
of_id = of_match_node(rcar_gyroadc_child_match, child);
|
||||
if (!of_id) {
|
||||
dev_err(dev, "Ignoring unsupported ADC \"%s\".",
|
||||
child->name);
|
||||
dev_err(dev, "Ignoring unsupported ADC \"%pOFn\".",
|
||||
child);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -381,16 +381,16 @@ static int rcar_gyroadc_parse_subdevs(struct iio_dev *indio_dev)
|
||||
ret = of_property_read_u32(child, "reg", ®);
|
||||
if (ret) {
|
||||
dev_err(dev,
|
||||
"Failed to get child reg property of ADC \"%s\".\n",
|
||||
child->name);
|
||||
"Failed to get child reg property of ADC \"%pOFn\".\n",
|
||||
child);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Channel number is too high. */
|
||||
if (reg >= num_channels) {
|
||||
dev_err(dev,
|
||||
"Only %i channels supported with %s, but reg = <%i>.\n",
|
||||
num_channels, child->name, reg);
|
||||
"Only %i channels supported with %pOFn, but reg = <%i>.\n",
|
||||
num_channels, child, reg);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
@@ -5,10 +5,12 @@
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/nvmem-consumer.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
/* PMIC global registers definition */
|
||||
#define SC27XX_MODULE_EN 0xc08
|
||||
@@ -87,16 +89,73 @@ struct sc27xx_adc_linear_graph {
|
||||
* should use the small-scale graph, and if more than 1.2v, we should use the
|
||||
* big-scale graph.
|
||||
*/
|
||||
static const struct sc27xx_adc_linear_graph big_scale_graph = {
|
||||
static struct sc27xx_adc_linear_graph big_scale_graph = {
|
||||
4200, 3310,
|
||||
3600, 2832,
|
||||
};
|
||||
|
||||
static const struct sc27xx_adc_linear_graph small_scale_graph = {
|
||||
static struct sc27xx_adc_linear_graph small_scale_graph = {
|
||||
1000, 3413,
|
||||
100, 341,
|
||||
};
|
||||
|
||||
static const struct sc27xx_adc_linear_graph big_scale_graph_calib = {
|
||||
4200, 856,
|
||||
3600, 733,
|
||||
};
|
||||
|
||||
static const struct sc27xx_adc_linear_graph small_scale_graph_calib = {
|
||||
1000, 833,
|
||||
100, 80,
|
||||
};
|
||||
|
||||
static int sc27xx_adc_get_calib_data(u32 calib_data, int calib_adc)
|
||||
{
|
||||
return ((calib_data & 0xff) + calib_adc - 128) * 4;
|
||||
}
|
||||
|
||||
static int sc27xx_adc_scale_calibration(struct sc27xx_adc_data *data,
|
||||
bool big_scale)
|
||||
{
|
||||
const struct sc27xx_adc_linear_graph *calib_graph;
|
||||
struct sc27xx_adc_linear_graph *graph;
|
||||
struct nvmem_cell *cell;
|
||||
const char *cell_name;
|
||||
u32 calib_data = 0;
|
||||
void *buf;
|
||||
size_t len;
|
||||
|
||||
if (big_scale) {
|
||||
calib_graph = &big_scale_graph_calib;
|
||||
graph = &big_scale_graph;
|
||||
cell_name = "big_scale_calib";
|
||||
} else {
|
||||
calib_graph = &small_scale_graph_calib;
|
||||
graph = &small_scale_graph;
|
||||
cell_name = "small_scale_calib";
|
||||
}
|
||||
|
||||
cell = nvmem_cell_get(data->dev, cell_name);
|
||||
if (IS_ERR(cell))
|
||||
return PTR_ERR(cell);
|
||||
|
||||
buf = nvmem_cell_read(cell, &len);
|
||||
nvmem_cell_put(cell);
|
||||
|
||||
if (IS_ERR(buf))
|
||||
return PTR_ERR(buf);
|
||||
|
||||
memcpy(&calib_data, buf, min(len, sizeof(u32)));
|
||||
|
||||
/* Only need to calibrate the adc values in the linear graph. */
|
||||
graph->adc0 = sc27xx_adc_get_calib_data(calib_data, calib_graph->adc0);
|
||||
graph->adc1 = sc27xx_adc_get_calib_data(calib_data >> 8,
|
||||
calib_graph->adc1);
|
||||
|
||||
kfree(buf);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sc27xx_adc_get_ratio(int channel, int scale)
|
||||
{
|
||||
switch (channel) {
|
||||
@@ -209,7 +268,7 @@ static void sc27xx_adc_volt_ratio(struct sc27xx_adc_data *data,
|
||||
*div_denominator = ratio & SC27XX_RATIO_DENOMINATOR_MASK;
|
||||
}
|
||||
|
||||
static int sc27xx_adc_to_volt(const struct sc27xx_adc_linear_graph *graph,
|
||||
static int sc27xx_adc_to_volt(struct sc27xx_adc_linear_graph *graph,
|
||||
int raw_adc)
|
||||
{
|
||||
int tmp;
|
||||
@@ -273,6 +332,17 @@ static int sc27xx_adc_read_raw(struct iio_dev *indio_dev,
|
||||
int ret, tmp;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
ret = sc27xx_adc_read(data, chan->channel, scale, &tmp);
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
*val = tmp;
|
||||
return IIO_VAL_INT;
|
||||
|
||||
case IIO_CHAN_INFO_PROCESSED:
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
ret = sc27xx_adc_read_processed(data, chan->channel, scale,
|
||||
@@ -315,48 +385,47 @@ static const struct iio_info sc27xx_info = {
|
||||
.write_raw = &sc27xx_adc_write_raw,
|
||||
};
|
||||
|
||||
#define SC27XX_ADC_CHANNEL(index) { \
|
||||
#define SC27XX_ADC_CHANNEL(index, mask) { \
|
||||
.type = IIO_VOLTAGE, \
|
||||
.channel = index, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED) | \
|
||||
BIT(IIO_CHAN_INFO_SCALE), \
|
||||
.info_mask_separate = mask | BIT(IIO_CHAN_INFO_SCALE), \
|
||||
.datasheet_name = "CH##index", \
|
||||
.indexed = 1, \
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec sc27xx_channels[] = {
|
||||
SC27XX_ADC_CHANNEL(0),
|
||||
SC27XX_ADC_CHANNEL(1),
|
||||
SC27XX_ADC_CHANNEL(2),
|
||||
SC27XX_ADC_CHANNEL(3),
|
||||
SC27XX_ADC_CHANNEL(4),
|
||||
SC27XX_ADC_CHANNEL(5),
|
||||
SC27XX_ADC_CHANNEL(6),
|
||||
SC27XX_ADC_CHANNEL(7),
|
||||
SC27XX_ADC_CHANNEL(8),
|
||||
SC27XX_ADC_CHANNEL(9),
|
||||
SC27XX_ADC_CHANNEL(10),
|
||||
SC27XX_ADC_CHANNEL(11),
|
||||
SC27XX_ADC_CHANNEL(12),
|
||||
SC27XX_ADC_CHANNEL(13),
|
||||
SC27XX_ADC_CHANNEL(14),
|
||||
SC27XX_ADC_CHANNEL(15),
|
||||
SC27XX_ADC_CHANNEL(16),
|
||||
SC27XX_ADC_CHANNEL(17),
|
||||
SC27XX_ADC_CHANNEL(18),
|
||||
SC27XX_ADC_CHANNEL(19),
|
||||
SC27XX_ADC_CHANNEL(20),
|
||||
SC27XX_ADC_CHANNEL(21),
|
||||
SC27XX_ADC_CHANNEL(22),
|
||||
SC27XX_ADC_CHANNEL(23),
|
||||
SC27XX_ADC_CHANNEL(24),
|
||||
SC27XX_ADC_CHANNEL(25),
|
||||
SC27XX_ADC_CHANNEL(26),
|
||||
SC27XX_ADC_CHANNEL(27),
|
||||
SC27XX_ADC_CHANNEL(28),
|
||||
SC27XX_ADC_CHANNEL(29),
|
||||
SC27XX_ADC_CHANNEL(30),
|
||||
SC27XX_ADC_CHANNEL(31),
|
||||
SC27XX_ADC_CHANNEL(0, BIT(IIO_CHAN_INFO_PROCESSED)),
|
||||
SC27XX_ADC_CHANNEL(1, BIT(IIO_CHAN_INFO_PROCESSED)),
|
||||
SC27XX_ADC_CHANNEL(2, BIT(IIO_CHAN_INFO_PROCESSED)),
|
||||
SC27XX_ADC_CHANNEL(3, BIT(IIO_CHAN_INFO_PROCESSED)),
|
||||
SC27XX_ADC_CHANNEL(4, BIT(IIO_CHAN_INFO_PROCESSED)),
|
||||
SC27XX_ADC_CHANNEL(5, BIT(IIO_CHAN_INFO_PROCESSED)),
|
||||
SC27XX_ADC_CHANNEL(6, BIT(IIO_CHAN_INFO_PROCESSED)),
|
||||
SC27XX_ADC_CHANNEL(7, BIT(IIO_CHAN_INFO_PROCESSED)),
|
||||
SC27XX_ADC_CHANNEL(8, BIT(IIO_CHAN_INFO_PROCESSED)),
|
||||
SC27XX_ADC_CHANNEL(9, BIT(IIO_CHAN_INFO_PROCESSED)),
|
||||
SC27XX_ADC_CHANNEL(10, BIT(IIO_CHAN_INFO_PROCESSED)),
|
||||
SC27XX_ADC_CHANNEL(11, BIT(IIO_CHAN_INFO_PROCESSED)),
|
||||
SC27XX_ADC_CHANNEL(12, BIT(IIO_CHAN_INFO_PROCESSED)),
|
||||
SC27XX_ADC_CHANNEL(13, BIT(IIO_CHAN_INFO_PROCESSED)),
|
||||
SC27XX_ADC_CHANNEL(14, BIT(IIO_CHAN_INFO_PROCESSED)),
|
||||
SC27XX_ADC_CHANNEL(15, BIT(IIO_CHAN_INFO_PROCESSED)),
|
||||
SC27XX_ADC_CHANNEL(16, BIT(IIO_CHAN_INFO_PROCESSED)),
|
||||
SC27XX_ADC_CHANNEL(17, BIT(IIO_CHAN_INFO_PROCESSED)),
|
||||
SC27XX_ADC_CHANNEL(18, BIT(IIO_CHAN_INFO_PROCESSED)),
|
||||
SC27XX_ADC_CHANNEL(19, BIT(IIO_CHAN_INFO_PROCESSED)),
|
||||
SC27XX_ADC_CHANNEL(20, BIT(IIO_CHAN_INFO_RAW)),
|
||||
SC27XX_ADC_CHANNEL(21, BIT(IIO_CHAN_INFO_PROCESSED)),
|
||||
SC27XX_ADC_CHANNEL(22, BIT(IIO_CHAN_INFO_PROCESSED)),
|
||||
SC27XX_ADC_CHANNEL(23, BIT(IIO_CHAN_INFO_PROCESSED)),
|
||||
SC27XX_ADC_CHANNEL(24, BIT(IIO_CHAN_INFO_PROCESSED)),
|
||||
SC27XX_ADC_CHANNEL(25, BIT(IIO_CHAN_INFO_PROCESSED)),
|
||||
SC27XX_ADC_CHANNEL(26, BIT(IIO_CHAN_INFO_PROCESSED)),
|
||||
SC27XX_ADC_CHANNEL(27, BIT(IIO_CHAN_INFO_PROCESSED)),
|
||||
SC27XX_ADC_CHANNEL(28, BIT(IIO_CHAN_INFO_PROCESSED)),
|
||||
SC27XX_ADC_CHANNEL(29, BIT(IIO_CHAN_INFO_PROCESSED)),
|
||||
SC27XX_ADC_CHANNEL(30, BIT(IIO_CHAN_INFO_PROCESSED)),
|
||||
SC27XX_ADC_CHANNEL(31, BIT(IIO_CHAN_INFO_PROCESSED)),
|
||||
};
|
||||
|
||||
static int sc27xx_adc_enable(struct sc27xx_adc_data *data)
|
||||
@@ -380,6 +449,15 @@ static int sc27xx_adc_enable(struct sc27xx_adc_data *data)
|
||||
if (ret)
|
||||
goto disable_clk;
|
||||
|
||||
/* ADC channel scales' calibration from nvmem device */
|
||||
ret = sc27xx_adc_scale_calibration(data, true);
|
||||
if (ret)
|
||||
goto disable_clk;
|
||||
|
||||
ret = sc27xx_adc_scale_calibration(data, false);
|
||||
if (ret)
|
||||
goto disable_clk;
|
||||
|
||||
return 0;
|
||||
|
||||
disable_clk:
|
||||
|
@@ -51,7 +51,7 @@
|
||||
|
||||
struct ti_ads7950_state {
|
||||
struct spi_device *spi;
|
||||
struct spi_transfer ring_xfer[TI_ADS7950_MAX_CHAN + 2];
|
||||
struct spi_transfer ring_xfer;
|
||||
struct spi_transfer scan_single_xfer[3];
|
||||
struct spi_message ring_msg;
|
||||
struct spi_message scan_single_msg;
|
||||
@@ -65,11 +65,11 @@ struct ti_ads7950_state {
|
||||
* DMA (thus cache coherency maintenance) requires the
|
||||
* transfer buffers to live in their own cache lines.
|
||||
*/
|
||||
__be16 rx_buf[TI_ADS7950_MAX_CHAN + TI_ADS7950_TIMESTAMP_SIZE]
|
||||
u16 rx_buf[TI_ADS7950_MAX_CHAN + 2 + TI_ADS7950_TIMESTAMP_SIZE]
|
||||
____cacheline_aligned;
|
||||
__be16 tx_buf[TI_ADS7950_MAX_CHAN];
|
||||
__be16 single_tx;
|
||||
__be16 single_rx;
|
||||
u16 tx_buf[TI_ADS7950_MAX_CHAN + 2];
|
||||
u16 single_tx;
|
||||
u16 single_rx;
|
||||
|
||||
};
|
||||
|
||||
@@ -108,7 +108,7 @@ enum ti_ads7950_id {
|
||||
.realbits = bits, \
|
||||
.storagebits = 16, \
|
||||
.shift = 12 - (bits), \
|
||||
.endianness = IIO_BE, \
|
||||
.endianness = IIO_CPU, \
|
||||
}, \
|
||||
}
|
||||
|
||||
@@ -249,23 +249,14 @@ static int ti_ads7950_update_scan_mode(struct iio_dev *indio_dev,
|
||||
len = 0;
|
||||
for_each_set_bit(i, active_scan_mask, indio_dev->num_channels) {
|
||||
cmd = TI_ADS7950_CR_WRITE | TI_ADS7950_CR_CHAN(i) | st->settings;
|
||||
st->tx_buf[len++] = cpu_to_be16(cmd);
|
||||
st->tx_buf[len++] = cmd;
|
||||
}
|
||||
|
||||
/* Data for the 1st channel is not returned until the 3rd transfer */
|
||||
len += 2;
|
||||
for (i = 0; i < len; i++) {
|
||||
if ((i + 2) < len)
|
||||
st->ring_xfer[i].tx_buf = &st->tx_buf[i];
|
||||
if (i >= 2)
|
||||
st->ring_xfer[i].rx_buf = &st->rx_buf[i - 2];
|
||||
st->ring_xfer[i].len = 2;
|
||||
st->ring_xfer[i].cs_change = 1;
|
||||
}
|
||||
/* make sure last transfer's cs_change is not set */
|
||||
st->ring_xfer[len - 1].cs_change = 0;
|
||||
st->tx_buf[len++] = 0;
|
||||
st->tx_buf[len++] = 0;
|
||||
|
||||
spi_message_init_with_transfers(&st->ring_msg, st->ring_xfer, len);
|
||||
st->ring_xfer.len = len * 2;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -281,7 +272,7 @@ static irqreturn_t ti_ads7950_trigger_handler(int irq, void *p)
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
iio_push_to_buffers_with_timestamp(indio_dev, st->rx_buf,
|
||||
iio_push_to_buffers_with_timestamp(indio_dev, &st->rx_buf[2],
|
||||
iio_get_time_ns(indio_dev));
|
||||
|
||||
out:
|
||||
@@ -298,13 +289,13 @@ static int ti_ads7950_scan_direct(struct iio_dev *indio_dev, unsigned int ch)
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
|
||||
cmd = TI_ADS7950_CR_WRITE | TI_ADS7950_CR_CHAN(ch) | st->settings;
|
||||
st->single_tx = cpu_to_be16(cmd);
|
||||
st->single_tx = cmd;
|
||||
|
||||
ret = spi_sync(st->spi, &st->scan_single_msg);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
ret = be16_to_cpu(st->single_rx);
|
||||
ret = st->single_rx;
|
||||
|
||||
out:
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
@@ -378,6 +369,14 @@ static int ti_ads7950_probe(struct spi_device *spi)
|
||||
const struct ti_ads7950_chip_info *info;
|
||||
int ret;
|
||||
|
||||
spi->bits_per_word = 16;
|
||||
spi->mode |= SPI_CS_WORD;
|
||||
ret = spi_setup(spi);
|
||||
if (ret < 0) {
|
||||
dev_err(&spi->dev, "Error in spi setup\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
@@ -398,6 +397,16 @@ static int ti_ads7950_probe(struct spi_device *spi)
|
||||
indio_dev->num_channels = info->num_channels;
|
||||
indio_dev->info = &ti_ads7950_info;
|
||||
|
||||
/* build spi ring message */
|
||||
spi_message_init(&st->ring_msg);
|
||||
|
||||
st->ring_xfer.tx_buf = &st->tx_buf[0];
|
||||
st->ring_xfer.rx_buf = &st->rx_buf[0];
|
||||
/* len will be set later */
|
||||
st->ring_xfer.cs_change = true;
|
||||
|
||||
spi_message_add_tail(&st->ring_xfer, &st->ring_msg);
|
||||
|
||||
/*
|
||||
* Setup default message. The sample is read at the end of the first
|
||||
* transfer, then it takes one full cycle to convert the sample and one
|
||||
|
Reference in New Issue
Block a user