iio: adc: add driver for TADC

TADC (Telemetry ADC) is a new ADC hardware peripheral. This driver
implements an iio device using the iio framework. Raw channel data is
converted upon request into a human readable format.

Change-Id: Ie99dfe41d8f50f82097c45cd760bf8352c635381
Signed-off-by: Nicholas Troast <ntroast@codeaurora.org>
This commit is contained in:
Nicholas Troast 2016-05-27 16:26:28 -07:00
parent c605e110ab
commit 7106a3f1a8
4 changed files with 896 additions and 0 deletions

View file

@ -0,0 +1,141 @@
Qualcomm Technologies, Inc. TADC Specific Bindings
TADC (Telemetry ADC) is a 10 bit resolution ADC which has 8 channels: battery
temperature, skin temperature, die temperature, battery current, battery
voltage, input current, input voltage, and OTG current.
=======================
Required Node Structure
=======================
A TADC must be described in two levels of devices nodes.
=======================
First Level Node - TADC
=======================
- reg
Usage: required
Value type: <prop-encoded-array>
Definition: Address and size of the TADC register block.
TADC specific properties:
- compatible
Usage: required
Value type: <string>
Definition: Must be "qcom,tadc".
- interrupts
Usage: required
Value type: <prop-encoded-array>
Definition: Peripheral interrupt specifier.
- interrupt-names
Usage: required
Value type: <stringlist>
Definition: Interrupt names. This list must match up 1-to-1 with the
interrupts specified in the 'interrupts' property.
=============================================
Second Level Nodes - TADC Thermistor Channels
=============================================
- reg
Usage: required
Value type: <u32>
Definition: The 0 based channel number.
TADC thermistor channel specific properties:
- qcom,rbias
Usage: required
Value type: <u32>
Definition: The bias resistor value.
- qcom,therm-at-25degc
Usage: required
Value type: <u32>
Definition: The thermistor resistance at 25 DegC.
- qcom,beta-coefficient
Usage: required
Value type: <u32>
Definition: The beta coefficeent or B-parameter of the thermistor.
===============================================
Second Level Nodes - TADC Scale/Offset Channels
===============================================
- reg
Usage: required
Value type: <u32>
Definition: The 0 based channel number.
TADC scale/offset channel specific properties:
- qcom,scale
Usage: required
Value type: <s32>
Definition: The RAW scaling factor.
- qcom,offset
Usage: optional
Value type: <s32>
Definition: The offset after scaling.
=======
Example
=======
smb138x_tadc: qcom,tadc@3600 {
compatible = "qcom,tadc";
#address-cells = <1>;
#size-cells = <0>;
#io-channel-cells = <1>;
interrupts = <0x36 0x0 IRQ_TYPE_EDGE_BOTH>;
interrupt-names = "eoc";
batt_temp@0 {
reg = <0>;
qcom,rbias = <68100>;
qcom,rtherm-at-25degc = <68000>;
qcom,beta-coefficient = <3450>;
};
skin_temp@1 {
reg = <1>;
qcom,rbias = <33000>;
qcom,rtherm-at-25degc = <68000>;
qcom,beta-coefficient = <3450>;
};
die_temp@2 {
reg = <2>;
qcom,scale = <(-1032)>;
qcom,offset = <344125>;
};
batt_i@3 {
reg = <3>;
qcom,channel = <3>;
qcom,scale = <20000000>;
};
batt_v@4 {
reg = <4>;
qcom,scale = <5000000>;
};
input_i@5 {
reg = <5>;
qcom,scale = <14285714>;
};
input_v@6 {
reg = <6>;
qcom,scale = <25000000>;
};
otg_i@7 {
reg = <7>;
qcom,scale = <5714286>;
};
};

View file

@ -318,6 +318,18 @@ config QCOM_RRADC
To compile this driver as a module, choose M here: the module will
be called qcom-rradc.
config QCOM_TADC
tristate "Qualcomm Technologies Inc. TADC driver"
depends on MFD_I2C_PMIC
help
Say yes here to support the Qualcomm Technologies Inc. telemetry ADC.
The TADC provides battery temperature, skin temperature,
die temperature, battery voltage, battery current, input voltage,
input current, and OTG current.
The driver can also be built as a module. If so, the module will be
called qcom-tadc.
config ROCKCHIP_SARADC
tristate "Rockchip SARADC driver"
depends on ARCH_ROCKCHIP || (ARM && COMPILE_TEST)

View file

@ -30,6 +30,7 @@ obj-$(CONFIG_NAU7802) += nau7802.o
obj-$(CONFIG_QCOM_SPMI_IADC) += qcom-spmi-iadc.o
obj-$(CONFIG_QCOM_SPMI_VADC) += qcom-spmi-vadc.o
obj-$(CONFIG_QCOM_RRADC) += qcom-rradc.o
obj-$(CONFIG_QCOM_TADC) += qcom-tadc.o
obj-$(CONFIG_ROCKCHIP_SARADC) += rockchip_saradc.o
obj-$(CONFIG_TI_ADC081C) += ti-adc081c.o
obj-$(CONFIG_TI_ADC128S052) += ti-adc128s052.o

742
drivers/iio/adc/qcom-tadc.c Normal file
View file

@ -0,0 +1,742 @@
/* Copyright (c) 2016, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation.
*
* 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/iio/iio.h>
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/of_irq.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#define TADC_REVISION1_REG 0x00
#define TADC_REVISION2_REG 0x01
#define TADC_REVISION3_REG 0x02
#define TADC_REVISION4_REG 0x03
#define TADC_PERPH_TYPE_REG 0x04
#define TADC_PERPH_SUBTYPE_REG 0x05
/* TADC register definitions */
#define TADC_SW_CH_CONV_REG(chip) (chip->tadc_base + 0x06)
#define TADC_MBG_ERR_REG(chip) (chip->tadc_base + 0x07)
#define TADC_EN_CTL_REG(chip) (chip->tadc_base + 0x46)
#define TADC_CONV_REQ_REG(chip) (chip->tadc_base + 0x51)
#define TADC_HWTRIG_CONV_CH_EN_REG(chip) (chip->tadc_base + 0x52)
#define TADC_HW_SETTLE_DELAY_REG(chip) (chip->tadc_base + 0x53)
#define TADC_LONG_HW_SETTLE_DLY_EN_REG(chip) (chip->tadc_base + 0x54)
#define TADC_LONG_HW_SETTLE_DLY_REG(chip) (chip->tadc_base + 0x55)
#define TADC_ADC_BUF_CH_REG(chip) (chip->tadc_base + 0x56)
#define TADC_ADC_AAF_CH_REG(chip) (chip->tadc_base + 0x57)
#define TADC_ADC_DATA_RDBK_REG(chip) (chip->tadc_base + 0x58)
#define TADC_CH1_ADC_LO_REG(chip) (chip->tadc_base + 0x60)
#define TADC_CH1_ADC_HI_REG(chip) (chip->tadc_base + 0x61)
#define TADC_CH2_ADC_LO_REG(chip) (chip->tadc_base + 0x62)
#define TADC_CH2_ADC_HI_REG(chip) (chip->tadc_base + 0x63)
#define TADC_CH3_ADC_LO_REG(chip) (chip->tadc_base + 0x64)
#define TADC_CH3_ADC_HI_REG(chip) (chip->tadc_base + 0x65)
#define TADC_CH4_ADC_LO_REG(chip) (chip->tadc_base + 0x66)
#define TADC_CH4_ADC_HI_REG(chip) (chip->tadc_base + 0x67)
#define TADC_CH5_ADC_LO_REG(chip) (chip->tadc_base + 0x68)
#define TADC_CH5_ADC_HI_REG(chip) (chip->tadc_base + 0x69)
#define TADC_CH6_ADC_LO_REG(chip) (chip->tadc_base + 0x70)
#define TADC_CH6_ADC_HI_REG(chip) (chip->tadc_base + 0x71)
#define TADC_CH7_ADC_LO_REG(chip) (chip->tadc_base + 0x72)
#define TADC_CH7_ADC_HI_REG(chip) (chip->tadc_base + 0x73)
#define TADC_CH8_ADC_LO_REG(chip) (chip->tadc_base + 0x74)
#define TADC_CH8_ADC_HI_REG(chip) (chip->tadc_base + 0x75)
/* TADC_CMP register definitions */
#define TADC_CMP_THR1_CMP_REG(chip) (chip->tadc_cmp_base + 0x51)
#define TADC_CMP_THR1_CH1_CMP_LO_REG(chip) (chip->tadc_cmp_base + 0x52)
#define TADC_CMP_THR1_CH1_CMP_HI_REG(chip) (chip->tadc_cmp_base + 0x53)
#define TADC_CMP_THR1_CH2_CMP_LO_REG(chip) (chip->tadc_cmp_base + 0x54)
#define TADC_CMP_THR1_CH2_CMP_HI_REG(chip) (chip->tadc_cmp_base + 0x55)
#define TADC_CMP_THR1_CH3_CMP_LO_REG(chip) (chip->tadc_cmp_base + 0x56)
#define TADC_CMP_THR1_CH3_CMP_HI_REG(chip) (chip->tadc_cmp_base + 0x57)
#define TADC_CMP_THR2_CMP_REG(chip) (chip->tadc_cmp_base + 0x67)
#define TADC_CMP_THR2_CH1_CMP_LO_REG(chip) (chip->tadc_cmp_base + 0x68)
#define TADC_CMP_THR2_CH1_CMP_HI_REG(chip) (chip->tadc_cmp_base + 0x69)
#define TADC_CMP_THR2_CH2_CMP_LO_REG(chip) (chip->tadc_cmp_base + 0x6A)
#define TADC_CMP_THR2_CH2_CMP_HI_REG(chip) (chip->tadc_cmp_base + 0x6B)
#define TADC_CMP_THR2_CH3_CMP_LO_REG(chip) (chip->tadc_cmp_base + 0x6C)
#define TADC_CMP_THR2_CH3_CMP_HI_REG(chip) (chip->tadc_cmp_base + 0x6D)
#define TADC_CMP_THR3_CMP_REG(chip) (chip->tadc_cmp_base + 0x7D)
#define TADC_CMP_THR3_CH1_CMP_LO_REG(chip) (chip->tadc_cmp_base + 0x7E)
#define TADC_CMP_THR3_CH1_CMP_HI_REG(chip) (chip->tadc_cmp_base + 0x7F)
#define TADC_CMP_THR3_CH2_CMP_LO_REG(chip) (chip->tadc_cmp_base + 0x80)
#define TADC_CMP_THR3_CH2_CMP_HI_REG(chip) (chip->tadc_cmp_base + 0x81)
#define TADC_CMP_THR3_CH3_CMP_LO_REG(chip) (chip->tadc_cmp_base + 0x82)
#define TADC_CMP_THR3_CH3_CMP_HI_REG(chip) (chip->tadc_cmp_base + 0x83)
#define TADC_CMP_THR4_CMP_REG(chip) (chip->tadc_cmp_base + 0x93)
#define TADC_CMP_THR4_CH1_CMP_LO_REG(chip) (chip->tadc_cmp_base + 0x94)
#define TADC_CMP_THR4_CH1_CMP_HI_REG(chip) (chip->tadc_cmp_base + 0x95)
#define TADC_CMP_THR1_CH1_HYST_REG(chip) (chip->tadc_cmp_base + 0xB0)
#define TADC_CMP_THR2_CH1_HYST_REG(chip) (chip->tadc_cmp_base + 0xB1)
#define TADC_CMP_THR3_CH1_HYST_REG(chip) (chip->tadc_cmp_base + 0xB2)
#define TADC_CMP_THR4_CH1_HYST_REG(chip) (chip->tadc_cmp_base + 0xB3)
/* 10 bits of resolution */
#define TADC_RESOLUTION 1024
/* number of hardware channels */
#define TADC_NUM_CH 8
enum tadc_chan_id {
TADC_THERM1 = 0,
TADC_THERM2,
TADC_DIE_TEMP,
TADC_BATT_I,
TADC_BATT_V,
TADC_INPUT_I,
TADC_INPUT_V,
TADC_OTG_I,
/* virtual channels */
TADC_BATT_P,
TADC_INPUT_P,
TADC_THERM1_THR1,
TADC_THERM2_THR1,
TADC_DIE_TEMP_THR1,
};
#define TADC_CHAN(_name, _type, _channel, _info_mask) \
{ \
.type = _type, \
.channel = _channel, \
.info_mask_separate = _info_mask, \
.extend_name = _name, \
}
#define TADC_THERM_CHAN(_name, _channel) \
TADC_CHAN(_name, IIO_TEMP, _channel, \
BIT(IIO_CHAN_INFO_RAW) | \
BIT(IIO_CHAN_INFO_PROCESSED))
#define TADC_TEMP_CHAN(_name, _channel) \
TADC_CHAN(_name, IIO_TEMP, _channel, \
BIT(IIO_CHAN_INFO_RAW) | \
BIT(IIO_CHAN_INFO_PROCESSED) | \
BIT(IIO_CHAN_INFO_SCALE) | \
BIT(IIO_CHAN_INFO_OFFSET))
#define TADC_CURRENT_CHAN(_name, _channel) \
TADC_CHAN(_name, IIO_CURRENT, _channel, \
BIT(IIO_CHAN_INFO_RAW) | \
BIT(IIO_CHAN_INFO_PROCESSED) | \
BIT(IIO_CHAN_INFO_SCALE))
#define TADC_VOLTAGE_CHAN(_name, _channel) \
TADC_CHAN(_name, IIO_VOLTAGE, _channel, \
BIT(IIO_CHAN_INFO_RAW) | \
BIT(IIO_CHAN_INFO_PROCESSED) | \
BIT(IIO_CHAN_INFO_SCALE))
#define TADC_POWER_CHAN(_name, _channel) \
TADC_CHAN(_name, IIO_POWER, _channel, \
BIT(IIO_CHAN_INFO_PROCESSED))
static const struct iio_chan_spec tadc_iio_chans[] = {
[TADC_THERM1] = TADC_THERM_CHAN(
"batt", TADC_THERM1),
[TADC_THERM2] = TADC_THERM_CHAN(
"skin", TADC_THERM2),
[TADC_DIE_TEMP] = TADC_TEMP_CHAN(
"die", TADC_DIE_TEMP),
[TADC_BATT_I] = TADC_CURRENT_CHAN(
"batt", TADC_BATT_I),
[TADC_BATT_V] = TADC_VOLTAGE_CHAN(
"batt", TADC_BATT_V),
[TADC_INPUT_I] = TADC_CURRENT_CHAN(
"input", TADC_INPUT_I),
[TADC_INPUT_V] = TADC_VOLTAGE_CHAN(
"input", TADC_INPUT_V),
[TADC_OTG_I] = TADC_CURRENT_CHAN(
"otg", TADC_OTG_I),
[TADC_BATT_P] = TADC_POWER_CHAN(
"batt", TADC_BATT_P),
[TADC_INPUT_P] = TADC_POWER_CHAN(
"input", TADC_INPUT_P),
[TADC_THERM1_THR1] = TADC_THERM_CHAN(
"batt_hot", TADC_THERM1_THR1),
[TADC_THERM2_THR1] = TADC_THERM_CHAN(
"skin_hot", TADC_THERM2_THR1),
[TADC_DIE_TEMP_THR1] = TADC_THERM_CHAN(
"die_hot", TADC_DIE_TEMP_THR1),
};
struct tadc_chan_data {
s32 scale;
s32 offset;
u32 rbias;
const struct tadc_pt *table;
size_t tablesize;
};
struct tadc_chip {
struct device *dev;
struct regmap *regmap;
u32 tadc_base;
u32 tadc_cmp_base;
struct tadc_chan_data chans[TADC_NUM_CH];
struct completion eoc_complete;
};
struct tadc_pt {
s32 x;
s32 y;
};
/*
* Thermistor tables are generated by the B-parameter equation which is a
* simplifed version of the Steinhart-Hart equation.
*
* (1 / T) = (1 / T0) + (1 / B) * ln(R / R0)
*
* Where R0 is the resistance at temperature T0, and T0 is typically room
* temperature (25C).
*/
static const struct tadc_pt tadc_therm_3450b_68k[] = {
{ 4151, 120000 },
{ 4648, 115000 },
{ 5220, 110000 },
{ 5880, 105000 },
{ 6644, 100000 },
{ 7533, 95000 },
{ 8571, 90000 },
{ 9786, 85000 },
{ 11216, 80000 },
{ 12906, 75000 },
{ 14910, 70000 },
{ 17300, 65000 },
{ 20163, 60000 },
{ 23609, 55000 },
{ 27780, 50000 },
{ 32855, 45000 },
{ 39065, 40000 },
{ 46712, 35000 },
{ 56185, 30000 },
{ 68000, 25000 },
{ 82837, 20000 },
{ 101604, 15000 },
{ 125525, 10000 },
{ 156261, 5000 },
{ 196090, 0 },
{ 248163, -5000 },
{ 316887, -10000 },
{ 408493, -15000 },
{ 531889, -20000 },
{ 699966, -25000 },
{ 931618, -30000 },
{ 1254910, -35000 },
{ 1712127, -40000 },
};
static int tadc_read(struct tadc_chip *chip, u16 reg, u8 *val,
size_t val_count)
{
int rc = 0;
rc = regmap_bulk_read(chip->regmap, reg, val, val_count);
if (rc < 0)
pr_err("Couldn't read %04x rc=%d\n", reg, rc);
return rc;
}
static int tadc_write(struct tadc_chip *chip, u16 reg, u8 data)
{
int rc = 0;
rc = regmap_write(chip->regmap, reg, data);
if (rc < 0)
pr_err("Couldn't write %02x to %04x rc=%d\n",
data, reg, rc);
return rc;
}
static int tadc_lerp(const struct tadc_pt *pts, size_t tablesize, s32 input,
s32 *output)
{
int i;
s64 temp;
if (pts == NULL) {
pr_err("Table is NULL\n");
return -EINVAL;
}
if (tablesize < 1) {
pr_err("Table has no entries\n");
return -ENOENT;
}
if (tablesize == 1) {
*output = pts[0].y;
return 0;
}
if (pts[0].x > pts[1].x) {
pr_err("Table is not in acending order\n");
return -EINVAL;
}
if (input <= pts[0].x) {
*output = pts[0].y;
return 0;
}
if (input >= pts[tablesize - 1].x) {
*output = pts[tablesize - 1].y;
return 0;
}
for (i = 1; i < tablesize; i++)
if (input <= pts[i].x)
break;
temp = (s64)(pts[i].y - pts[i - 1].y) * (s64)(input - pts[i - 1].x);
temp = div_s64(temp, pts[i].x - pts[i - 1].x);
*output = temp + pts[i - 1].y;
return 0;
}
/*
* Process the result of a thermistor reading.
*
* The voltage input to the ADC is a result of a voltage divider circuit.
* Vout = (Rtherm / (Rbias + Rtherm)) * Vbias
*
* The ADC value is based on the output voltage of the voltage divider, and the
* bias voltage.
* ADC = (Vin * 1024) / Vbias
*
* Combine these equations and solve for Rtherm
* Rtherm = (ADC * Rbias) / (1024 - ADC)
*/
static int tadc_process_therm(const struct tadc_chan_data *chan_data,
s16 adc, s32 *result)
{
s64 rtherm;
rtherm = (s64)adc * (s64)chan_data->rbias;
rtherm = div_s64(rtherm, TADC_RESOLUTION - adc);
return tadc_lerp(chan_data->table, chan_data->tablesize, rtherm,
result);
}
static int tadc_read_channel(struct tadc_chip *chip, u16 address, int *adc)
{
u8 val[2];
int rc;
rc = tadc_read(chip, address, val, ARRAY_SIZE(val));
if (rc < 0) {
dev_err(chip->dev, "Couldn't read channel rc=%d\n", rc);
return rc;
}
*adc = (s16)(val[0] | val[1] << BITS_PER_BYTE);
return 0;
}
#define CONVERSION_TIMEOUT_MS 100
static int tadc_do_conversion(struct tadc_chip *chip, u8 channels, s16 *adc)
{
unsigned long timeout, timeleft;
u8 val[TADC_NUM_CH * 2];
int rc, i;
rc = tadc_read(chip, TADC_MBG_ERR_REG(chip), val, 1);
if (rc < 0) {
dev_err(chip->dev, "Couldn't read mbg error status rc=%d\n",
rc);
return rc;
}
if (val[0] != 0) {
tadc_write(chip, TADC_EN_CTL_REG(chip), 0);
tadc_write(chip, TADC_EN_CTL_REG(chip), 0x80);
}
rc = tadc_write(chip, TADC_CONV_REQ_REG(chip), channels);
if (rc < 0) {
dev_err(chip->dev, "Couldn't write conversion request rc=%d\n",
rc);
return rc;
}
timeout = msecs_to_jiffies(CONVERSION_TIMEOUT_MS);
timeleft = wait_for_completion_timeout(&chip->eoc_complete, timeout);
if (timeleft == 0) {
rc = tadc_read(chip, TADC_SW_CH_CONV_REG(chip), val, 1);
if (rc < 0) {
dev_err(chip->dev, "Couldn't read conversion status rc=%d\n",
rc);
return rc;
}
if (val[0] != channels) {
dev_err(chip->dev, "Conversion timed out\n");
return -ETIMEDOUT;
}
}
rc = tadc_read(chip, TADC_CH1_ADC_LO_REG(chip), val, ARRAY_SIZE(val));
if (rc < 0) {
dev_err(chip->dev, "Couldn't read adc channels rc=%d\n",
rc);
return rc;
}
for (i = 0; i < TADC_NUM_CH; i++)
adc[i] = val[i * 2] | val[i * 2 + 1] << BITS_PER_BYTE;
return jiffies_to_msecs(timeout - timeleft);
}
static int tadc_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan, int *val, int *val2,
long mask)
{
struct tadc_chip *chip = iio_priv(indio_dev);
const struct tadc_chan_data *chan_data = &chip->chans[chan->channel];
int rc = 0, offset = 0, scale, scale2, scale_type;
s16 adc[TADC_NUM_CH];
switch (chan->channel) {
case TADC_THERM1_THR1:
chan_data = &chip->chans[TADC_THERM1];
break;
case TADC_THERM2_THR1:
chan_data = &chip->chans[TADC_THERM2];
break;
case TADC_DIE_TEMP_THR1:
chan_data = &chip->chans[TADC_DIE_TEMP];
break;
default:
break;
}
switch (mask) {
case IIO_CHAN_INFO_RAW:
switch (chan->channel) {
case TADC_THERM1_THR1:
rc = tadc_read_channel(chip,
TADC_CMP_THR1_CH1_CMP_LO_REG(chip), val);
if (rc < 0) {
dev_err(chip->dev, "Couldn't read THERM1 threshold rc=%d\n",
rc);
return rc;
}
break;
case TADC_THERM2_THR1:
rc = tadc_read_channel(chip,
TADC_CMP_THR1_CH2_CMP_LO_REG(chip), val);
if (rc < 0) {
dev_err(chip->dev, "Couldn't read THERM2 threshold rc=%d\n",
rc);
return rc;
}
break;
case TADC_DIE_TEMP_THR1:
rc = tadc_read_channel(chip,
TADC_CMP_THR1_CH3_CMP_LO_REG(chip), val);
if (rc < 0) {
dev_err(chip->dev, "Couldn't read DIE_TEMP threshold rc=%d\n",
rc);
return rc;
}
break;
default:
rc = tadc_do_conversion(chip, BIT(chan->channel), adc);
if (rc < 0) {
dev_err(chip->dev, "Couldn't read channel %d\n",
chan->channel);
return rc;
}
*val = adc[chan->channel];
break;
}
return IIO_VAL_INT;
case IIO_CHAN_INFO_PROCESSED:
switch (chan->channel) {
case TADC_THERM1:
case TADC_THERM2:
case TADC_THERM1_THR1:
case TADC_THERM2_THR1:
rc = tadc_read_raw(indio_dev, chan, val, NULL,
IIO_CHAN_INFO_RAW);
if (rc < 0)
return rc;
rc = tadc_process_therm(chan_data, *val, val);
if (rc < 0) {
dev_err(chip->dev, "Couldn't process 0x%04x from channel %d rc=%d\n",
*val, chan->channel, rc);
return rc;
}
break;
case TADC_BATT_P:
rc = tadc_do_conversion(chip,
BIT(TADC_BATT_I) | BIT(TADC_BATT_V), adc);
if (rc < 0) {
dev_err(chip->dev, "Couldn't read battery current and voltage channels\n");
return rc;
}
*val = adc[TADC_BATT_I] * adc[TADC_BATT_V];
break;
case TADC_INPUT_P:
rc = tadc_do_conversion(chip,
BIT(TADC_INPUT_I) | BIT(TADC_INPUT_V), adc);
if (rc < 0) {
dev_err(chip->dev, "Couldn't read input current and voltage channels\n");
return rc;
}
*val = adc[TADC_INPUT_I] * adc[TADC_INPUT_V];
break;
default:
rc = tadc_read_raw(indio_dev, chan, val, NULL,
IIO_CHAN_INFO_RAW);
if (rc < 0)
return rc;
/* offset is optional */
rc = tadc_read_raw(indio_dev, chan, &offset, NULL,
IIO_CHAN_INFO_OFFSET);
if (rc < 0)
return rc;
scale_type = tadc_read_raw(indio_dev, chan,
&scale, &scale2, IIO_CHAN_INFO_SCALE);
switch (scale_type) {
case IIO_VAL_INT:
*val = *val * scale + offset;
break;
case IIO_VAL_FRACTIONAL:
*val = div_s64((s64)*val * scale + offset,
scale2);
break;
default:
return -EINVAL;
}
break;
}
return IIO_VAL_INT;
case IIO_CHAN_INFO_SCALE:
switch (chan->channel) {
case TADC_DIE_TEMP:
case TADC_DIE_TEMP_THR1:
*val = chan_data->scale;
return IIO_VAL_INT;
case TADC_BATT_I:
case TADC_BATT_V:
case TADC_INPUT_I:
case TADC_INPUT_V:
case TADC_OTG_I:
*val = chan_data->scale;
*val2 = TADC_RESOLUTION;
return IIO_VAL_FRACTIONAL;
}
return -EINVAL;
case IIO_CHAN_INFO_OFFSET:
*val = chan_data->offset;
return IIO_VAL_INT;
}
return -EINVAL;
}
static irqreturn_t handle_eoc(int irq, void *dev_id)
{
struct tadc_chip *chip = dev_id;
complete(&chip->eoc_complete);
return IRQ_HANDLED;
}
static int tadc_set_therm_table(struct tadc_chan_data *chan_data, u32 beta,
u32 rtherm)
{
if (beta == 3450 && rtherm == 68000) {
chan_data->table = tadc_therm_3450b_68k;
chan_data->tablesize = ARRAY_SIZE(tadc_therm_3450b_68k);
return 0;
}
return -ENOENT;
}
static int tadc_parse_dt(struct tadc_chip *chip)
{
struct device_node *child, *node;
struct tadc_chan_data *chan_data;
u32 chan_id, rtherm, beta;
int rc = 0;
node = chip->dev->of_node;
for_each_available_child_of_node(node, child) {
rc = of_property_read_u32(child, "reg", &chan_id);
if (rc < 0) {
dev_err(chip->dev, "Couldn't find channel for %s rc=%d",
child->name, rc);
return rc;
}
if (chan_id > TADC_NUM_CH - 1) {
dev_err(chip->dev, "Channel %d is out of range [0, %d]\n",
chan_id, TADC_NUM_CH - 1);
return -EINVAL;
}
chan_data = &chip->chans[chan_id];
switch (chan_id) {
case TADC_THERM1:
case TADC_THERM2:
rc = of_property_read_u32(child,
"qcom,rbias", &chan_data->rbias);
if (rc < 0) {
dev_err(chip->dev, "Couldn't read qcom,rbias rc=%d\n",
rc);
return rc;
}
rc = of_property_read_u32(child,
"qcom,beta-coefficient", &beta);
if (rc < 0) {
dev_err(chip->dev, "Couldn't read qcom,beta-coefficient rc=%d\n",
rc);
return rc;
}
rc = of_property_read_u32(child,
"qcom,rtherm-at-25degc", &rtherm);
if (rc < 0) {
dev_err(chip->dev, "Couldn't read qcom,rtherm-at-25degc rc=%d\n",
rc);
return rc;
}
rc = tadc_set_therm_table(chan_data, beta, rtherm);
if (rc < 0) {
dev_err(chip->dev, "Couldn't set therm table rc=%d\n",
rc);
return rc;
}
break;
default:
rc = of_property_read_s32(child, "qcom,scale",
&chan_data->scale);
if (rc < 0) {
dev_err(chip->dev, "Couldn't read scale rc=%d\n",
rc);
return rc;
}
of_property_read_s32(child, "qcom,offset",
&chan_data->offset);
break;
}
}
return rc;
}
static const struct iio_info tadc_info = {
.read_raw = &tadc_read_raw,
.driver_module = THIS_MODULE,
};
static int tadc_probe(struct platform_device *pdev)
{
struct device_node *node = pdev->dev.of_node;
struct iio_dev *indio_dev;
struct tadc_chip *chip;
int rc = 0, irq;
indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*chip));
if (!indio_dev)
return -ENOMEM;
chip = iio_priv(indio_dev);
chip->dev = &pdev->dev;
init_completion(&chip->eoc_complete);
rc = of_property_read_u32(node, "reg", &chip->tadc_base);
if (rc < 0) {
dev_err(chip->dev, "Couldn't read base address rc=%d\n", rc);
return rc;
}
chip->tadc_cmp_base = chip->tadc_base + 0x100;
chip->regmap = dev_get_regmap(chip->dev->parent, NULL);
if (!chip->regmap) {
pr_err("Couldn't get regmap\n");
return -ENODEV;
}
rc = tadc_parse_dt(chip);
if (rc < 0) {
pr_err("Couldn't parse device tree rc=%d\n", rc);
return rc;
}
irq = of_irq_get_byname(node, "eoc");
if (irq < 0) {
pr_err("Couldn't get eoc irq rc=%d\n", irq);
return irq;
}
rc = devm_request_threaded_irq(chip->dev, irq, NULL, handle_eoc,
IRQF_ONESHOT, "eoc", chip);
if (rc < 0) {
pr_err("Couldn't request irq %d rc=%d\n", irq, rc);
return rc;
}
indio_dev->dev.parent = chip->dev;
indio_dev->name = pdev->name;
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->info = &tadc_info;
indio_dev->channels = tadc_iio_chans;
indio_dev->num_channels = ARRAY_SIZE(tadc_iio_chans);
rc = devm_iio_device_register(chip->dev, indio_dev);
if (rc < 0)
dev_err(chip->dev, "Couldn't register IIO device rc=%d\n", rc);
return rc;
}
static int tadc_remove(struct platform_device *pdev)
{
return 0;
}
static const struct of_device_id tadc_match_table[] = {
{ .compatible = "qcom,tadc" },
{ }
};
MODULE_DEVICE_TABLE(of, tadc_match_table);
static struct platform_driver tadc_driver = {
.driver = {
.name = "qcom-tadc",
.of_match_table = tadc_match_table,
},
.probe = tadc_probe,
.remove = tadc_remove,
};
module_platform_driver(tadc_driver);
MODULE_DESCRIPTION("Qualcomm Technologies Inc. TADC driver");
MODULE_LICENSE("GPL v2");