hwmon: qpnp-adc: Add PMIC QPNP VADC Driver

The QPNP VADC driver supports the User Bank Peripheral of
the voltage ADC(VADC).

VADC is a 15 bit ADC that measures signals through the
Main analog multiplexer (AMUX) and PREMUX. The driver
arbitrates the request to issue ADC read requests.

VADC driver includes support for the conversion sequencer.
The conversion sequencer is a HW triggered signal to start
ADC measurement on trigger events for PA ON,
camera flash and TX threshold.

The AMUX supports external pull-ups simultaneously. Clients
can select the appropriate AMUX input channel to measure the
ADC for the intended pull up configuration.

Change-Id: I8886968ccec54ad03334b113b4516d4d200e0da8
Signed-off-by: Siddartha Mohanadoss <smohanad@codeaurora.org>
This commit is contained in:
Siddartha Mohanadoss 2012-06-05 23:27:46 -07:00 committed by Stephen Boyd
parent 27362bd421
commit 7cdb79523b
6 changed files with 1840 additions and 0 deletions

View file

@ -0,0 +1,98 @@
Qualcomm's QPNP PMIC Voltage ADC Arbiter
QPNP PMIC Voltage ADC (VADC) provides interface to clients to read
Voltage. A 15 bit ADC is used for Voltage measurements. There are multiple
peripherals to the VADC and the scope of the driver is to provide interface
for the USR peripheral of the VADC.
VADC node
Required properties:
- compatible : should be "qcom,qpnp-vadc" for Voltage ADC driver.
- reg : offset and length of the PMIC Aribter register map.
- interrupts : The USR bank peripheral VADC interrupt.
- qcom,adc-bit-resolution : Bit resolution of the ADC.
- qcom,adc-vdd-reference : Voltage reference used by the ADC.
Channel nodes
NOTE: Atleast one Channel node is required.
Required properties:
- label : Channel name used for sysfs entry.
- qcom,channel-num : Channel number associated to the AMUX input.
- qcom,decimation : Sampling rate to use for the individual channel measurement.
Select from following unsigned int.
0 : 512
1 : 1K
2 : 2K
3 : 4K
- qcom,pre-div-channel-scaling : Pre-div used for the channel before the signal
is being measured.
- qcom,calibration-type : Reference voltage to use for channel calibration.
Channel calibration is dependendent on the channel.
Certain channels like XO_THERM, BATT_THERM use ratiometric
calibration. Most other channels fall under absolute calibration.
Select from the following strings.
"absolute" : Uses the 625mv and 1.25V reference channels.
"ratiometric" : Uses the reference Voltage/GND for calibration.
- qcom,scale-function : Scaling function used to convert raw ADC code to units specific to
a given channel.
Select from the following unsigned int.
0 : Default scaling to convert raw adc code to voltage.
1 : Conversion to temperature based on btm parameters.
2 : Returns result in milli degree's Centigrade.
3 : Returns current across 0.1 ohm resistor.
4 : Returns XO thermistor voltage in degree's Centigrade.
- qcom,hw-settle-time : Settling period for the channel before ADC read.
Select from the following unsigned int.
0 : 0us
1 : 100us
2 : 200us
3 : 300us
4 : 400us
5 : 500us
6 : 600us
7 : 700us
8 : 800us
9 : 900us
0xa : 1ms
0xb : 2ms
0xc : 4ms
0xd : 6ms
0xe : 8ms
0xf : 10ms
- qcom,fast-avg-setup : Average number of samples to be used for measurement. Fast averaging
provides the option to obtain a single measurement from the ADC that
is an average of multiple samples. The value selected is 2^(value)
Select from the following unsigned int.
0 : 1
1 : 2
2 : 4
3 : 8
4 : 16
5 : 32
6 : 64
7 : 128
8 : 256
Example:
/* Main Node */
qcom,vadc@3100 {
compatible = "qcom,qpnp-vadc";
reg = <0x3100 0x100>;
interrupts = <0x0 0x31 0x0>;
qcom,adc-bit-resolution = <15>;
qcom,adc-vdd-reference = <1800>;
/* Channel Node */
chan@0 {
label = "usb_in";
qcom,channel-num = <0>;
qcom,decimation = <0>;
qcom,pre-div-channel-scaling = <20>;
qcom,calibration-type = "absolute";
qcom,scale-function = <0>;
qcom,hw-settle-time = <0>;
qcom,fast-avg-setup = <0>;
};
};

View file

@ -986,6 +986,16 @@ config SENSORS_EPM_ADC
Provides interface for measuring the current on specific power rails
through the channels on ADC1158 ADC
config SENSORS_QPNP_ADC_VOLTAGE
tristate "Support for Qualcomm QPNP Voltage ADC"
depends on SPMI
help
This is the VADC arbiter driver for Qualcomm QPNP ADC Chip.
The driver supports reading the HKADC, XOADC through the ADC AMUX arbiter.
The VADC includes support for the conversion sequencer. The driver supports
reading the ADC through the AMUX channels for external pull-ups simultaneously.
config SENSORS_PC87360
tristate "National Semiconductor PC87360 family"
depends on !PPC

View file

@ -144,6 +144,7 @@ obj-$(CONFIG_SENSORS_WPCE775X) += wpce775x.o
obj-$(CONFIG_SENSORS_MSM_ADC) += msm_adc.o m_adcproc.o
obj-$(CONFIG_SENSORS_PM8XXX_ADC) += pm8xxx-adc.o pm8xxx-adc-scale.o
obj-$(CONFIG_SENSORS_EPM_ADC) += epm_adc.o
obj-$(CONFIG_SENSORS_QPNP_ADC_VOLTAGE) += qpnp-adc-voltage.o qpnp-adc-common.o
obj-$(CONFIG_PMBUS) += pmbus/

View file

@ -0,0 +1,258 @@
/* Copyright (c) 2012, 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.
*/
#define pr_fmt(fmt) "%s: " fmt, __func__
#include <linux/kernel.h>
#include <linux/of.h>
#include <linux/err.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/mutex.h>
#include <linux/types.h>
#include <linux/hwmon.h>
#include <linux/module.h>
#include <linux/debugfs.h>
#include <linux/spmi.h>
#include <linux/of_irq.h>
#include <linux/interrupt.h>
#include <linux/qpnp/qpnp-adc.h>
#include <linux/platform_device.h>
/* Min ADC code represets 0V */
#define QPNP_VADC_MIN_ADC_CODE 0x6000
/* Max ADC code represents full-scale range of 1.8V */
#define QPNP_VADC_MAX_ADC_CODE 0xA800
int32_t qpnp_adc_scale_default(int32_t adc_code,
const struct qpnp_adc_properties *adc_properties,
const struct qpnp_vadc_chan_properties *chan_properties,
struct qpnp_vadc_result *adc_chan_result)
{
bool negative_rawfromoffset = 0, negative_offset = 0;
int64_t scale_voltage = 0;
if (!chan_properties || !chan_properties->offset_gain_numerator ||
!chan_properties->offset_gain_denominator || !adc_properties
|| !adc_chan_result)
return -EINVAL;
scale_voltage = (adc_code -
chan_properties->adc_graph[CALIB_ABSOLUTE].adc_gnd)
* chan_properties->adc_graph[CALIB_ABSOLUTE].dx;
if (scale_voltage < 0) {
negative_offset = 1;
scale_voltage = -scale_voltage;
}
do_div(scale_voltage,
chan_properties->adc_graph[CALIB_ABSOLUTE].dy);
if (negative_offset)
scale_voltage = -scale_voltage;
scale_voltage += chan_properties->adc_graph[CALIB_ABSOLUTE].dx;
if (scale_voltage < 0) {
if (adc_properties->bipolar) {
scale_voltage = -scale_voltage;
negative_rawfromoffset = 1;
} else {
scale_voltage = 0;
}
}
adc_chan_result->measurement = scale_voltage *
chan_properties->offset_gain_denominator;
/* do_div only perform positive integer division! */
do_div(adc_chan_result->measurement,
chan_properties->offset_gain_numerator);
if (negative_rawfromoffset)
adc_chan_result->measurement = -adc_chan_result->measurement;
/*
* Note: adc_chan_result->measurement is in the unit of
* adc_properties.adc_reference. For generic channel processing,
* channel measurement is a scale/ratio relative to the adc
* reference input
*/
adc_chan_result->physical = adc_chan_result->measurement;
return 0;
}
EXPORT_SYMBOL_GPL(qpnp_adc_scale_default);
int32_t qpnp_vadc_check_result(int32_t *data)
{
if (*data < QPNP_VADC_MIN_ADC_CODE)
*data = QPNP_VADC_MIN_ADC_CODE;
else if (*data > QPNP_VADC_MAX_ADC_CODE)
*data = QPNP_VADC_MAX_ADC_CODE;
return 0;
}
EXPORT_SYMBOL_GPL(qpnp_vadc_check_result);
int32_t qpnp_adc_get_devicetree_data(struct spmi_device *spmi,
struct qpnp_adc_drv *adc_qpnp)
{
struct device_node *node = spmi->dev.of_node;
struct resource *res;
struct device_node *child;
struct qpnp_vadc_amux *adc_channel_list;
struct qpnp_adc_properties *adc_prop;
struct qpnp_vadc_amux_properties *amux_prop;
int count_adc_channel_list = 0, decimation, rc = 0;
if (!node)
return -EINVAL;
for_each_child_of_node(node, child)
count_adc_channel_list++;
if (!count_adc_channel_list) {
pr_err("No channel listing\n");
return -EINVAL;
}
adc_qpnp->spmi = spmi;
adc_prop = devm_kzalloc(&spmi->dev, sizeof(struct qpnp_adc_properties),
GFP_KERNEL);
if (!adc_prop) {
dev_err(&spmi->dev, "Unable to allocate memory\n");
return -ENOMEM;
}
adc_channel_list = devm_kzalloc(&spmi->dev,
(sizeof(struct qpnp_vadc_amux) * count_adc_channel_list),
GFP_KERNEL);
if (!adc_channel_list) {
dev_err(&spmi->dev, "Unable to allocate memory\n");
return -ENOMEM;
}
amux_prop = devm_kzalloc(&spmi->dev,
sizeof(struct qpnp_vadc_amux_properties) +
sizeof(struct qpnp_vadc_chan_properties), GFP_KERNEL);
if (!amux_prop) {
dev_err(&spmi->dev, "Unable to allocate memory\n");
return -ENOMEM;
}
for_each_child_of_node(node, child) {
int channel_num, scaling, post_scaling, hw_settle_time;
int fast_avg_setup, calib_type, i = 0, rc;
const char *calibration_param, *channel_name;
channel_name = of_get_property(child,
"label", NULL) ? : child->name;
if (!channel_name) {
pr_err("Invalid channel name\n");
return -EINVAL;
}
rc = of_property_read_u32(child, "qcom,channel-num",
&channel_num);
if (rc) {
pr_err("Invalid channel num\n");
return -EINVAL;
}
rc = of_property_read_u32(child, "qcom,decimation",
&decimation);
if (rc) {
pr_err("Invalid channel decimation property\n");
return -EINVAL;
}
rc = of_property_read_u32(child,
"qcom,pre-div-channel-scaling", &scaling);
if (rc) {
pr_err("Invalid channel scaling property\n");
return -EINVAL;
}
rc = of_property_read_u32(child,
"qcom,scale-function", &post_scaling);
if (rc) {
pr_err("Invalid channel post scaling property\n");
return -EINVAL;
}
rc = of_property_read_u32(child,
"qcom,hw-settle-time", &hw_settle_time);
if (rc) {
pr_err("Invalid channel hw settle time property\n");
return -EINVAL;
}
rc = of_property_read_u32(child,
"qcom,fast-avg-setup", &fast_avg_setup);
if (rc) {
pr_err("Invalid channel fast average setup\n");
return -EINVAL;
}
calibration_param = of_get_property(child,
"qcom,calibration-type", NULL);
if (!strncmp(calibration_param, "absolute", 8))
calib_type = CALIB_ABSOLUTE;
else if (!strncmp(calibration_param, "historical", 9))
calib_type = CALIB_RATIOMETRIC;
else {
pr_err("%s: Invalid calibration property\n", __func__);
return -EINVAL;
}
/* Individual channel properties */
adc_channel_list[i].name = (char *)channel_name;
adc_channel_list[i].channel_num = channel_num;
adc_channel_list[i].chan_path_prescaling = scaling;
adc_channel_list[i].adc_decimation = decimation;
adc_channel_list[i].adc_scale_fn = post_scaling;
adc_channel_list[i].hw_settle_time = hw_settle_time;
adc_channel_list[i].fast_avg_setup = fast_avg_setup;
i++;
}
adc_qpnp->adc_channels = adc_channel_list;
adc_qpnp->amux_prop = amux_prop;
/* Get the ADC VDD reference voltage and ADC bit resolution */
rc = of_property_read_u32(node, "qcom,adc-vdd-reference",
&adc_prop->adc_vdd_reference);
if (rc) {
pr_err("Invalid adc vdd reference property\n");
return -EINVAL;
}
rc = of_property_read_u32(node, "qcom,adc-bit-resolution",
&adc_prop->bitresolution);
if (rc) {
pr_err("Invalid adc bit resolution property\n");
return -EINVAL;
}
adc_qpnp->adc_prop = adc_prop;
/* Get the peripheral address */
res = spmi_get_resource(spmi, 0, IORESOURCE_MEM, 0);
if (!res) {
pr_err("No base address definition\n");
return -EINVAL;
}
adc_qpnp->slave = spmi->sid;
adc_qpnp->offset = res->start;
/* Register the ADC peripheral interrupt */
adc_qpnp->adc_irq = spmi_get_irq(spmi, 0, 0);
if (adc_qpnp->adc_irq < 0) {
pr_err("Invalid irq\n");
return -ENXIO;
}
mutex_init(&adc_qpnp->adc_lock);
return 0;
}
EXPORT_SYMBOL(qpnp_adc_get_devicetree_data);

View file

@ -0,0 +1,784 @@
/* Copyright (c) 2012, 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.
*/
#define pr_fmt(fmt) "%s: " fmt, __func__
#include <linux/kernel.h>
#include <linux/of.h>
#include <linux/err.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/mutex.h>
#include <linux/types.h>
#include <linux/hwmon.h>
#include <linux/module.h>
#include <linux/debugfs.h>
#include <linux/spmi.h>
#include <linux/of_irq.h>
#include <linux/interrupt.h>
#include <linux/completion.h>
#include <linux/hwmon-sysfs.h>
#include <linux/qpnp/qpnp-adc.h>
#include <linux/platform_device.h>
/* QPNP VADC register definition */
#define QPNP_VADC_STATUS1 0x8
#define QPNP_VADC_STATUS1_OP_MODE 4
#define QPNP_VADC_STATUS1_MEAS_INTERVAL_EN_STS BIT(2)
#define QPNP_VADC_STATUS1_REQ_STS BIT(1)
#define QPNP_VADC_STATUS1_EOC BIT(0)
#define QPNP_VADC_STATUS2 0x9
#define QPNP_VADC_STATUS2_CONV_SEQ_STATE 6
#define QPNP_VADC_STATUS2_FIFO_NOT_EMPTY_FLAG BIT(1)
#define QPNP_VADC_STATUS2_CONV_SEQ_TIMEOUT_STS BIT(0)
#define QPNP_VADC_STATUS2_CONV_SEQ_STATE_SHIFT 4
#define QPNP_VADC_CONV_TIMEOUT_ERR 2
#define QPNP_VADC_INT_SET_TYPE 0x11
#define QPNP_VADC_INT_POLARITY_HIGH 0x12
#define QPNP_VADC_INT_POLARITY_LOW 0x13
#define QPNP_VADC_INT_LATCHED_CLR 0x14
#define QPNP_VADC_INT_EN_SET 0x15
#define QPNP_VADC_INT_CLR 0x16
#define QPNP_VADC_INT_LOW_THR_BIT BIT(4)
#define QPNP_VADC_INT_HIGH_THR_BIT BIT(3)
#define QPNP_VADC_INT_CONV_SEQ_TIMEOUT_BIT BIT(2)
#define QPNP_VADC_INT_FIFO_NOT_EMPTY_BIT BIT(1)
#define QPNP_VADC_INT_EOC_BIT BIT(0)
#define QPNP_VADC_INT_CLR_MASK 0x1f
#define QPNP_VADC_MODE_CTL 0x40
#define QPNP_VADC_OP_MODE_SHIFT 4
#define QPNP_VADC_VREF_XO_THM_FORCE BIT(2)
#define QPNP_VADC_AMUX_TRIM_EN BIT(1)
#define QPNP_VADC_ADC_TRIM_EN BIT(0)
#define QPNP_VADC_EN_CTL1 0x46
#define QPNP_VADC_ADC_EN BIT(7)
#define QPNP_VADC_ADC_CH_SEL_CTL 0x48
#define QPNP_VADC_ADC_DIG_PARAM 0x50
#define QPNP_VADC_ADC_DIG_DEC_RATIO_SEL_SHIFT 3
#define QPNP_VADC_HW_SETTLE_DELAY 0x51
#define QPNP_VADC_CONV_REQ 0x52
#define QPNP_VADC_CONV_REQ_SET BIT(7)
#define QPNP_VADC_CONV_SEQ_CTL 0x54
#define QPNP_VADC_CONV_SEQ_HOLDOFF_SHIFT 4
#define QPNP_VADC_CONV_SEQ_TRIG_CTL 0x55
#define QPNP_VADC_CONV_SEQ_FALLING_EDGE 0x0
#define QPNP_VADC_CONV_SEQ_RISING_EDGE 0x1
#define QPNP_VADC_CONV_SEQ_EDGE_SHIFT 7
#define QPNP_VADC_FAST_AVG_CTL 0x5a
#define QPNP_VADC_M0_LOW_THR_LSB 0x5c
#define QPNP_VADC_M0_LOW_THR_MSB 0x5d
#define QPNP_VADC_M0_HIGH_THR_LSB 0x5e
#define QPNP_VADC_M0_HIGH_THR_MSB 0x5f
#define QPNP_VADC_M1_LOW_THR_LSB 0x69
#define QPNP_VADC_M1_LOW_THR_MSB 0x6a
#define QPNP_VADC_M1_HIGH_THR_LSB 0x6b
#define QPNP_VADC_M1_HIGH_THR_MSB 0x6c
#define QPNP_VADC_DATA0 0x60
#define QPNP_VADC_DATA1 0x61
#define QPNP_VADC_CONV_TIMEOUT_ERR 2
#define QPNP_VADC_CONV_TIME_MIN 2000
#define QPNP_VADC_CONV_TIME_MAX 2100
#define QPNP_ADC_HWMON_NAME_LENGTH 16
struct qpnp_vadc_drv {
struct qpnp_adc_drv *adc;
struct dentry *dent;
struct device *vadc_hwmon;
bool vadc_init_calib;
struct sensor_device_attribute sens_attr[0];
};
struct qpnp_vadc_drv *qpnp_vadc;
static struct qpnp_vadc_scale_fn vadc_scale_fn[] = {
[SCALE_DEFAULT] = {qpnp_adc_scale_default},
};
static int32_t qpnp_vadc_read_reg(int16_t reg, u8 *data)
{
struct qpnp_vadc_drv *vadc = qpnp_vadc;
int rc;
rc = spmi_ext_register_readl(vadc->adc->spmi->ctrl, vadc->adc->slave,
reg, data, 1);
if (rc < 0) {
pr_err("qpnp adc read reg %d failed with %d\n", reg, rc);
return rc;
}
return 0;
}
static int32_t qpnp_vadc_write_reg(int16_t reg, u8 data)
{
struct qpnp_vadc_drv *vadc = qpnp_vadc;
int rc;
u8 *buf;
buf = &data;
rc = spmi_ext_register_writel(vadc->adc->spmi->ctrl, vadc->adc->slave,
reg, buf, 1);
if (rc < 0) {
pr_err("qpnp adc write reg %d failed with %d\n", reg, rc);
return rc;
}
return 0;
}
static int32_t qpnp_vadc_configure_interrupt(void)
{
int rc = 0;
u8 data = 0;
/* Configure interrupt as an Edge trigger */
rc = qpnp_vadc_write_reg(QPNP_VADC_INT_SET_TYPE,
QPNP_VADC_INT_CLR_MASK);
if (rc < 0) {
pr_err("%s Interrupt configure failed\n", __func__);
return rc;
}
/* Configure interrupt for rising edge trigger */
rc = qpnp_vadc_write_reg(QPNP_VADC_INT_POLARITY_HIGH,
QPNP_VADC_INT_CLR_MASK);
if (rc < 0) {
pr_err("%s Rising edge trigger configure failed\n", __func__);
return rc;
}
/* Disable low level interrupt triggering */
data = QPNP_VADC_INT_CLR_MASK;
rc = qpnp_vadc_write_reg(QPNP_VADC_INT_POLARITY_LOW,
(~data & QPNP_VADC_INT_CLR_MASK));
if (rc < 0) {
pr_err("%s Setting level low to disable failed\n", __func__);
return rc;
}
return 0;
}
static int32_t qpnp_vadc_enable(bool state)
{
int rc = 0;
u8 data = 0;
data = QPNP_VADC_ADC_EN;
if (state) {
rc = qpnp_vadc_write_reg(QPNP_VADC_EN_CTL1,
data);
if (rc < 0) {
pr_err("VADC enable failed\n");
return rc;
}
} else {
rc = qpnp_vadc_write_reg(QPNP_VADC_EN_CTL1,
(~data & QPNP_VADC_ADC_EN));
if (rc < 0) {
pr_err("VADC disable failed\n");
return rc;
}
}
return 0;
}
int32_t qpnp_vadc_configure(
struct qpnp_vadc_amux_properties *chan_prop)
{
u8 decimation = 0, conv_sequence = 0, conv_sequence_trig = 0;
int rc = 0;
rc = qpnp_vadc_write_reg(QPNP_VADC_INT_EN_SET,
QPNP_VADC_INT_EOC_BIT);
if (rc < 0) {
pr_err("qpnp adc configure error for interrupt setup\n");
return rc;
}
rc = qpnp_vadc_write_reg(QPNP_VADC_MODE_CTL, chan_prop->mode_sel);
if (rc < 0) {
pr_err("qpnp adc configure error for mode selection\n");
return rc;
}
rc = qpnp_vadc_write_reg(QPNP_VADC_ADC_CH_SEL_CTL,
chan_prop->amux_channel);
if (rc < 0) {
pr_err("qpnp adc configure error for channel selection\n");
return rc;
}
decimation |= chan_prop->decimation <<
QPNP_VADC_ADC_DIG_DEC_RATIO_SEL_SHIFT;
rc = qpnp_vadc_write_reg(QPNP_VADC_ADC_DIG_PARAM, decimation);
if (rc < 0) {
pr_err("qpnp adc configure error for digital parameter setup\n");
return rc;
}
rc = qpnp_vadc_write_reg(QPNP_VADC_HW_SETTLE_DELAY,
chan_prop->hw_settle_time);
if (rc < 0) {
pr_err("qpnp adc configure error for hw settling time setup\n");
return rc;
}
if (chan_prop->mode_sel == (ADC_OP_NORMAL_MODE <<
QPNP_VADC_OP_MODE_SHIFT)) {
rc = qpnp_vadc_write_reg(QPNP_VADC_FAST_AVG_CTL,
chan_prop->fast_avg_setup);
if (rc < 0) {
pr_err("qpnp adc fast averaging configure error\n");
return rc;
}
} else if (chan_prop->mode_sel == (ADC_OP_CONVERSION_SEQUENCER <<
QPNP_VADC_OP_MODE_SHIFT)) {
conv_sequence = ((ADC_SEQ_HOLD_100US <<
QPNP_VADC_CONV_SEQ_HOLDOFF_SHIFT) |
ADC_CONV_SEQ_TIMEOUT_5MS);
rc = qpnp_vadc_write_reg(QPNP_VADC_CONV_SEQ_CTL,
conv_sequence);
if (rc < 0) {
pr_err("qpnp adc conversion sequence error\n");
return rc;
}
conv_sequence_trig = ((QPNP_VADC_CONV_SEQ_RISING_EDGE <<
QPNP_VADC_CONV_SEQ_EDGE_SHIFT) |
chan_prop->trigger_channel);
rc = qpnp_vadc_write_reg(QPNP_VADC_CONV_SEQ_TRIG_CTL,
conv_sequence_trig);
if (rc < 0) {
pr_err("qpnp adc conversion trigger error\n");
return rc;
}
}
rc = qpnp_vadc_write_reg(QPNP_VADC_CONV_REQ, QPNP_VADC_CONV_REQ_SET);
if (rc < 0) {
pr_err("qpnp adc request conversion failed\n");
return rc;
}
return 0;
}
EXPORT_SYMBOL(qpnp_vadc_configure);
static int32_t qpnp_vadc_read_conversion_result(int32_t *data)
{
uint8_t rslt_lsb, rslt_msb;
int rc = 0;
rc = qpnp_vadc_read_reg(QPNP_VADC_DATA0, &rslt_lsb);
if (rc < 0) {
pr_err("qpnp adc result read failed for data0 with %d\n", rc);
return rc;
}
rc = qpnp_vadc_read_reg(QPNP_VADC_DATA1, &rslt_msb);
if (rc < 0) {
pr_err("qpnp adc result read failed for data1 with %d\n", rc);
return rc;
}
*data = (rslt_msb << 8) | rslt_lsb;
rc = qpnp_vadc_check_result(data);
if (rc < 0) {
pr_err("VADC data check failed\n");
return rc;
}
return 0;
}
static int32_t qpnp_vadc_read_status(int mode_sel)
{
u8 status1, status2, status2_conv_seq_state;
u8 status_err = QPNP_VADC_CONV_TIMEOUT_ERR;
int rc;
switch (mode_sel) {
case (ADC_OP_CONVERSION_SEQUENCER << QPNP_VADC_OP_MODE_SHIFT):
rc = qpnp_vadc_read_reg(QPNP_VADC_STATUS1, &status1);
if (rc) {
pr_err("qpnp_vadc read mask interrupt failed\n");
return rc;
}
rc = qpnp_vadc_read_reg(QPNP_VADC_STATUS2, &status2);
if (rc) {
pr_err("qpnp_vadc read mask interrupt failed\n");
return rc;
}
if (!(status2 & ~QPNP_VADC_STATUS2_CONV_SEQ_TIMEOUT_STS) &&
(status1 & (~QPNP_VADC_STATUS1_REQ_STS |
QPNP_VADC_STATUS1_EOC))) {
rc = status_err;
return rc;
}
status2_conv_seq_state = status2 >>
QPNP_VADC_STATUS2_CONV_SEQ_STATE_SHIFT;
if (status2_conv_seq_state != ADC_CONV_SEQ_IDLE) {
pr_err("qpnp vadc seq error with status %d\n",
status2);
rc = -EINVAL;
return rc;
}
}
return 0;
}
static void qpnp_vadc_work(struct work_struct *work)
{
struct qpnp_vadc_drv *vadc = qpnp_vadc;
int rc;
rc = qpnp_vadc_write_reg(QPNP_VADC_INT_CLR, QPNP_VADC_INT_EOC_BIT);
if (rc)
pr_err("qpnp_vadc clear mask interrupt failed with %d\n", rc);
complete(&vadc->adc->adc_rslt_completion);
return;
}
DECLARE_WORK(trigger_completion_work, qpnp_vadc_work);
static irqreturn_t qpnp_vadc_isr(int irq, void *dev_id)
{
schedule_work(&trigger_completion_work);
return IRQ_HANDLED;
}
static uint32_t qpnp_vadc_calib_device(void)
{
struct qpnp_vadc_drv *vadc = qpnp_vadc;
struct qpnp_vadc_amux_properties conv;
int rc, calib_read_1, calib_read_2;
u8 status1 = 0;
conv.amux_channel = REF_125V;
conv.decimation = DECIMATION_TYPE2;
conv.mode_sel = ADC_OP_NORMAL_MODE << QPNP_VADC_OP_MODE_SHIFT;
conv.hw_settle_time = ADC_CHANNEL_HW_SETTLE_DELAY_0US;
conv.fast_avg_setup = ADC_FAST_AVG_SAMPLE_1;
rc = qpnp_vadc_configure(&conv);
if (rc) {
pr_err("qpnp_vadc configure failed with %d\n", rc);
goto calib_fail;
}
while (status1 != (~QPNP_VADC_STATUS1_REQ_STS |
QPNP_VADC_STATUS1_EOC)) {
rc = qpnp_vadc_read_reg(QPNP_VADC_STATUS1, &status1);
if (rc < 0)
return rc;
usleep_range(QPNP_VADC_CONV_TIME_MIN,
QPNP_VADC_CONV_TIME_MAX);
}
rc = qpnp_vadc_read_conversion_result(&calib_read_1);
if (rc) {
pr_err("qpnp adc read adc failed with %d\n", rc);
goto calib_fail;
}
conv.amux_channel = REF_625MV;
conv.decimation = DECIMATION_TYPE2;
conv.mode_sel = ADC_OP_NORMAL_MODE << QPNP_VADC_OP_MODE_SHIFT;
conv.hw_settle_time = ADC_CHANNEL_HW_SETTLE_DELAY_0US;
conv.fast_avg_setup = ADC_FAST_AVG_SAMPLE_1;
rc = qpnp_vadc_configure(&conv);
if (rc) {
pr_err("qpnp adc configure failed with %d\n", rc);
goto calib_fail;
}
while (status1 != (~QPNP_VADC_STATUS1_REQ_STS |
QPNP_VADC_STATUS1_EOC)) {
rc = qpnp_vadc_read_reg(QPNP_VADC_STATUS1, &status1);
if (rc < 0)
return rc;
usleep_range(QPNP_VADC_CONV_TIME_MIN,
QPNP_VADC_CONV_TIME_MAX);
}
rc = qpnp_vadc_read_conversion_result(&calib_read_1);
if (rc) {
pr_err("qpnp adc read adc failed with %d\n", rc);
goto calib_fail;
}
vadc->adc->amux_prop->chan_prop->adc_graph[CALIB_ABSOLUTE].dy =
(calib_read_1 - calib_read_2);
vadc->adc->amux_prop->chan_prop->adc_graph[CALIB_ABSOLUTE].dx
= QPNP_ADC_625_UV;
vadc->adc->amux_prop->chan_prop->adc_graph[CALIB_ABSOLUTE].adc_vref =
calib_read_1;
vadc->adc->amux_prop->chan_prop->adc_graph[CALIB_ABSOLUTE].adc_gnd =
calib_read_2;
/* Ratiometric Calibration */
conv.amux_channel = VDD_VADC;
conv.decimation = DECIMATION_TYPE2;
conv.mode_sel = ADC_OP_NORMAL_MODE << QPNP_VADC_OP_MODE_SHIFT;
conv.hw_settle_time = ADC_CHANNEL_HW_SETTLE_DELAY_0US;
conv.fast_avg_setup = ADC_FAST_AVG_SAMPLE_1;
rc = qpnp_vadc_configure(&conv);
if (rc) {
pr_err("qpnp adc configure failed with %d\n", rc);
goto calib_fail;
}
while (status1 != (~QPNP_VADC_STATUS1_REQ_STS |
QPNP_VADC_STATUS1_EOC)) {
rc = qpnp_vadc_read_reg(QPNP_VADC_STATUS1, &status1);
if (rc < 0)
return rc;
usleep_range(QPNP_VADC_CONV_TIME_MIN,
QPNP_VADC_CONV_TIME_MAX);
}
rc = qpnp_vadc_read_conversion_result(&calib_read_1);
if (rc) {
pr_err("qpnp adc read adc failed with %d\n", rc);
goto calib_fail;
}
conv.amux_channel = VDD_VADC;
conv.decimation = DECIMATION_TYPE2;
conv.mode_sel = ADC_OP_NORMAL_MODE << QPNP_VADC_OP_MODE_SHIFT;
conv.hw_settle_time = ADC_CHANNEL_HW_SETTLE_DELAY_0US;
conv.fast_avg_setup = ADC_FAST_AVG_SAMPLE_1;
rc = qpnp_vadc_configure(&conv);
if (rc) {
pr_err("qpnp adc configure failed with %d\n", rc);
goto calib_fail;
}
while (status1 != (~QPNP_VADC_STATUS1_REQ_STS |
QPNP_VADC_STATUS1_EOC)) {
rc = qpnp_vadc_read_reg(QPNP_VADC_STATUS1, &status1);
if (rc < 0)
return rc;
usleep_range(QPNP_VADC_CONV_TIME_MIN,
QPNP_VADC_CONV_TIME_MAX);
}
rc = qpnp_vadc_read_conversion_result(&calib_read_1);
if (rc) {
pr_err("qpnp adc read adc failed with %d\n", rc);
goto calib_fail;
}
vadc->adc->amux_prop->chan_prop->adc_graph[CALIB_RATIOMETRIC].dy =
(calib_read_1 - calib_read_2);
vadc->adc->amux_prop->chan_prop->adc_graph[CALIB_RATIOMETRIC].dx =
vadc->adc->adc_prop->adc_vdd_reference;
vadc->adc->amux_prop->chan_prop->adc_graph[CALIB_RATIOMETRIC].adc_vref =
calib_read_1;
vadc->adc->amux_prop->chan_prop->adc_graph[CALIB_RATIOMETRIC].adc_gnd =
calib_read_2;
calib_fail:
return rc;
}
int32_t qpnp_vadc_conv_seq_request(enum qpnp_vadc_trigger trigger_channel,
enum qpnp_vadc_channels channel,
struct qpnp_vadc_result *result)
{
struct qpnp_vadc_drv *vadc = qpnp_vadc;
int rc, scale_type, amux_prescaling;
if (!vadc->vadc_init_calib) {
rc = qpnp_vadc_calib_device();
if (rc) {
pr_err("Calibration failed\n");
return rc;
} else
vadc->vadc_init_calib = true;
}
mutex_lock(&vadc->adc->adc_lock);
rc = qpnp_vadc_enable(true);
if (rc)
goto fail_unlock;
vadc->adc->amux_prop->amux_channel = channel;
vadc->adc->amux_prop->decimation =
vadc->adc->adc_channels[channel].adc_decimation;
vadc->adc->amux_prop->hw_settle_time =
vadc->adc->adc_channels[channel].hw_settle_time;
vadc->adc->amux_prop->fast_avg_setup =
vadc->adc->adc_channels[channel].fast_avg_setup;
if (trigger_channel < ADC_SEQ_NONE)
vadc->adc->amux_prop->mode_sel = (ADC_OP_CONVERSION_SEQUENCER
<< QPNP_VADC_OP_MODE_SHIFT);
else if (trigger_channel == ADC_SEQ_NONE)
vadc->adc->amux_prop->mode_sel = (ADC_OP_NORMAL_MODE
<< QPNP_VADC_OP_MODE_SHIFT);
else {
pr_err("Invalid trigger channel:%d\n", trigger_channel);
goto fail;
}
vadc->adc->amux_prop->trigger_channel = trigger_channel;
rc = qpnp_vadc_configure(vadc->adc->amux_prop);
if (rc) {
pr_info("qpnp vadc configure failed with %d\n", rc);
goto fail;
}
wait_for_completion(&vadc->adc->adc_rslt_completion);
if (trigger_channel < ADC_SEQ_NONE) {
rc = qpnp_vadc_read_status(vadc->adc->amux_prop->mode_sel);
if (rc)
pr_info("Conversion sequence timed out - %d\n", rc);
}
rc = qpnp_vadc_read_conversion_result(&result->adc_code);
if (rc) {
pr_info("qpnp vadc read adc code failed with %d\n", rc);
goto fail;
}
amux_prescaling = vadc->adc->adc_channels[channel].chan_path_prescaling;
vadc->adc->amux_prop->chan_prop->offset_gain_numerator =
qpnp_vadc_amux_scaling_ratio[amux_prescaling].num;
vadc->adc->amux_prop->chan_prop->offset_gain_denominator =
qpnp_vadc_amux_scaling_ratio[amux_prescaling].den;
scale_type = vadc->adc->adc_channels[channel].adc_scale_fn;
if (scale_type >= SCALE_NONE) {
rc = -EBADF;
goto fail;
}
vadc_scale_fn[scale_type].chan(result->adc_code,
vadc->adc->adc_prop, vadc->adc->amux_prop->chan_prop, result);
fail:
rc = qpnp_vadc_enable(false);
if (rc)
pr_err("Disable ADC failed during configuration\n");
fail_unlock:
mutex_unlock(&vadc->adc->adc_lock);
return rc;
}
EXPORT_SYMBOL(qpnp_vadc_conv_seq_request);
int32_t qpnp_vadc_read(enum qpnp_vadc_channels channel,
struct qpnp_vadc_result *result)
{
return qpnp_vadc_conv_seq_request(ADC_SEQ_NONE,
channel, result);
}
EXPORT_SYMBOL_GPL(qpnp_vadc_read);
static ssize_t qpnp_adc_show(struct device *dev,
struct device_attribute *devattr, char *buf)
{
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
struct qpnp_vadc_result result;
int rc = -1;
rc = qpnp_vadc_read(attr->index, &result);
if (rc)
return 0;
return snprintf(buf, QPNP_ADC_HWMON_NAME_LENGTH,
"Result:%lld Raw:%d\n", result.physical, result.adc_code);
}
static struct sensor_device_attribute qpnp_adc_attr =
SENSOR_ATTR(NULL, S_IRUGO, qpnp_adc_show, NULL, 0);
static int32_t qpnp_vadc_init_hwmon(struct spmi_device *spmi)
{
struct qpnp_vadc_drv *vadc = qpnp_vadc;
struct device_node *child;
struct device_node *node = spmi->dev.of_node;
int rc = 0, i = 0, channel;
for_each_child_of_node(node, child) {
channel = vadc->adc->adc_channels[i].channel_num;
qpnp_adc_attr.index = vadc->adc->adc_channels[i].channel_num;
qpnp_adc_attr.dev_attr.attr.name =
vadc->adc->adc_channels[i].name;
sysfs_attr_init(&vadc->sens_attr[i].dev_attr.attr);
memcpy(&vadc->sens_attr[i], &qpnp_adc_attr,
sizeof(qpnp_adc_attr));
rc = device_create_file(&spmi->dev,
&vadc->sens_attr[i].dev_attr);
if (rc) {
dev_err(&spmi->dev,
"device_create_file failed for dev %s\n",
vadc->adc->adc_channels[i].name);
goto hwmon_err_sens;
}
i++;
}
return 0;
hwmon_err_sens:
pr_info("Init HWMON failed for qpnp_adc with %d\n", rc);
return rc;
}
static int qpnp_vadc_probe(struct spmi_device *spmi)
{
struct qpnp_vadc_drv *vadc;
struct qpnp_adc_drv *adc_qpnp;
struct device_node *node = spmi->dev.of_node;
struct device_node *child;
int rc, count_adc_channel_list = 0;
if (!node)
return -EINVAL;
if (qpnp_vadc) {
pr_err("VADC already in use\n");
return -EBUSY;
}
for_each_child_of_node(node, child)
count_adc_channel_list++;
if (!count_adc_channel_list) {
pr_err("No channel listing\n");
return -EINVAL;
}
vadc = devm_kzalloc(&spmi->dev, sizeof(struct qpnp_vadc_drv) +
(sizeof(struct sensor_device_attribute) *
count_adc_channel_list), GFP_KERNEL);
if (!vadc) {
dev_err(&spmi->dev, "Unable to allocate memory\n");
return -ENOMEM;
}
adc_qpnp = devm_kzalloc(&spmi->dev, sizeof(struct qpnp_adc_drv),
GFP_KERNEL);
if (!adc_qpnp) {
dev_err(&spmi->dev, "Unable to allocate memory\n");
return -ENOMEM;
}
vadc->adc = adc_qpnp;
rc = qpnp_adc_get_devicetree_data(spmi, vadc->adc);
if (rc) {
dev_err(&spmi->dev, "failed to read device tree\n");
return rc;
}
rc = devm_request_irq(&spmi->dev, vadc->adc->adc_irq,
qpnp_vadc_isr, IRQF_TRIGGER_RISING,
"qpnp_vadc_interrupt", vadc);
if (rc) {
dev_err(&spmi->dev,
"failed to request adc irq with error %d\n", rc);
return rc;
}
qpnp_vadc = vadc;
dev_set_drvdata(&spmi->dev, vadc);
rc = qpnp_vadc_init_hwmon(spmi);
if (rc) {
dev_err(&spmi->dev, "failed to initialize qpnp hwmon adc\n");
goto fail_free_irq;
}
vadc->vadc_hwmon = hwmon_device_register(&vadc->adc->spmi->dev);
vadc->vadc_init_calib = false;
rc = qpnp_vadc_configure_interrupt();
if (rc) {
dev_err(&spmi->dev, "failed to configure interrupt");
goto fail_free_irq;
}
return 0;
fail_free_irq:
free_irq(vadc->adc->adc_irq, vadc);
return rc;
}
static int qpnp_vadc_remove(struct spmi_device *spmi)
{
struct qpnp_vadc_drv *vadc = dev_get_drvdata(&spmi->dev);
struct device_node *node = spmi->dev.of_node;
struct device_node *child;
int i = 0;
for_each_child_of_node(node, child) {
device_remove_file(&spmi->dev,
&vadc->sens_attr[i].dev_attr);
i++;
}
free_irq(vadc->adc->adc_irq, vadc);
dev_set_drvdata(&spmi->dev, NULL);
return 0;
}
static const struct of_device_id qpnp_vadc_match_table[] = {
{ .compatible = "qcom,qpnp-vadc",
},
{}
};
static struct spmi_driver qpnp_vadc_driver = {
.driver = {
.name = "qcom,qpnp-vadc",
.of_match_table = qpnp_vadc_match_table,
},
.probe = qpnp_vadc_probe,
.remove = qpnp_vadc_remove,
};
static int __init qpnp_vadc_init(void)
{
return spmi_driver_register(&qpnp_vadc_driver);
}
module_init(qpnp_vadc_init);
static void __exit qpnp_vadc_exit(void)
{
spmi_driver_unregister(&qpnp_vadc_driver);
}
module_exit(qpnp_vadc_exit);
MODULE_DESCRIPTION("QPNP PMIC Voltage ADC driver");
MODULE_LICENSE("GPL v2");

View file

@ -0,0 +1,689 @@
/*
* Copyright (c) 2012, 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.
*/
/*
* Qualcomm PMIC QPNP ADC driver header file
*
*/
#ifndef __QPNP_ADC_H
#define __QPNP_ADC_H
#include <linux/kernel.h>
#include <linux/list.h>
/**
* enum qpnp_vadc_channels - QPNP AMUX arbiter channels
*/
enum qpnp_vadc_channels {
USBIN = 0,
DCIN,
VCHG_SNS,
SPARE1_03,
SPARE2_03,
VCOIN,
VBAT_SNS,
VSYS,
DIE_TEMP,
REF_625MV,
REF_125V,
CHG_TEMP,
SPARE1,
SPARE2,
GND_REF,
VDD_VADC,
P_MUX1_1_1,
P_MUX2_1_1,
P_MUX3_1_1,
P_MUX4_1_1,
P_MUX5_1_1,
P_MUX6_1_1,
P_MUX7_1_1,
P_MUX8_1_1,
P_MUX9_1_1,
P_MUX10_1_1,
P_MUX11_1_1,
P_MUX12_1_1,
P_MUX13_1_1,
P_MUX14_1_1,
P_MUX15_1_1,
P_MUX16_1_1,
P_MUX1_1_3,
P_MUX2_1_3,
P_MUX3_1_3,
P_MUX4_1_3,
P_MUX5_1_3,
P_MUX6_1_3,
P_MUX7_1_3,
P_MUX8_1_3,
P_MUX9_1_3,
P_MUX10_1_3,
P_MUX11_1_3,
P_MUX12_1_3,
P_MUX13_1_3,
P_MUX14_1_3,
P_MUX15_1_3,
P_MUX16_1_3,
LR_MUX1_BATT_THERM,
LR_MUX2_BAT_ID,
LR_MUX3_XO_THERM,
LR_MUX4_AMUX_THM1,
LR_MUX5_AMUX_THM2,
LR_MUX6_AMUX_THM3,
LR_MUX7_HW_ID,
LR_MUX8_AMUX_THM4,
LR_MUX9_AMUX_THM5,
LR_MUX10_USB_ID,
AMUX_PU1,
AMUX_PU2,
LR_MUX3_BUF_XO_THERM_BUF,
LR_MUX1_PU1_BAT_THERM,
LR_MUX2_PU1_BAT_ID,
LR_MUX3_PU1_XO_THERM,
LR_MUX4_PU1_AMUX_THM1,
LR_MUX5_PU1_AMUX_THM2,
LR_MUX6_PU1_AMUX_THM3,
LR_MUX7_PU1_AMUX_HW_ID,
LR_MUX8_PU1_AMUX_THM4,
LR_MUX9_PU1_AMUX_THM5,
LR_MUX10_PU1_AMUX_USB_ID,
LR_MUX3_BUF_PU1_XO_THERM_BUF,
LR_MUX1_PU2_BAT_THERM,
LR_MUX2_PU2_BAT_ID,
LR_MUX3_PU2_XO_THERM,
LR_MUX4_PU2_AMUX_THM1,
LR_MUX5_PU2_AMUX_THM2,
LR_MUX6_PU2_AMUX_THM3,
LR_MUX7_PU2_AMUX_HW_ID,
LR_MUX8_PU2_AMUX_THM4,
LR_MUX9_PU2_AMUX_THM5,
LR_MUX10_PU2_AMUX_USB_ID,
LR_MUX3_BUF_PU2_XO_THERM_BUF,
LR_MUX1_PU1_PU2_BAT_THERM,
LR_MUX2_PU1_PU2_BAT_ID,
LR_MUX3_PU1_PU2_XO_THERM,
LR_MUX4_PU1_PU2_AMUX_THM1,
LR_MUX5_PU1_PU2_AMUX_THM2,
LR_MUX6_PU1_PU2_AMUX_THM3,
LR_MUX7_PU1_PU2_AMUX_HW_ID,
LR_MUX8_PU1_PU2_AMUX_THM4,
LR_MUX9_PU1_PU2_AMUX_THM5,
LR_MUX10_PU1_PU2_AMUX_USB_ID,
LR_MUX3_BUF_PU1_PU2_XO_THERM_BUF,
ALL_OFF,
ADC_MAX_NUM,
};
#define QPNP_ADC_625_UV 625000
/**
* enum qpnp_adc_decimation_type - Sampling rate supported.
* %DECIMATION_TYPE1: 512
* %DECIMATION_TYPE2: 1K
* %DECIMATION_TYPE3: 2K
* %DECIMATION_TYPE4: 4k
* %DECIMATION_NONE: Do not use this Sampling type.
*
* The Sampling rate is specific to each channel of the QPNP ADC arbiter.
*/
enum qpnp_adc_decimation_type {
DECIMATION_TYPE1 = 0,
DECIMATION_TYPE2,
DECIMATION_TYPE3,
DECIMATION_TYPE4,
DECIMATION_NONE,
};
/**
* enum qpnp_adc_calib_type - QPNP ADC Calibration type.
* %ADC_CALIB_ABSOLUTE: Use 625mV and 1.25V reference channels.
* %ADC_CALIB_RATIOMETRIC: Use reference Voltage/GND.
* %ADC_CALIB_CONFIG_NONE: Do not use this calibration type.
*
* Use the input reference voltage depending on the calibration type
* to calcluate the offset and gain parameters. The calibration is
* specific to each channel of the QPNP ADC.
*/
enum qpnp_adc_calib_type {
CALIB_ABSOLUTE = 0,
CALIB_RATIOMETRIC,
CALIB_NONE,
};
/**
* enum qpnp_adc_channel_scaling_param - pre-scaling AMUX ratio.
* %CHAN_PATH_SCALING1: ratio of {1, 1}
* %CHAN_PATH_SCALING2: ratio of {1, 3}
* %CHAN_PATH_SCALING3: ratio of {1, 4}
* %CHAN_PATH_SCALING4: ratio of {1, 6}
* %CHAN_PATH_NONE: Do not use this pre-scaling ratio type.
*
* The pre-scaling is applied for signals to be within the voltage range
* of the ADC.
*/
enum qpnp_adc_channel_scaling_param {
PATH_SCALING1 = 0,
PATH_SCALING2,
PATH_SCALING3,
PATH_SCALING4,
PATH_SCALING_NONE,
};
/**
* enum qpnp_adc_scale_fn_type - Scaling function for pm8921 pre calibrated
* digital data relative to ADC reference.
* %ADC_SCALE_DEFAULT: Default scaling to convert raw adc code to voltage.
* %ADC_SCALE_BATT_THERM: Conversion to temperature based on btm parameters.
* %ADC_SCALE_PMIC_THERM: Returns result in milli degree's Centigrade.
* %ADC_SCALE_XTERN_CHGR_CUR: Returns current across 0.1 ohm resistor.
* %ADC_SCALE_XOTHERM: Returns XO thermistor voltage in degree's Centigrade.
* %ADC_SCALE_NONE: Do not use this scaling type.
*/
enum qpnp_adc_scale_fn_type {
SCALE_DEFAULT = 0,
SCALE_BATT_THERM,
SCALE_PA_THERM,
SCALE_PMIC_THERM,
SCALE_XOTHERM,
SCALE_NONE,
};
/**
* enum qpnp_adc_fast_avg_ctl - Provides ability to obtain single result
* from the ADC that is an average of multiple measurement
* samples. Select number of samples for use in fast
* average mode (i.e. 2 ^ value).
* %ADC_FAST_AVG_SAMPLE_1: 0x0 = 1
* %ADC_FAST_AVG_SAMPLE_2: 0x1 = 2
* %ADC_FAST_AVG_SAMPLE_4: 0x2 = 4
* %ADC_FAST_AVG_SAMPLE_8: 0x3 = 8
* %ADC_FAST_AVG_SAMPLE_16: 0x4 = 16
* %ADC_FAST_AVG_SAMPLE_32: 0x5 = 32
* %ADC_FAST_AVG_SAMPLE_64: 0x6 = 64
* %ADC_FAST_AVG_SAMPLE_128: 0x7 = 128
* %ADC_FAST_AVG_SAMPLE_256: 0x8 = 256
* %ADC_FAST_AVG_SAMPLE_512: 0x9 = 512
*/
enum qpnp_adc_fast_avg_ctl {
ADC_FAST_AVG_SAMPLE_1 = 0,
ADC_FAST_AVG_SAMPLE_2,
ADC_FAST_AVG_SAMPLE_4,
ADC_FAST_AVG_SAMPLE_8,
ADC_FAST_AVG_SAMPLE_16,
ADC_FAST_AVG_SAMPLE_32,
ADC_FAST_AVG_SAMPLE_64,
ADC_FAST_AVG_SAMPLE_128,
ADC_FAST_AVG_SAMPLE_256,
ADC_FAST_AVG_SAMPLE_512,
ADC_FAST_AVG_SAMPLE_NONE,
};
/**
* enum qpnp_adc_hw_settle_time - Time between AMUX getting configured and
* the ADC starting conversion. Delay = 100us * value for
* value < 11 and 2ms * (value - 10) otherwise.
* %ADC_CHANNEL_HW_SETTLE_DELAY_0US: 0us
* %ADC_CHANNEL_HW_SETTLE_DELAY_100US: 100us
* %ADC_CHANNEL_HW_SETTLE_DELAY_200US: 200us
* %ADC_CHANNEL_HW_SETTLE_DELAY_300US: 300us
* %ADC_CHANNEL_HW_SETTLE_DELAY_400US: 400us
* %ADC_CHANNEL_HW_SETTLE_DELAY_500US: 500us
* %ADC_CHANNEL_HW_SETTLE_DELAY_600US: 600us
* %ADC_CHANNEL_HW_SETTLE_DELAY_700US: 700us
* %ADC_CHANNEL_HW_SETTLE_DELAY_800US: 800us
* %ADC_CHANNEL_HW_SETTLE_DELAY_900US: 900us
* %ADC_CHANNEL_HW_SETTLE_DELAY_1MS: 1ms
* %ADC_CHANNEL_HW_SETTLE_DELAY_2MS: 2ms
* %ADC_CHANNEL_HW_SETTLE_DELAY_4MS: 4ms
* %ADC_CHANNEL_HW_SETTLE_DELAY_6MS: 6ms
* %ADC_CHANNEL_HW_SETTLE_DELAY_8MS: 8ms
* %ADC_CHANNEL_HW_SETTLE_DELAY_10MS: 10ms
* %ADC_CHANNEL_HW_SETTLE_NONE
*/
enum qpnp_adc_hw_settle_time {
ADC_CHANNEL_HW_SETTLE_DELAY_0US = 0,
ADC_CHANNEL_HW_SETTLE_DELAY_100US,
ADC_CHANNEL_HW_SETTLE_DELAY_2000US,
ADC_CHANNEL_HW_SETTLE_DELAY_300US,
ADC_CHANNEL_HW_SETTLE_DELAY_400US,
ADC_CHANNEL_HW_SETTLE_DELAY_500US,
ADC_CHANNEL_HW_SETTLE_DELAY_600US,
ADC_CHANNEL_HW_SETTLE_DELAY_700US,
ADC_CHANNEL_HW_SETTLE_DELAY_800US,
ADC_CHANNEL_HW_SETTLE_DELAY_900US,
ADC_CHANNEL_HW_SETTLE_DELAY_1MS,
ADC_CHANNEL_HW_SETTLE_DELAY_2MS,
ADC_CHANNEL_HW_SETTLE_DELAY_4MS,
ADC_CHANNEL_HW_SETTLE_DELAY_6MS,
ADC_CHANNEL_HW_SETTLE_DELAY_8MS,
ADC_CHANNEL_HW_SETTLE_DELAY_10MS,
ADC_CHANNEL_HW_SETTLE_NONE,
};
/**
* enum qpnp_vadc_mode_sel - Selects the basic mode of operation.
* - The normal mode is used for single measurement.
* - The Conversion sequencer is used to trigger an
* ADC read when a HW trigger is selected.
* - The measurement interval performs a single or
* continous measurement at a specified interval/delay.
* %ADC_OP_NORMAL_MODE : Normal mode used for single measurement.
* %ADC_OP_CONVERSION_SEQUENCER : Conversion sequencer used to trigger
* an ADC read on a HW supported trigger.
* Refer to enum qpnp_vadc_trigger for
* supported HW triggers.
* %ADC_OP_MEASUREMENT_INTERVAL : The measurement interval performs a
* single or continous measurement after a specified delay.
* For delay look at qpnp_adc_meas_timer.
*/
enum qpnp_vadc_mode_sel {
ADC_OP_NORMAL_MODE = 0,
ADC_OP_CONVERSION_SEQUENCER,
ADC_OP_MEASUREMENT_INTERVAL,
ADC_OP_MODE_NONE,
};
/**
* enum qpnp_vadc_trigger - Select the HW trigger to be used while
* measuring the ADC reading.
* %ADC_GSM_PA_ON : GSM power amplifier on.
* %ADC_TX_GTR_THRES : Transmit power greater than threshold.
* %ADC_CAMERA_FLASH_RAMP : Flash ramp up done.
* %ADC_DTEST : DTEST.
*/
enum qpnp_vadc_trigger {
ADC_GSM_PA_ON = 0,
ADC_TX_GTR_THRES,
ADC_CAMERA_FLASH_RAMP,
ADC_DTEST,
ADC_SEQ_NONE,
};
/**
* enum qpnp_vadc_conv_seq_timeout - Select delay (0 to 15ms) from
* conversion request to triggering conversion sequencer
* hold off time.
*/
enum qpnp_vadc_conv_seq_timeout {
ADC_CONV_SEQ_TIMEOUT_0MS = 0,
ADC_CONV_SEQ_TIMEOUT_1MS,
ADC_CONV_SEQ_TIMEOUT_2MS,
ADC_CONV_SEQ_TIMEOUT_3MS,
ADC_CONV_SEQ_TIMEOUT_4MS,
ADC_CONV_SEQ_TIMEOUT_5MS,
ADC_CONV_SEQ_TIMEOUT_6MS,
ADC_CONV_SEQ_TIMEOUT_7MS,
ADC_CONV_SEQ_TIMEOUT_8MS,
ADC_CONV_SEQ_TIMEOUT_9MS,
ADC_CONV_SEQ_TIMEOUT_10MS,
ADC_CONV_SEQ_TIMEOUT_11MS,
ADC_CONV_SEQ_TIMEOUT_12MS,
ADC_CONV_SEQ_TIMEOUT_13MS,
ADC_CONV_SEQ_TIMEOUT_14MS,
ADC_CONV_SEQ_TIMEOUT_15MS,
ADC_CONV_SEQ_TIMEOUT_NONE,
};
/**
* enum qpnp_adc_conv_seq_holdoff - Select delay from conversion
* trigger signal (i.e. adc_conv_seq_trig) transition
* to ADC enable. Delay = 25us * (value + 1).
*/
enum qpnp_adc_conv_seq_holdoff {
ADC_SEQ_HOLD_25US = 0,
ADC_SEQ_HOLD_50US,
ADC_SEQ_HOLD_75US,
ADC_SEQ_HOLD_100US,
ADC_SEQ_HOLD_125US,
ADC_SEQ_HOLD_150US,
ADC_SEQ_HOLD_175US,
ADC_SEQ_HOLD_200US,
ADC_SEQ_HOLD_225US,
ADC_SEQ_HOLD_250US,
ADC_SEQ_HOLD_275US,
ADC_SEQ_HOLD_300US,
ADC_SEQ_HOLD_325US,
ADC_SEQ_HOLD_350US,
ADC_SEQ_HOLD_375US,
ADC_SEQ_HOLD_400US,
ADC_SEQ_HOLD_NONE,
};
/**
* enum qpnp_adc_conv_seq_state - Conversion sequencer operating state
* %ADC_CONV_SEQ_IDLE : Sequencer is in idle.
* %ADC_CONV_TRIG_RISE : Waiting for rising edge trigger.
* %ADC_CONV_TRIG_HOLDOFF : Waiting for rising trigger hold off time.
* %ADC_CONV_MEAS_RISE : Measuring selected ADC signal.
* %ADC_CONV_TRIG_FALL : Waiting for falling trigger edge.
* %ADC_CONV_FALL_HOLDOFF : Waiting for falling trigger hold off time.
* %ADC_CONV_MEAS_FALL : Measuring selected ADC signal.
* %ADC_CONV_ERROR : Aberrant Hardware problem.
*/
enum qpnp_adc_conv_seq_state {
ADC_CONV_SEQ_IDLE = 0,
ADC_CONV_TRIG_RISE,
ADC_CONV_TRIG_HOLDOFF,
ADC_CONV_MEAS_RISE,
ADC_CONV_TRIG_FALL,
ADC_CONV_FALL_HOLDOFF,
ADC_CONV_MEAS_FALL,
ADC_CONV_ERROR,
ADC_CONV_NONE,
};
/**
* enum qpnp_adc_meas_timer - Selects the measurement interval time.
* If value = 0, use 0ms else use 2^(value + 4)/ 32768).
* %ADC_MEAS_INTERVAL_0MS : 0ms
* %ADC_MEAS_INTERVAL_1P0MS : 1ms
* %ADC_MEAS_INTERVAL_2P0MS : 2ms
* %ADC_MEAS_INTERVAL_3P9MS : 3.9ms
* %ADC_MEAS_INTERVAL_7P8MS : 7.8ms
* %ADC_MEAS_INTERVAL_15P6MS : 15.6ms
* %ADC_MEAS_INTERVAL_31P3MS : 31.3ms
* %ADC_MEAS_INTERVAL_62P5MS : 62.5ms
* %ADC_MEAS_INTERVAL_125MS : 125ms
* %ADC_MEAS_INTERVAL_250MS : 250ms
* %ADC_MEAS_INTERVAL_500MS : 500ms
* %ADC_MEAS_INTERVAL_1S : 1seconds
* %ADC_MEAS_INTERVAL_2S : 2seconds
* %ADC_MEAS_INTERVAL_4S : 4seconds
* %ADC_MEAS_INTERVAL_8S : 8seconds
* %ADC_MEAS_INTERVAL_16S: 16seconds
*/
enum qpnp_adc_meas_timer {
ADC_MEAS_INTERVAL_0MS = 0,
ADC_MEAS_INTERVAL_1P0MS,
ADC_MEAS_INTERVAL_2P0MS,
ADC_MEAS_INTERVAL_3P9MS,
ADC_MEAS_INTERVAL_7P8MS,
ADC_MEAS_INTERVAL_15P6MS,
ADC_MEAS_INTERVAL_31P3MS,
ADC_MEAS_INTERVAL_62P5MS,
ADC_MEAS_INTERVAL_125MS,
ADC_MEAS_INTERVAL_250MS,
ADC_MEAS_INTERVAL_500MS,
ADC_MEAS_INTERVAL_1S,
ADC_MEAS_INTERVAL_2S,
ADC_MEAS_INTERVAL_4S,
ADC_MEAS_INTERVAL_8S,
ADC_MEAS_INTERVAL_16S,
ADC_MEAS_INTERVAL_NONE,
};
/**
* enum qpnp_adc_meas_interval_op_ctl - Select operating mode.
* %ADC_MEAS_INTERVAL_OP_SINGLE : Conduct single measurement at specified time
* delay.
* %ADC_MEAS_INTERVAL_OP_CONTINUOUS : Make measurements at measurement interval
* times.
*/
enum qpnp_adc_meas_interval_op_ctl {
ADC_MEAS_INTERVAL_OP_SINGLE = 0,
ADC_MEAS_INTERVAL_OP_CONTINUOUS,
ADC_MEAS_INTERVAL_OP_NONE,
};
/**
* struct qpnp_vadc_linear_graph - Represent ADC characteristics.
* @dy: Numerator slope to calculate the gain.
* @dx: Denominator slope to calculate the gain.
* @adc_vref: A/D word of the voltage reference used for the channel.
* @adc_gnd: A/D word of the ground reference used for the channel.
*
* Each ADC device has different offset and gain parameters which are computed
* to calibrate the device.
*/
struct qpnp_vadc_linear_graph {
int64_t dy;
int64_t dx;
int64_t adc_vref;
int64_t adc_gnd;
};
/**
* struct qpnp_vadc_map_pt - Map the graph representation for ADC channel
* @x: Represent the ADC digitized code.
* @y: Represent the physical data which can be temperature, voltage,
* resistance.
*/
struct qpnp_vadc_map_pt {
int32_t x;
int32_t y;
};
/**
* struct qpnp_vadc_scaling_ratio - Represent scaling ratio for adc input.
* @num: Numerator scaling parameter.
* @den: Denominator scaling parameter.
*/
struct qpnp_vadc_scaling_ratio {
int32_t num;
int32_t den;
};
/**
* struct qpnp_adc_properties - Represent the ADC properties.
* @adc_reference: Reference voltage for QPNP ADC.
* @bitresolution: ADC bit resolution for QPNP ADC.
* @biploar: Polarity for QPNP ADC.
*/
struct qpnp_adc_properties {
uint32_t adc_vdd_reference;
uint32_t bitresolution;
bool bipolar;
};
/**
* struct qpnp_vadc_chan_properties - Represent channel properties of the ADC.
* @offset_gain_numerator: The inverse numerator of the gain applied to the
* input channel.
* @offset_gain_denominator: The inverse denominator of the gain applied to the
* input channel.
* @adc_graph: ADC graph for the channel of struct type qpnp_adc_linear_graph.
*/
struct qpnp_vadc_chan_properties {
uint32_t offset_gain_numerator;
uint32_t offset_gain_denominator;
struct qpnp_vadc_linear_graph adc_graph[2];
};
/**
* struct qpnp_adc_result - Represent the result of the QPNP ADC.
* @chan: The channel number of the requested conversion.
* @adc_code: The pre-calibrated digital output of a given ADC relative to the
* the ADC reference.
* @measurement: In units specific for a given ADC; most ADC uses reference
* voltage but some ADC uses reference current. This measurement
* here is a number relative to a reference of a given ADC.
* @physical: The data meaningful for each individual channel whether it is
* voltage, current, temperature, etc.
* All voltage units are represented in micro - volts.
* -Battery temperature units are represented as 0.1 DegC.
* -PA Therm temperature units are represented as DegC.
* -PMIC Die temperature units are represented as 0.001 DegC.
*/
struct qpnp_vadc_result {
uint32_t chan;
int32_t adc_code;
int64_t measurement;
int64_t physical;
};
/**
* struct qpnp_adc_amux - AMUX properties for individual channel
* @name: Channel string name.
* @channel_num: Channel in integer used from qpnp_adc_channels.
* @chan_path_prescaling: Channel scaling performed on the input signal.
* @adc_decimation: Sampling rate desired for the channel.
* adc_scale_fn: Scaling function to convert to the data meaningful for
* each individual channel whether it is voltage, current,
* temperature, etc and compensates the channel properties.
*/
struct qpnp_vadc_amux {
char *name;
enum qpnp_vadc_channels channel_num;
enum qpnp_adc_channel_scaling_param chan_path_prescaling;
enum qpnp_adc_decimation_type adc_decimation;
enum qpnp_adc_scale_fn_type adc_scale_fn;
enum qpnp_adc_fast_avg_ctl fast_avg_setup;
enum qpnp_adc_hw_settle_time hw_settle_time;
};
/**
* struct qpnp_vadc_scaling_ratio
*
*/
static const struct qpnp_vadc_scaling_ratio qpnp_vadc_amux_scaling_ratio[] = {
{1, 1},
{1, 3},
{1, 4},
{1, 6},
{1, 20}
};
/**
* struct qpnp_vadc_scale_fn - Scaling function prototype
* @chan: Function pointer to one of the scaling functions
* which takes the adc properties, channel properties,
* and returns the physical result
*/
struct qpnp_vadc_scale_fn {
int32_t (*chan) (int32_t,
const struct qpnp_adc_properties *,
const struct qpnp_vadc_chan_properties *,
struct qpnp_vadc_result *);
};
/**
* struct qpnp_adc_drv - QPNP ADC device structure.
* @spmi - spmi device for ADC peripheral.
* @offset - base offset for the ADC peripheral.
* @adc_prop - ADC properties specific to the ADC peripheral.
* @amux_prop - AMUX properties representing the ADC peripheral.
* @adc_channels - ADC channel properties for the ADC peripheral.
* @adc_irq - IRQ number that is mapped to the ADC peripheral.
* @adc_lock - ADC lock for access to the peripheral.
* @adc_rslt_completion - ADC result notification after interrupt
* is received.
*/
struct qpnp_adc_drv {
struct spmi_device *spmi;
uint8_t slave;
uint16_t offset;
struct qpnp_adc_properties *adc_prop;
struct qpnp_vadc_amux_properties *amux_prop;
struct qpnp_vadc_amux *adc_channels;
int adc_irq;
struct mutex adc_lock;
struct completion adc_rslt_completion;
};
/**
* struct qpnp_vadc_amux_properties - QPNP VADC amux channel property.
* @amux_channel - Refer to the qpnp_vadc_channel list.
* @decimation - Sampling rate supported for the channel.
* @mode_sel - The basic mode of operation.
* @hw_settle_time - The time between AMUX being configured and the
* start of conversion.
* @fast_avg_setup - Ability to provide single result from the ADC
* that is an average of multiple measurements.
* @trigger_channel - HW trigger channel for conversion sequencer.
* @chan_prop - Represent the channel properties of the ADC.
*/
struct qpnp_vadc_amux_properties {
uint32_t amux_channel;
uint32_t decimation;
uint32_t mode_sel;
uint32_t hw_settle_time;
uint32_t fast_avg_setup;
enum qpnp_vadc_trigger trigger_channel;
struct qpnp_vadc_chan_properties chan_prop[0];
};
/* Public API */
#if defined(CONFIG_SENSORS_QPNP_ADC_VOLTAGE) \
|| defined(CONFIG_SENSORS_QPNP_ADC_VOLTAGE_MODULE)
/**
* qpnp_vadc_read() - Performs ADC read on the channel.
* @channel: Input channel to perform the ADC read.
* @result: Structure pointer of type adc_chan_result
* in which the ADC read results are stored.
*/
int32_t qpnp_vadc_read(enum qpnp_vadc_channels channel,
struct qpnp_vadc_result *result);
/**
* qpnp_vadc_conv_seq_request() - Performs ADC read on the conversion
* sequencer channel.
* @channel: Input channel to perform the ADC read.
* @result: Structure pointer of type adc_chan_result
* in which the ADC read results are stored.
*/
int32_t qpnp_vadc_conv_seq_request(
enum qpnp_vadc_trigger trigger_channel,
enum qpnp_vadc_channels channel,
struct qpnp_vadc_result *result);
/**
* qpnp_vadc_check_result() - Performs check on the ADC raw code.
* @data: Data used for verifying the range of the ADC code.
*/
int32_t qpnp_vadc_check_result(int32_t *data);
/**
* qpnp_adc_get_devicetree_data() - Abstracts the ADC devicetree data.
* @spmi: spmi ADC device.
* @adc_qpnp: spmi device tree node structure
*/
int32_t qpnp_adc_get_devicetree_data(struct spmi_device *spmi,
struct qpnp_adc_drv *adc_qpnp);
/**
* qpnp_vadc_configure() - Configure ADC device to start conversion.
* @chan_prop: Individual channel properties for the AMUX channel.
*/
int32_t qpnp_vadc_configure(
struct qpnp_vadc_amux_properties *chan_prop);
/**
* qpnp_adc_scale_default() - Scales the pre-calibrated digital output
* of an ADC to the ADC reference and compensates for the
* gain and offset.
* @adc_code: pre-calibrated digital ouput of the ADC.
* @adc_prop: adc properties of the qpnp adc such as bit resolution,
* reference voltage.
* @chan_prop: Individual channel properties to compensate the i/p scaling,
* slope and offset.
* @chan_rslt: Physical result to be stored.
*/
int32_t qpnp_adc_scale_default(int32_t adc_code,
const struct qpnp_adc_properties *adc_prop,
const struct qpnp_vadc_chan_properties *chan_prop,
struct qpnp_vadc_result *chan_rslt);
#else
static inline int32_t qpnp_vadc_read(uint32_t channel,
struct qpnp_vadc_result *result)
{ return -ENXIO; }
static inline int32_t qpnp_vadc_conv_seq_request(
enum qpnp_vadc_trigger trigger_channel,
enum qpnp_vadc_channels channel,
struct qpnp_vadc_result *result)
{ return -ENXIO; }
static inline int32_t qpnp_adc_scale_default(int32_t adc_code,
const struct qpnp_adc_properties *adc_prop,
const struct qpnp_adc_chan_properties *chan_prop,
struct qpnp_adc_chan_result *chan_rslt)
{ return -ENXIO; }
#endif
#endif