Add samsung specific changes
This commit is contained in:
@@ -961,6 +961,48 @@ config QCOM_SPMI_ADC5
|
||||
To compile this driver as a module, choose M here: the module will
|
||||
be called qcom-spmi-adc5.
|
||||
|
||||
config QCOM_SPMI_ADC5_GEN3
|
||||
tristate "Qualcomm Technologies Inc. SPMI PMIC5 GEN3 ADC"
|
||||
depends on SPMI
|
||||
select REGMAP_SPMI
|
||||
select QCOM_VADC_COMMON
|
||||
help
|
||||
This is the IIO Voltage PMIC5 Gen3 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-gen3.
|
||||
|
||||
config QCOM_SPMI_ADC5_GEN3_DEBUG_LOGGING
|
||||
bool "ADC5 GEN3 Debug Logging"
|
||||
depends on QCOM_SPMI_ADC5_GEN3
|
||||
help
|
||||
Say yes here to enable debug logging for Qualcomm Technologies, Inc.
|
||||
ADC5 Gen3 driver.
|
||||
|
||||
This should be enabled when ADC timeout or ADC conversion faults are
|
||||
seen and need to be debugged. It will support dumping ADC registers
|
||||
when these issues occur and then will trigger kernel panic to get the
|
||||
context of the issue.
|
||||
|
||||
config QTI_GLINK_ADC
|
||||
tristate "Qualcomm Technologies Inc. PMIC Glink ADC"
|
||||
depends on QTI_PMIC_GLINK
|
||||
help
|
||||
This is the IIO PMIC Glink ADC driver for Qualcomm Technologies, Inc.
|
||||
PMICs. It supports reading multiple ADC channels using the Glink
|
||||
interface to communicate with charger firmware. Channel value
|
||||
conversion is performed in firmware. Both converted and raw values
|
||||
can be read.
|
||||
|
||||
config RCAR_GYRO_ADC
|
||||
tristate "Renesas R-Car GyroADC driver"
|
||||
depends on ARCH_RCAR_GEN2 || COMPILE_TEST
|
||||
|
@@ -83,11 +83,13 @@ obj-$(CONFIG_NAU7802) += nau7802.o
|
||||
obj-$(CONFIG_NPCM_ADC) += npcm_adc.o
|
||||
obj-$(CONFIG_PALMAS_GPADC) += palmas_gpadc.o
|
||||
obj-$(CONFIG_QCOM_SPMI_ADC5) += qcom-spmi-adc5.o
|
||||
obj-$(CONFIG_QCOM_SPMI_ADC5_GEN3) += qcom-spmi-adc5-gen3.o
|
||||
obj-$(CONFIG_QCOM_SPMI_IADC) += qcom-spmi-iadc.o
|
||||
obj-$(CONFIG_QCOM_SPMI_RRADC) += qcom-spmi-rradc.o
|
||||
obj-$(CONFIG_QCOM_VADC_COMMON) += qcom-vadc-common.o
|
||||
obj-$(CONFIG_QCOM_SPMI_VADC) += qcom-spmi-vadc.o
|
||||
obj-$(CONFIG_QCOM_PM8XXX_XOADC) += qcom-pm8xxx-xoadc.o
|
||||
obj-$(CONFIG_QTI_GLINK_ADC) += qti-glink-adc.o
|
||||
obj-$(CONFIG_RCAR_GYRO_ADC) += rcar-gyroadc.o
|
||||
obj-$(CONFIG_RN5T618_ADC) += rn5t618-adc.o
|
||||
obj-$(CONFIG_ROCKCHIP_SARADC) += rockchip_saradc.o
|
||||
|
2126
drivers/iio/adc/qcom-spmi-adc5-gen3.c
Normal file
2126
drivers/iio/adc/qcom-spmi-adc5-gen3.c
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,7 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright (c) 2018, 2020, The Linux Foundation. All rights reserved.
|
||||
* Copyright (c) 2018-2021, The Linux Foundation. All rights reserved.
|
||||
* Copyright (c) 2023-2024, Qualcomm Innovation Center, Inc. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <linux/bitops.h>
|
||||
@@ -15,6 +16,7 @@
|
||||
#include <linux/math64.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mod_devicetable.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/property.h>
|
||||
#include <linux/regmap.h>
|
||||
@@ -75,9 +77,10 @@
|
||||
* 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_POLL_DELAY_MIN_US 10000
|
||||
#define ADC5_POLL_DELAY_MAX_US 10001
|
||||
#define ADC5_CONV_TIME_RETRY_POLL 40
|
||||
#define ADC5_CONV_TIME_RETRY 30
|
||||
#define ADC5_CONV_TIMEOUT msecs_to_jiffies(100)
|
||||
|
||||
/* Digital version >= 5.3 supports hw_settle_2 */
|
||||
@@ -87,7 +90,24 @@
|
||||
/* For PMIC7 */
|
||||
#define ADC_APP_SID 0x40
|
||||
#define ADC_APP_SID_MASK GENMASK(3, 0)
|
||||
#define ADC7_CONV_TIMEOUT msecs_to_jiffies(10)
|
||||
#define ADC7_CONV_TIMEOUT_MS 501
|
||||
|
||||
/* For ADC_PBS on PMIC7 with SW calibration */
|
||||
#define ADC7_SW_CALIB_PBS_CALREF_FLAG 0x57
|
||||
#define ADC7_SW_CALIB_PBS_CALREF_RDY BIT(7)
|
||||
|
||||
#define ADC7_SW_CALIB_PBS_GND_REF_D0 0x58
|
||||
#define ADC7_SW_CALIB_PBS_GND_REF_D1 0x59
|
||||
#define ADC7_SW_CALIB_PBS_VREF_VADC_DELTA_D0 0x5a
|
||||
#define ADC7_SW_CALIB_PBS_VREF_VADC_DELTA_D1 0x5b
|
||||
#define ADC7_SW_CALIB_PBS_VREF_MBG_DELTA_D0 0x5c
|
||||
#define ADC7_SW_CALIB_PBS_VREF_MBG_DELTA_D1 0x5d
|
||||
|
||||
/* For ADC_CMN on PMIC7 with SW calibration */
|
||||
#define ADC7_SW_CALIB_CMN_PBUS_WRITE_SYNC_CTL 0x4e
|
||||
#define ADC7_PBUS_WRITE_SYNC_SW_CLK_REQ BIT(2)
|
||||
#define ADC7_PBUS_WRITE_SYNC_SW_CLK_REQ_MODE BIT(1)
|
||||
#define ADC7_PBUS_WRITE_SYNC_BYPASS BIT(0)
|
||||
|
||||
enum adc5_cal_method {
|
||||
ADC5_NO_CAL = 0,
|
||||
@@ -134,6 +154,8 @@ struct adc5_channel_prop {
|
||||
* @regmap: SPMI ADC5 peripheral register map field.
|
||||
* @dev: SPMI ADC5 device.
|
||||
* @base: base address for the ADC peripheral.
|
||||
* @cmn_base: base address for the ADC_CMN peripheral, needed
|
||||
* for SW calibrated ADC.
|
||||
* @nchannels: number of ADC channels.
|
||||
* @chan_props: array of ADC channel properties.
|
||||
* @iio_chans: array of IIO channels specification.
|
||||
@@ -146,6 +168,7 @@ struct adc5_chip {
|
||||
struct regmap *regmap;
|
||||
struct device *dev;
|
||||
u16 base;
|
||||
u16 cmn_base;
|
||||
unsigned int nchannels;
|
||||
struct adc5_channel_prop *chan_props;
|
||||
struct iio_chan_spec *iio_chans;
|
||||
@@ -153,21 +176,55 @@ struct adc5_chip {
|
||||
struct completion complete;
|
||||
struct mutex lock;
|
||||
const struct adc5_data *data;
|
||||
int irq_eoc;
|
||||
};
|
||||
|
||||
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);
|
||||
int ret;
|
||||
|
||||
ret = regmap_bulk_read(adc->regmap, adc->base + offset, data, len);
|
||||
if (ret)
|
||||
dev_err(adc->dev, "adc read to register %#x of length:%d failed, ret=%d\n",
|
||||
offset, len, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
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);
|
||||
int ret;
|
||||
|
||||
ret = regmap_bulk_write(adc->regmap, adc->base + offset, data, len);
|
||||
if (ret)
|
||||
dev_err(adc->dev, "adc write to register %#x of length:%d failed, ret=%d\n",
|
||||
offset, len, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int adc5_masked_write(struct adc5_chip *adc, u16 offset, u8 mask, u8 val)
|
||||
{
|
||||
return regmap_update_bits(adc->regmap, adc->base + offset, mask, val);
|
||||
int ret;
|
||||
|
||||
ret = regmap_update_bits(adc->regmap, adc->base + offset, mask, val);
|
||||
if (ret)
|
||||
dev_err(adc->dev, "adc masked write to register %#x with mask:0x%x failed, ret=%d\n",
|
||||
offset, mask, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int adc5_cmn_write(struct adc5_chip *adc, u16 offset, u8 *data, int len)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = regmap_bulk_write(adc->regmap, adc->cmn_base + offset, data, len);
|
||||
if (ret)
|
||||
dev_err(adc->dev, "adc_cmn write to register %#x of length:%d failed, ret=%d\n",
|
||||
offset, len, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int adc5_read_voltage_data(struct adc5_chip *adc, u16 *data)
|
||||
@@ -175,11 +232,11 @@ 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));
|
||||
ret = adc5_read(adc, ADC5_USR_DATA0, &rslt_lsb, 1);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = adc5_read(adc, ADC5_USR_DATA1, &rslt_msb, sizeof(rslt_lsb));
|
||||
ret = adc5_read(adc, ADC5_USR_DATA1, &rslt_msb, 1);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
@@ -195,12 +252,17 @@ static int adc5_read_voltage_data(struct adc5_chip *adc, u16 *data)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int adc5_poll_wait_eoc(struct adc5_chip *adc)
|
||||
static int adc5_poll_wait_eoc(struct adc5_chip *adc, bool poll_only)
|
||||
{
|
||||
unsigned int count, retry = ADC5_CONV_TIME_RETRY;
|
||||
u8 status1;
|
||||
int ret;
|
||||
|
||||
if (poll_only)
|
||||
retry = ADC5_CONV_TIME_RETRY_POLL;
|
||||
else
|
||||
retry = ADC5_CONV_TIME_RETRY;
|
||||
|
||||
for (count = 0; count < retry; count++) {
|
||||
ret = adc5_read(adc, ADC5_USR_STATUS1, &status1,
|
||||
sizeof(status1));
|
||||
@@ -211,7 +273,7 @@ static int adc5_poll_wait_eoc(struct adc5_chip *adc)
|
||||
if (status1 == ADC5_USR_STATUS1_EOC)
|
||||
return 0;
|
||||
|
||||
usleep_range(ADC5_CONV_TIME_MIN_US, ADC5_CONV_TIME_MAX_US);
|
||||
usleep_range(ADC5_POLL_DELAY_MIN_US, ADC5_POLL_DELAY_MAX_US);
|
||||
}
|
||||
|
||||
return -ETIMEDOUT;
|
||||
@@ -311,6 +373,52 @@ static int adc7_configure(struct adc5_chip *adc,
|
||||
return adc5_write(adc, ADC5_USR_CONV_REQ, &conv_req, 1);
|
||||
}
|
||||
|
||||
static int adc7_sw_calib_configure(struct adc5_chip *adc,
|
||||
struct adc5_channel_prop *prop)
|
||||
{
|
||||
int ret;
|
||||
u8 buf[5], val = 0;
|
||||
|
||||
/* 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 | ADC5_USR_FAST_AVG_CTL_EN;
|
||||
|
||||
/* 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;
|
||||
|
||||
if (!adc->poll_eoc)
|
||||
reinit_completion(&adc->complete);
|
||||
|
||||
ret = adc5_write(adc, ADC5_USR_DIG_PARAM, buf, sizeof(buf));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
val = ADC7_PBUS_WRITE_SYNC_SW_CLK_REQ | ADC7_PBUS_WRITE_SYNC_SW_CLK_REQ_MODE;
|
||||
|
||||
ret = adc5_cmn_write(adc, ADC7_SW_CALIB_CMN_PBUS_WRITE_SYNC_CTL, &val, 1);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* Select CONV request */
|
||||
val = ADC5_USR_CONV_REQ_REQ;
|
||||
return adc5_write(adc, ADC5_USR_CONV_REQ, &val, 1);
|
||||
}
|
||||
|
||||
static int adc5_do_conversion(struct adc5_chip *adc,
|
||||
struct adc5_channel_prop *prop,
|
||||
struct iio_chan_spec const *chan,
|
||||
@@ -327,7 +435,7 @@ static int adc5_do_conversion(struct adc5_chip *adc,
|
||||
}
|
||||
|
||||
if (adc->poll_eoc) {
|
||||
ret = adc5_poll_wait_eoc(adc);
|
||||
ret = adc5_poll_wait_eoc(adc, true);
|
||||
if (ret) {
|
||||
dev_err(adc->dev, "EOC bit not set\n");
|
||||
goto unlock;
|
||||
@@ -337,7 +445,7 @@ static int adc5_do_conversion(struct adc5_chip *adc,
|
||||
ADC5_CONV_TIMEOUT);
|
||||
if (!ret) {
|
||||
dev_dbg(adc->dev, "Did not get completion timeout.\n");
|
||||
ret = adc5_poll_wait_eoc(adc);
|
||||
ret = adc5_poll_wait_eoc(adc, false);
|
||||
if (ret) {
|
||||
dev_err(adc->dev, "EOC bit not set\n");
|
||||
goto unlock;
|
||||
@@ -359,6 +467,8 @@ static int adc7_do_conversion(struct adc5_chip *adc,
|
||||
{
|
||||
int ret;
|
||||
u8 status;
|
||||
unsigned long rc;
|
||||
unsigned int time_pending_ms;
|
||||
|
||||
mutex_lock(&adc->lock);
|
||||
|
||||
@@ -369,14 +479,44 @@ static int adc7_do_conversion(struct adc5_chip *adc,
|
||||
}
|
||||
|
||||
/* No support for polling mode at present */
|
||||
wait_for_completion_timeout(&adc->complete, ADC7_CONV_TIMEOUT);
|
||||
rc = wait_for_completion_timeout(&adc->complete,
|
||||
msecs_to_jiffies(ADC7_CONV_TIMEOUT_MS));
|
||||
if (!rc) {
|
||||
dev_err(adc->dev, "Reading ADC channel %s timed out\n",
|
||||
prop->channel_name);
|
||||
ret = -ETIMEDOUT;
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
/*
|
||||
* As per the hardware documentation, EOC should happen within 15 ms
|
||||
* in a good case where there could be multiple conversion requests
|
||||
* going through PMIC HW arbiter for reading ADC channels. However, if
|
||||
* for some reason, one of the conversion request fails and times out,
|
||||
* worst possible delay can be 500 ms. Hence print a warning when we
|
||||
* see EOC completion happened more than 15 ms.
|
||||
*/
|
||||
time_pending_ms = jiffies_to_msecs(rc);
|
||||
if (time_pending_ms < ADC7_CONV_TIMEOUT_MS &&
|
||||
(ADC7_CONV_TIMEOUT_MS - time_pending_ms) > 15)
|
||||
dev_warn(adc->dev, "ADC channel %s EOC took %u ms\n",
|
||||
prop->channel_name,
|
||||
ADC7_CONV_TIMEOUT_MS - time_pending_ms);
|
||||
|
||||
ret = adc5_read(adc, ADC5_USR_STATUS1, &status, 1);
|
||||
if (ret)
|
||||
goto unlock;
|
||||
|
||||
if (status & ADC5_USR_STATUS1_CONV_FAULT) {
|
||||
dev_err(adc->dev, "Unexpected conversion fault\n");
|
||||
dev_err(adc->dev, "ADC channel %s unexpected conversion fault\n",
|
||||
prop->channel_name);
|
||||
ret = -EIO;
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
if (!(status & ADC5_USR_STATUS1_EOC)) {
|
||||
dev_err(adc->dev, "ADC channel %s EOC bit not set, status=%#x\n",
|
||||
prop->channel_name, status);
|
||||
ret = -EIO;
|
||||
goto unlock;
|
||||
}
|
||||
@@ -389,6 +529,59 @@ unlock:
|
||||
return ret;
|
||||
}
|
||||
|
||||
#define ADC7_SW_CALIB_CONV_TIMEOUT_MS 150
|
||||
static int adc7_sw_calib_do_conversion(struct adc5_chip *adc,
|
||||
struct adc5_channel_prop *prop, u16 *adc_code_volt)
|
||||
{
|
||||
int ret;
|
||||
unsigned long rc;
|
||||
u8 status = 0, val;
|
||||
|
||||
mutex_lock(&adc->lock);
|
||||
|
||||
ret = adc7_sw_calib_configure(adc, prop);
|
||||
if (ret) {
|
||||
dev_err(adc->dev, "ADC configure failed with %d\n", ret);
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
/* No support for polling mode at present*/
|
||||
rc = wait_for_completion_timeout(&adc->complete,
|
||||
msecs_to_jiffies(ADC7_SW_CALIB_CONV_TIMEOUT_MS));
|
||||
if (!rc) {
|
||||
dev_err(adc->dev, "Reading ADC channel %s timed out\n",
|
||||
prop->channel_name);
|
||||
ret = -ETIMEDOUT;
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
ret = adc5_read(adc, ADC5_USR_STATUS1, &status, 1);
|
||||
if (ret < 0)
|
||||
goto unlock;
|
||||
|
||||
if (!(status & ADC5_USR_STATUS1_EOC)) {
|
||||
dev_err(adc->dev, "ADC channel %s EOC bit not set, status=%#x\n",
|
||||
prop->channel_name, status);
|
||||
ret = -EIO;
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
ret = adc5_read_voltage_data(adc, adc_code_volt);
|
||||
if (ret < 0)
|
||||
goto unlock;
|
||||
|
||||
val = 0;
|
||||
ret = adc5_write(adc, ADC5_USR_EN_CTL1, &val, 1);
|
||||
if (ret < 0)
|
||||
goto unlock;
|
||||
|
||||
ret = adc5_cmn_write(adc, ADC7_SW_CALIB_CMN_PBUS_WRITE_SYNC_CTL, &val, 1);
|
||||
unlock:
|
||||
mutex_unlock(&adc->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
typedef int (*adc_do_conversion)(struct adc5_chip *adc,
|
||||
struct adc5_channel_prop *prop,
|
||||
struct iio_chan_spec const *chan,
|
||||
@@ -403,6 +596,21 @@ static irqreturn_t adc5_isr(int irq, void *dev_id)
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static struct adc5_channel_prop *adc7_get_channel(struct adc5_chip *adc,
|
||||
unsigned int num)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < adc->nchannels; i++) {
|
||||
if (adc->chan_props[i].channel == num)
|
||||
return &adc->chan_props[i];
|
||||
}
|
||||
|
||||
dev_err(adc->dev, "Invalid channel %02x\n", num);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int adc5_fwnode_xlate(struct iio_dev *indio_dev,
|
||||
const struct fwnode_reference_args *iiospec)
|
||||
{
|
||||
@@ -479,6 +687,118 @@ static int adc7_read_raw(struct iio_dev *indio_dev,
|
||||
mask, adc7_do_conversion);
|
||||
}
|
||||
|
||||
static int adc7_calib(struct adc5_chip *adc)
|
||||
{
|
||||
int ret = 0;
|
||||
u16 gnd, vref_1p25, vref_vdd;
|
||||
u8 buf[2];
|
||||
struct adc5_channel_prop *gnd_prop, *vref_1p25_prop, *vref_vdd_prop;
|
||||
|
||||
/* These channels are mandatory, they are used as reference points */
|
||||
gnd_prop = adc7_get_channel(adc, ADC7_REF_GND);
|
||||
if (!gnd_prop) {
|
||||
dev_err(adc->dev, "GND channel not defined for SW calibration\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
vref_1p25_prop = adc7_get_channel(adc, ADC7_1P25VREF);
|
||||
if (!vref_1p25_prop) {
|
||||
dev_err(adc->dev, "1.25VREF channel not defined for SW calibration\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
vref_vdd_prop = adc7_get_channel(adc, ADC7_VREF_VADC);
|
||||
if (!vref_vdd_prop) {
|
||||
dev_err(adc->dev, "VDD channel not defined for SW calibration\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
ret = adc7_sw_calib_do_conversion(adc, gnd_prop, &gnd);
|
||||
if (ret) {
|
||||
dev_err(adc->dev, "Failed to read GND channel, ret = %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = adc7_sw_calib_do_conversion(adc, vref_1p25_prop, &vref_1p25);
|
||||
if (ret) {
|
||||
dev_err(adc->dev, "Failed to read 1.25VREF channel, ret = %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = adc7_sw_calib_do_conversion(adc, vref_vdd_prop, &vref_vdd);
|
||||
if (ret) {
|
||||
dev_err(adc->dev, "Failed to read VDD channel, ret = %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
buf[0] = gnd & 0xff;
|
||||
buf[1] = gnd >> 8;
|
||||
ret = adc5_write(adc, ADC7_SW_CALIB_PBS_GND_REF_D0, buf, sizeof(buf));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
vref_vdd -= gnd;
|
||||
buf[0] = vref_vdd & 0xff;
|
||||
buf[1] = vref_vdd >> 8;
|
||||
ret = adc5_write(adc, ADC7_SW_CALIB_PBS_VREF_VADC_DELTA_D0, buf, sizeof(buf));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
vref_1p25 -= gnd;
|
||||
buf[0] = vref_1p25 & 0xff;
|
||||
buf[1] = vref_1p25 >> 8;
|
||||
ret = adc5_write(adc, ADC7_SW_CALIB_PBS_VREF_MBG_DELTA_D0, buf, sizeof(buf));
|
||||
|
||||
if (!ret)
|
||||
dev_dbg(adc->dev, "SW calibration done, gnd:0x%x vref_vdd:0x%x vref_1p25:0x%x\n",
|
||||
gnd, vref_vdd, vref_1p25);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int adc7_sw_calib_conv(struct adc5_chip *adc, struct adc5_channel_prop *prop, int *val)
|
||||
{
|
||||
int ret = 0;
|
||||
u16 adc_code_volt;
|
||||
|
||||
ret = adc7_calib(adc);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = adc7_sw_calib_do_conversion(adc, prop, &adc_code_volt);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return qcom_adc5_hw_scale(prop->scale_fn_type,
|
||||
prop->prescale,
|
||||
adc->data,
|
||||
adc_code_volt, val);
|
||||
}
|
||||
|
||||
static int adc7_sw_calib_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;
|
||||
int ret;
|
||||
|
||||
prop = &adc->chan_props[chan->address];
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_PROCESSED:
|
||||
ret = adc7_sw_calib_conv(adc, prop, 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,
|
||||
.fwnode_xlate = adc5_fwnode_xlate,
|
||||
@@ -489,6 +809,11 @@ static const struct iio_info adc7_info = {
|
||||
.fwnode_xlate = adc7_fwnode_xlate,
|
||||
};
|
||||
|
||||
static const struct iio_info adc7_sw_calib_info = {
|
||||
.read_raw = adc7_sw_calib_read_raw,
|
||||
.fwnode_xlate = adc5_fwnode_xlate,
|
||||
};
|
||||
|
||||
struct adc5_channels {
|
||||
const char *datasheet_name;
|
||||
unsigned int prescale_index;
|
||||
@@ -517,6 +842,11 @@ struct adc5_channels {
|
||||
BIT(IIO_CHAN_INFO_PROCESSED), \
|
||||
_pre, _scale) \
|
||||
|
||||
#define ADC5_CHAN_CUR(_dname, _pre, _scale) \
|
||||
ADC5_CHAN(_dname, IIO_CURRENT, \
|
||||
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", 0,
|
||||
SCALE_HW_CALIB_DEFAULT)
|
||||
@@ -543,6 +873,12 @@ static const struct adc5_channels adc5_chans_pmic[ADC5_MAX_CHANNEL] = {
|
||||
SCALE_HW_CALIB_DEFAULT)
|
||||
[ADC5_XO_THERM_100K_PU] = ADC5_CHAN_TEMP("xo_therm", 0,
|
||||
SCALE_HW_CALIB_XOTHERM)
|
||||
[ADC5_BAT_THERM_100K_PU] = ADC5_CHAN_TEMP("bat_therm_100k_pu", 0,
|
||||
SCALE_HW_CALIB_BATT_THERM_100K)
|
||||
[ADC5_BAT_THERM_30K_PU] = ADC5_CHAN_TEMP("bat_therm_30k_pu", 0,
|
||||
SCALE_HW_CALIB_BATT_THERM_30K)
|
||||
[ADC5_BAT_THERM_400K_PU] = ADC5_CHAN_TEMP("bat_therm_400k_pu", 0,
|
||||
SCALE_HW_CALIB_BATT_THERM_400K)
|
||||
[ADC5_BAT_ID_100K_PU] = ADC5_CHAN_TEMP("bat_id", 0,
|
||||
SCALE_HW_CALIB_DEFAULT)
|
||||
[ADC5_AMUX_THM1_100K_PU] = ADC5_CHAN_TEMP("amux_thm1_100k_pu", 0,
|
||||
@@ -551,8 +887,12 @@ static const struct adc5_channels adc5_chans_pmic[ADC5_MAX_CHANNEL] = {
|
||||
SCALE_HW_CALIB_THERM_100K_PULLUP)
|
||||
[ADC5_AMUX_THM3_100K_PU] = ADC5_CHAN_TEMP("amux_thm3_100k_pu", 0,
|
||||
SCALE_HW_CALIB_THERM_100K_PULLUP)
|
||||
[ADC5_AMUX_THM4_100K_PU] = ADC5_CHAN_TEMP("amux_thm4_100k_pu", 0,
|
||||
SCALE_HW_CALIB_THERM_100K_PULLUP)
|
||||
[ADC5_AMUX_THM2] = ADC5_CHAN_TEMP("amux_thm2", 0,
|
||||
SCALE_HW_CALIB_PM5_SMB_TEMP)
|
||||
[ADC5_PARALLEL_ISENSE] = ADC5_CHAN_VOLT("parallel_isense", 0,
|
||||
SCALE_HW_CALIB_PM5_CUR)
|
||||
[ADC5_GPIO1_100K_PU] = ADC5_CHAN_TEMP("gpio1_100k_pu", 0,
|
||||
SCALE_HW_CALIB_THERM_100K_PULLUP)
|
||||
[ADC5_GPIO2_100K_PU] = ADC5_CHAN_TEMP("gpio2_100k_pu", 0,
|
||||
@@ -568,10 +908,26 @@ static const struct adc5_channels adc7_chans_pmic[ADC5_MAX_CHANNEL] = {
|
||||
SCALE_HW_CALIB_DEFAULT)
|
||||
[ADC7_1P25VREF] = ADC5_CHAN_VOLT("vref_1p25", 0,
|
||||
SCALE_HW_CALIB_DEFAULT)
|
||||
[ADC7_VREF_VADC] = ADC5_CHAN_VOLT("vref_vadc", 0,
|
||||
SCALE_HW_CALIB_DEFAULT)
|
||||
[ADC7_VPH_PWR] = ADC5_CHAN_VOLT("vph_pwr", 1,
|
||||
SCALE_HW_CALIB_DEFAULT)
|
||||
[ADC7_VBAT_SNS] = ADC5_CHAN_VOLT("vbat_sns", 3,
|
||||
SCALE_HW_CALIB_DEFAULT)
|
||||
[ADC7_USB_IN_V_16] = ADC5_CHAN_VOLT("usb_in_v_div_16", 8,
|
||||
SCALE_HW_CALIB_DEFAULT)
|
||||
[ADC7_AMUX_THM3] = ADC5_CHAN_TEMP("smb_temp", 9,
|
||||
SCALE_HW_CALIB_PM7_SMB_TEMP)
|
||||
[ADC7_CHG_TEMP] = ADC5_CHAN_TEMP("chg_temp", 0,
|
||||
SCALE_HW_CALIB_PM7_CHG_TEMP)
|
||||
[ADC7_IIN_FB] = ADC5_CHAN_CUR("iin_fb", 10,
|
||||
SCALE_HW_CALIB_CUR)
|
||||
[ADC7_IIN_SMB] = ADC5_CHAN_CUR("iin_smb", 12,
|
||||
SCALE_HW_CALIB_CUR)
|
||||
[ADC7_ICHG_SMB] = ADC5_CHAN_CUR("ichg_smb", 13,
|
||||
SCALE_HW_CALIB_CUR)
|
||||
[ADC7_ICHG_FB] = ADC5_CHAN_CUR("ichg_fb", 14,
|
||||
SCALE_HW_CALIB_CUR_RAW)
|
||||
[ADC7_DIE_TEMP] = ADC5_CHAN_TEMP("die_temp", 0,
|
||||
SCALE_HW_CALIB_PMIC_THERM_PM7)
|
||||
[ADC7_AMUX_THM1_100K_PU] = ADC5_CHAN_TEMP("amux_thm1_pu2", 0,
|
||||
@@ -623,6 +979,8 @@ static const struct adc5_channels adc5_chans_rev2[ADC5_MAX_CHANNEL] = {
|
||||
SCALE_HW_CALIB_THERM_100K_PULLUP)
|
||||
[ADC5_XO_THERM_100K_PU] = ADC5_CHAN_TEMP("xo_therm_100k_pu", 0,
|
||||
SCALE_HW_CALIB_THERM_100K_PULLUP)
|
||||
[ADC5_GPIO2_100K_PU] = ADC5_CHAN_TEMP("gpio2_100k_pu", 0,
|
||||
SCALE_HW_CALIB_THERM_100K_PULLUP)
|
||||
};
|
||||
|
||||
static int adc5_get_fw_channel_data(struct adc5_chip *adc,
|
||||
@@ -717,7 +1075,8 @@ static int adc5_get_fw_channel_data(struct adc5_chip *adc,
|
||||
/* 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) ||
|
||||
adc->data->info == &adc7_info)
|
||||
adc->data->info == &adc7_info ||
|
||||
adc->data->info == &adc7_sw_calib_info)
|
||||
ret = qcom_adc5_hw_settle_time_from_dt(value, data->hw_settle_2);
|
||||
else
|
||||
ret = qcom_adc5_hw_settle_time_from_dt(value, data->hw_settle_1);
|
||||
@@ -747,6 +1106,8 @@ static int adc5_get_fw_channel_data(struct adc5_chip *adc,
|
||||
|
||||
if (fwnode_property_read_bool(fwnode, "qcom,ratiometric"))
|
||||
prop->cal_method = ADC5_RATIOMETRIC_CAL;
|
||||
else if (fwnode_property_read_bool(fwnode, "qcom,no-cal"))
|
||||
prop->cal_method = ADC5_NO_CAL;
|
||||
else
|
||||
prop->cal_method = ADC5_ABSOLUTE_CAL;
|
||||
|
||||
@@ -762,8 +1123,10 @@ static int adc5_get_fw_channel_data(struct adc5_chip *adc,
|
||||
}
|
||||
|
||||
static const struct adc5_data adc5_data_pmic = {
|
||||
.name = "pm-adc5",
|
||||
.full_scale_code_volt = 0x70e4,
|
||||
.full_scale_code_cur = 0x2710,
|
||||
.full_scale_code_raw = 0x4000,
|
||||
.adc_chans = adc5_chans_pmic,
|
||||
.info = &adc5_info,
|
||||
.decimation = (unsigned int [ADC5_DECIMATION_SAMPLES_MAX])
|
||||
@@ -777,7 +1140,9 @@ static const struct adc5_data adc5_data_pmic = {
|
||||
};
|
||||
|
||||
static const struct adc5_data adc7_data_pmic = {
|
||||
.name = "pm-adc7",
|
||||
.full_scale_code_volt = 0x70e4,
|
||||
.full_scale_code_raw = 0x4000,
|
||||
.adc_chans = adc7_chans_pmic,
|
||||
.info = &adc7_info,
|
||||
.decimation = (unsigned int [ADC5_DECIMATION_SAMPLES_MAX])
|
||||
@@ -788,9 +1153,38 @@ static const struct adc5_data adc7_data_pmic = {
|
||||
64000, 128000},
|
||||
};
|
||||
|
||||
static const struct adc5_data adc7_sw_calib_data_pmic = {
|
||||
.name = "pm-adc7",
|
||||
.full_scale_code_volt = 0x70e4,
|
||||
.full_scale_code_raw = 0x4000,
|
||||
.adc_chans = adc7_chans_pmic,
|
||||
.info = &adc7_sw_calib_info,
|
||||
.decimation = (unsigned int [ADC5_DECIMATION_SAMPLES_MAX])
|
||||
{85, 340, 1360},
|
||||
.hw_settle_2 = (unsigned int [VADC_HW_SETTLE_SAMPLES_MAX])
|
||||
{15, 100, 200, 300, 400, 500, 600, 700,
|
||||
1000, 2000, 4000, 8000, 16000, 32000,
|
||||
64000, 128000},
|
||||
};
|
||||
|
||||
static const struct adc5_data adc5_data_pmic5_lite = {
|
||||
.name = "pm-adc5-lite",
|
||||
.full_scale_code_volt = 0x70e4,
|
||||
/* On PMI632, IBAT LSB = 5A/32767 */
|
||||
.full_scale_code_cur = 5000,
|
||||
.full_scale_code_raw = 0x4000,
|
||||
.adc_chans = adc5_chans_pmic,
|
||||
.info = &adc5_info,
|
||||
.decimation = (unsigned int []) {250, 420, 840},
|
||||
.hw_settle_1 = (unsigned int []) {15, 100, 200, 300, 400, 500, 600, 700,
|
||||
800, 900, 1, 2, 4, 6, 8, 10},
|
||||
};
|
||||
|
||||
static const struct adc5_data adc5_data_pmic_rev2 = {
|
||||
.name = "pm-adc4-rev2",
|
||||
.full_scale_code_volt = 0x4000,
|
||||
.full_scale_code_cur = 0x1800,
|
||||
.full_scale_code_raw = 0x4000,
|
||||
.adc_chans = adc5_chans_rev2,
|
||||
.info = &adc5_info,
|
||||
.decimation = (unsigned int [ADC5_DECIMATION_SAMPLES_MAX])
|
||||
@@ -812,10 +1206,18 @@ static const struct of_device_id adc5_match_table[] = {
|
||||
.compatible = "qcom,spmi-adc7",
|
||||
.data = &adc7_data_pmic,
|
||||
},
|
||||
{
|
||||
.compatible = "qcom,spmi-adc7-sw-calib",
|
||||
.data = &adc7_sw_calib_data_pmic,
|
||||
},
|
||||
{
|
||||
.compatible = "qcom,spmi-adc-rev2",
|
||||
.data = &adc5_data_pmic_rev2,
|
||||
},
|
||||
{
|
||||
.compatible = "qcom,spmi-adc5-lite",
|
||||
.data = &adc5_data_pmic5_lite,
|
||||
},
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, adc5_match_table);
|
||||
@@ -881,8 +1283,11 @@ static int adc5_probe(struct platform_device *pdev)
|
||||
struct iio_dev *indio_dev;
|
||||
struct adc5_chip *adc;
|
||||
struct regmap *regmap;
|
||||
int ret, irq_eoc;
|
||||
const char *irq_name;
|
||||
const __be32 *prop_addr;
|
||||
int ret;
|
||||
u32 reg;
|
||||
u8 val;
|
||||
|
||||
regmap = dev_get_regmap(dev->parent, NULL);
|
||||
if (!regmap)
|
||||
@@ -899,7 +1304,27 @@ static int adc5_probe(struct platform_device *pdev)
|
||||
adc = iio_priv(indio_dev);
|
||||
adc->regmap = regmap;
|
||||
adc->dev = dev;
|
||||
adc->base = reg;
|
||||
|
||||
prop_addr = of_get_address(dev->of_node, 0, NULL, NULL);
|
||||
if (!prop_addr) {
|
||||
dev_err(adc->dev, "invalid IO resource\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
adc->base = be32_to_cpu(*prop_addr);
|
||||
|
||||
prop_addr = of_get_address(dev->of_node, 1, NULL, NULL);
|
||||
if (!prop_addr)
|
||||
pr_debug("invalid cmn resource\n");
|
||||
else
|
||||
adc->cmn_base = be32_to_cpu(*prop_addr);
|
||||
|
||||
|
||||
if (of_device_is_compatible(dev->of_node, "qcom,spmi-adc7-sw-calib")) {
|
||||
if (!adc->cmn_base) {
|
||||
dev_err(adc->dev, "ADC_CMN undefined\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
}
|
||||
|
||||
init_completion(&adc->complete);
|
||||
mutex_init(&adc->lock);
|
||||
@@ -908,18 +1333,35 @@ static int adc5_probe(struct platform_device *pdev)
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret, "adc get dt data failed\n");
|
||||
|
||||
irq_eoc = platform_get_irq(pdev, 0);
|
||||
if (irq_eoc < 0) {
|
||||
if (irq_eoc == -EPROBE_DEFER || irq_eoc == -EINVAL)
|
||||
return irq_eoc;
|
||||
adc->irq_eoc = platform_get_irq(pdev, 0);
|
||||
if (adc->irq_eoc < 0) {
|
||||
if (adc->irq_eoc == -EPROBE_DEFER || adc->irq_eoc == -EINVAL)
|
||||
return adc->irq_eoc;
|
||||
adc->poll_eoc = true;
|
||||
} else {
|
||||
ret = devm_request_irq(dev, irq_eoc, adc5_isr, 0,
|
||||
"pm-adc5", adc);
|
||||
irq_name = "pm-adc5";
|
||||
if (adc->data->name)
|
||||
irq_name = adc->data->name;
|
||||
|
||||
ret = devm_request_irq(dev, adc->irq_eoc, adc5_isr, 0,
|
||||
irq_name, adc);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (of_device_is_compatible(dev->of_node, "qcom,spmi-adc7-sw-calib")) {
|
||||
ret = adc7_calib(adc);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
val = ADC7_SW_CALIB_PBS_CALREF_RDY;
|
||||
ret = adc5_write(adc, ADC7_SW_CALIB_PBS_CALREF_FLAG, &val, 1);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, adc);
|
||||
|
||||
indio_dev->name = pdev->name;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->info = adc->data->info;
|
||||
@@ -929,10 +1371,39 @@ static int adc5_probe(struct platform_device *pdev)
|
||||
return devm_iio_device_register(dev, indio_dev);
|
||||
}
|
||||
|
||||
static int __maybe_unused adc_restore(struct device *dev)
|
||||
{
|
||||
int ret = 0;
|
||||
struct adc5_chip *adc = dev_get_drvdata(dev);
|
||||
|
||||
if (adc->irq_eoc > 0)
|
||||
ret = devm_request_irq(dev, adc->irq_eoc, adc5_isr, 0,
|
||||
"pm-adc5", adc);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __maybe_unused adc_freeze(struct device *dev)
|
||||
{
|
||||
struct adc5_chip *adc = dev_get_drvdata(dev);
|
||||
|
||||
if (adc->irq_eoc > 0)
|
||||
devm_free_irq(dev, adc->irq_eoc, adc);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops __maybe_unused adc_pm_ops = {
|
||||
.freeze = pm_ptr(adc_freeze),
|
||||
.thaw = pm_ptr(adc_restore),
|
||||
.restore = pm_ptr(adc_restore),
|
||||
};
|
||||
|
||||
static struct platform_driver adc5_driver = {
|
||||
.driver = {
|
||||
.name = "qcom-spmi-adc5",
|
||||
.of_match_table = adc5_match_table,
|
||||
.pm = pm_ptr(&adc_pm_ops),
|
||||
},
|
||||
.probe = adc5_probe,
|
||||
};
|
||||
|
@@ -100,6 +100,237 @@ static const struct vadc_map_pt adcmap_100k_104ef_104fb_1875_vref[] = {
|
||||
{ 46, 125000 },
|
||||
};
|
||||
|
||||
/*
|
||||
* Voltage to temperature table for 100k pull up for bat_therm with
|
||||
* Alium.
|
||||
*/
|
||||
static const struct vadc_map_pt adcmap_batt_therm_100k[] = {
|
||||
{1840, -400},
|
||||
{1835, -380},
|
||||
{1828, -360},
|
||||
{1821, -340},
|
||||
{1813, -320},
|
||||
{1803, -300},
|
||||
{1793, -280},
|
||||
{1781, -260},
|
||||
{1768, -240},
|
||||
{1753, -220},
|
||||
{1737, -200},
|
||||
{1719, -180},
|
||||
{1700, -160},
|
||||
{1679, -140},
|
||||
{1655, -120},
|
||||
{1630, -100},
|
||||
{1603, -80},
|
||||
{1574, -60},
|
||||
{1543, -40},
|
||||
{1510, -20},
|
||||
{1475, 0},
|
||||
{1438, 20},
|
||||
{1400, 40},
|
||||
{1360, 60},
|
||||
{1318, 80},
|
||||
{1276, 100},
|
||||
{1232, 120},
|
||||
{1187, 140},
|
||||
{1142, 160},
|
||||
{1097, 180},
|
||||
{1051, 200},
|
||||
{1005, 220},
|
||||
{960, 240},
|
||||
{915, 260},
|
||||
{871, 280},
|
||||
{828, 300},
|
||||
{786, 320},
|
||||
{745, 340},
|
||||
{705, 360},
|
||||
{666, 380},
|
||||
{629, 400},
|
||||
{594, 420},
|
||||
{560, 440},
|
||||
{527, 460},
|
||||
{497, 480},
|
||||
{467, 500},
|
||||
{439, 520},
|
||||
{413, 540},
|
||||
{388, 560},
|
||||
{365, 580},
|
||||
{343, 600},
|
||||
{322, 620},
|
||||
{302, 640},
|
||||
{284, 660},
|
||||
{267, 680},
|
||||
{251, 700},
|
||||
{235, 720},
|
||||
{221, 740},
|
||||
{208, 760},
|
||||
{195, 780},
|
||||
{184, 800},
|
||||
{173, 820},
|
||||
{163, 840},
|
||||
{153, 860},
|
||||
{144, 880},
|
||||
{136, 900},
|
||||
{128, 920},
|
||||
{120, 940},
|
||||
{114, 960},
|
||||
{107, 980}
|
||||
};
|
||||
|
||||
/*
|
||||
* Voltage to temperature table for 30k pull up for bat_therm with
|
||||
* Alium.
|
||||
*/
|
||||
static const struct vadc_map_pt adcmap_batt_therm_30k[] = {
|
||||
{1864, -400},
|
||||
{1863, -380},
|
||||
{1861, -360},
|
||||
{1858, -340},
|
||||
{1856, -320},
|
||||
{1853, -300},
|
||||
{1850, -280},
|
||||
{1846, -260},
|
||||
{1842, -240},
|
||||
{1837, -220},
|
||||
{1831, -200},
|
||||
{1825, -180},
|
||||
{1819, -160},
|
||||
{1811, -140},
|
||||
{1803, -120},
|
||||
{1794, -100},
|
||||
{1784, -80},
|
||||
{1773, -60},
|
||||
{1761, -40},
|
||||
{1748, -20},
|
||||
{1734, 0},
|
||||
{1718, 20},
|
||||
{1702, 40},
|
||||
{1684, 60},
|
||||
{1664, 80},
|
||||
{1643, 100},
|
||||
{1621, 120},
|
||||
{1597, 140},
|
||||
{1572, 160},
|
||||
{1546, 180},
|
||||
{1518, 200},
|
||||
{1489, 220},
|
||||
{1458, 240},
|
||||
{1426, 260},
|
||||
{1393, 280},
|
||||
{1359, 300},
|
||||
{1324, 320},
|
||||
{1288, 340},
|
||||
{1252, 360},
|
||||
{1214, 380},
|
||||
{1176, 400},
|
||||
{1138, 420},
|
||||
{1100, 440},
|
||||
{1061, 460},
|
||||
{1023, 480},
|
||||
{985, 500},
|
||||
{947, 520},
|
||||
{910, 540},
|
||||
{873, 560},
|
||||
{836, 580},
|
||||
{801, 600},
|
||||
{766, 620},
|
||||
{732, 640},
|
||||
{699, 660},
|
||||
{668, 680},
|
||||
{637, 700},
|
||||
{607, 720},
|
||||
{578, 740},
|
||||
{550, 760},
|
||||
{524, 780},
|
||||
{498, 800},
|
||||
{474, 820},
|
||||
{451, 840},
|
||||
{428, 860},
|
||||
{407, 880},
|
||||
{387, 900},
|
||||
{367, 920},
|
||||
{349, 940},
|
||||
{332, 960},
|
||||
{315, 980}
|
||||
};
|
||||
|
||||
/*
|
||||
* Voltage to temperature table for 400k pull up for bat_therm with
|
||||
* Alium.
|
||||
*/
|
||||
static const struct vadc_map_pt adcmap_batt_therm_400k[] = {
|
||||
{1744, -400},
|
||||
{1724, -380},
|
||||
{1701, -360},
|
||||
{1676, -340},
|
||||
{1648, -320},
|
||||
{1618, -300},
|
||||
{1584, -280},
|
||||
{1548, -260},
|
||||
{1509, -240},
|
||||
{1468, -220},
|
||||
{1423, -200},
|
||||
{1377, -180},
|
||||
{1328, -160},
|
||||
{1277, -140},
|
||||
{1225, -120},
|
||||
{1171, -100},
|
||||
{1117, -80},
|
||||
{1062, -60},
|
||||
{1007, -40},
|
||||
{953, -20},
|
||||
{899, 0},
|
||||
{847, 20},
|
||||
{795, 40},
|
||||
{745, 60},
|
||||
{697, 80},
|
||||
{651, 100},
|
||||
{607, 120},
|
||||
{565, 140},
|
||||
{526, 160},
|
||||
{488, 180},
|
||||
{453, 200},
|
||||
{420, 220},
|
||||
{390, 240},
|
||||
{361, 260},
|
||||
{334, 280},
|
||||
{309, 300},
|
||||
{286, 320},
|
||||
{265, 340},
|
||||
{245, 360},
|
||||
{227, 380},
|
||||
{210, 400},
|
||||
{195, 420},
|
||||
{180, 440},
|
||||
{167, 460},
|
||||
{155, 480},
|
||||
{144, 500},
|
||||
{133, 520},
|
||||
{124, 540},
|
||||
{115, 560},
|
||||
{107, 580},
|
||||
{99, 600},
|
||||
{92, 620},
|
||||
{86, 640},
|
||||
{80, 660},
|
||||
{75, 680},
|
||||
{70, 700},
|
||||
{65, 720},
|
||||
{61, 740},
|
||||
{57, 760},
|
||||
{53, 780},
|
||||
{50, 800},
|
||||
{46, 820},
|
||||
{43, 840},
|
||||
{41, 860},
|
||||
{38, 880},
|
||||
{36, 900},
|
||||
{34, 920},
|
||||
{32, 940},
|
||||
{30, 960},
|
||||
{28, 980}
|
||||
};
|
||||
|
||||
static const struct vadc_map_pt adcmap7_die_temp[] = {
|
||||
{ 857300, 160000 },
|
||||
{ 820100, 140000 },
|
||||
@@ -289,6 +520,82 @@ static const struct vadc_map_pt adcmap7_100k[] = {
|
||||
{ 2420, 130048 }
|
||||
};
|
||||
|
||||
/*
|
||||
* Resistance to temperature table for batt_therm.
|
||||
*/
|
||||
static const struct vadc_map_pt adcmap_gen3_batt_therm_100k[] = {
|
||||
{ 5319890, -400 },
|
||||
{ 4555860, -380 },
|
||||
{ 3911780, -360 },
|
||||
{ 3367320, -340 },
|
||||
{ 2905860, -320 },
|
||||
{ 2513730, -300 },
|
||||
{ 2179660, -280 },
|
||||
{ 1894360, -260 },
|
||||
{ 1650110, -240 },
|
||||
{ 1440520, -220 },
|
||||
{ 1260250, -200 },
|
||||
{ 1104850, -180 },
|
||||
{ 970600, -160 },
|
||||
{ 854370, -140 },
|
||||
{ 753530, -120 },
|
||||
{ 665860, -100 },
|
||||
{ 589490, -80 },
|
||||
{ 522830, -60 },
|
||||
{ 464540, -40 },
|
||||
{ 413470, -20 },
|
||||
{ 368640, 0 },
|
||||
{ 329220, 20 },
|
||||
{ 294490, 40 },
|
||||
{ 263850, 60 },
|
||||
{ 236770, 80 },
|
||||
{ 212790, 100 },
|
||||
{ 191530, 120 },
|
||||
{ 172640, 140 },
|
||||
{ 155840, 160 },
|
||||
{ 140880, 180 },
|
||||
{ 127520, 200 },
|
||||
{ 115590, 220 },
|
||||
{ 104910, 240 },
|
||||
{ 95350, 260 },
|
||||
{ 86760, 280 },
|
||||
{ 79050, 300 },
|
||||
{ 72110, 320 },
|
||||
{ 65860, 340 },
|
||||
{ 60220, 360 },
|
||||
{ 55130, 380 },
|
||||
{ 50520, 400 },
|
||||
{ 46350, 420 },
|
||||
{ 42570, 440 },
|
||||
{ 39140, 460 },
|
||||
{ 36030, 480 },
|
||||
{ 33190, 500 },
|
||||
{ 30620, 520 },
|
||||
{ 28260, 540 },
|
||||
{ 26120, 560 },
|
||||
{ 24160, 580 },
|
||||
{ 22370, 600 },
|
||||
{ 20730, 620 },
|
||||
{ 19230, 640 },
|
||||
{ 17850, 660 },
|
||||
{ 16580, 680 },
|
||||
{ 15420, 700 },
|
||||
{ 14350, 720 },
|
||||
{ 13370, 740 },
|
||||
{ 12470, 760 },
|
||||
{ 11630, 780 },
|
||||
{ 10860, 800 },
|
||||
{ 10150, 820 },
|
||||
{ 9490, 840 },
|
||||
{ 8880, 860 },
|
||||
{ 8320, 880 },
|
||||
{ 7800, 900 },
|
||||
{ 7310, 920 },
|
||||
{ 6860, 940 },
|
||||
{ 6450, 960 },
|
||||
{ 6060, 980 }
|
||||
};
|
||||
|
||||
static const struct u32_fract adc5_prescale_ratios[] = {
|
||||
{ .numerator = 1, .denominator = 1 },
|
||||
{ .numerator = 1, .denominator = 3 },
|
||||
@@ -299,16 +606,54 @@ static const struct u32_fract adc5_prescale_ratios[] = {
|
||||
{ .numerator = 10, .denominator = 81 },
|
||||
{ .numerator = 1, .denominator = 10 },
|
||||
{ .numerator = 1, .denominator = 16 },
|
||||
{ .numerator = 40, .denominator = 41 }, /* PM7_SMB_TEMP */
|
||||
/* Prescale ratios for current channels below */
|
||||
{ .numerator = 32, .denominator = 100 }, /* IIN_FB, IIN_SMB */
|
||||
{ .numerator = 16, .denominator = 100 }, /* ICHG_SMB */
|
||||
{ .numerator = 1280, .denominator = 4100 }, /* IIN_SMB_new */
|
||||
{ .numerator = 640, .denominator = 4100 }, /* ICHG_SMB_new */
|
||||
{ .numerator = 1000, .denominator = 305185 }, /* ICHG_FB */
|
||||
{ .numerator = 1000, .denominator = 610370 }, /* ICHG_FB_2X, ICHG_FB for ADC5_GEN4 */
|
||||
{ .numerator = 1000, .denominator = 366220 }, /* ICHG_FB for ADC5_GEN3 */
|
||||
{ .numerator = 1000, .denominator = 732440 }, /* ICHG_FB_2X for ADC5_GEN3 */
|
||||
{ .numerator = 1000, .denominator = 1220740 }, /* ICHG_FB_2X for ADC5_GEN4*/
|
||||
};
|
||||
|
||||
static int qcom_vadc_scale_hw_calib_volt(
|
||||
const struct u32_fract *prescale,
|
||||
const struct adc5_data *data,
|
||||
u16 adc_code, int *result_uv);
|
||||
/* Current scaling for PMIC7 */
|
||||
static int qcom_vadc_scale_hw_calib_current(
|
||||
const struct u32_fract *prescale,
|
||||
const struct adc5_data *data,
|
||||
u16 adc_code, int *result_ua);
|
||||
/* Raw current for PMIC7 */
|
||||
static int qcom_vadc_scale_hw_calib_current_raw(
|
||||
const struct u32_fract *prescale,
|
||||
const struct adc5_data *data,
|
||||
u16 adc_code, int *result_ua);
|
||||
/* Current scaling for PMIC5 */
|
||||
static int qcom_vadc5_scale_hw_calib_current(
|
||||
const struct u32_fract *prescale,
|
||||
const struct adc5_data *data,
|
||||
u16 adc_code, int *result_ua);
|
||||
static int qcom_vadc_scale_hw_calib_therm(
|
||||
const struct u32_fract *prescale,
|
||||
const struct adc5_data *data,
|
||||
u16 adc_code, int *result_mdec);
|
||||
static int qcom_vadc_scale_hw_calib_batt_therm_100(
|
||||
const struct u32_fract *prescale,
|
||||
const struct adc5_data *data,
|
||||
u16 adc_code, int *result_mdec);
|
||||
static int qcom_vadc_scale_hw_calib_batt_therm_30(
|
||||
const struct u32_fract *prescale,
|
||||
const struct adc5_data *data,
|
||||
u16 adc_code, int *result_mdec);
|
||||
static int qcom_vadc_scale_hw_calib_batt_therm_400(
|
||||
const struct u32_fract *prescale,
|
||||
const struct adc5_data *data,
|
||||
u16 adc_code, int *result_mdec);
|
||||
static int qcom_vadc7_scale_hw_calib_therm(
|
||||
const struct u32_fract *prescale,
|
||||
const struct adc5_data *data,
|
||||
@@ -317,10 +662,38 @@ static int qcom_vadc_scale_hw_smb_temp(
|
||||
const struct u32_fract *prescale,
|
||||
const struct adc5_data *data,
|
||||
u16 adc_code, int *result_mdec);
|
||||
static int qcom_vadc_scale_hw_pm7_smb_temp(
|
||||
const struct u32_fract *prescale,
|
||||
const struct adc5_data *data,
|
||||
u16 adc_code, int *result_mdec);
|
||||
static int qcom_vadc_scale_hw_smb1398_temp(
|
||||
const struct u32_fract *prescale,
|
||||
const struct adc5_data *data,
|
||||
u16 adc_code, int *result_mdec);
|
||||
static int qcom_vadc_scale_hw_pm2250_s3_die_temp(
|
||||
const struct u32_fract *prescale,
|
||||
const struct adc5_data *data,
|
||||
u16 adc_code, int *result_mdec);
|
||||
static int qcom_adc5_gen3_scale_hw_calib_batt_therm_100(
|
||||
const struct u32_fract *prescale,
|
||||
const struct adc5_data *data,
|
||||
u16 adc_code, int *result_mdec);
|
||||
static int qcom_adc5_gen3_scale_hw_calib_batt_id_100(
|
||||
const struct u32_fract *prescale,
|
||||
const struct adc5_data *data,
|
||||
u16 adc_code, int *result_mdec);
|
||||
static int qcom_adc5_gen3_scale_hw_calib_usb_in_current(
|
||||
const struct u32_fract *prescale,
|
||||
const struct adc5_data *data,
|
||||
u16 adc_code, int *result_mdec);
|
||||
static int qcom_vadc_scale_hw_chg5_temp(
|
||||
const struct u32_fract *prescale,
|
||||
const struct adc5_data *data,
|
||||
u16 adc_code, int *result_mdec);
|
||||
static int qcom_vadc_scale_hw_pm7_chg_temp(
|
||||
const struct u32_fract *prescale,
|
||||
const struct adc5_data *data,
|
||||
u16 adc_code, int *result_mdec);
|
||||
static int qcom_vadc_scale_hw_calib_die_temp(
|
||||
const struct u32_fract *prescale,
|
||||
const struct adc5_data *data,
|
||||
@@ -329,10 +702,27 @@ static int qcom_vadc7_scale_hw_calib_die_temp(
|
||||
const struct u32_fract *prescale,
|
||||
const struct adc5_data *data,
|
||||
u16 adc_code, int *result_mdec);
|
||||
static int qcom_adc5_gen4_scale_hw_calib_batt_therm_10(
|
||||
const struct u32_fract *prescale,
|
||||
const struct adc5_data *data,
|
||||
u16 adc_code, int *result_mdec);
|
||||
static int qcom_adc5_gen4_scale_hw_calib_batt_id_10(
|
||||
const struct u32_fract *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_CUR] = {qcom_vadc_scale_hw_calib_current},
|
||||
[SCALE_HW_CALIB_CUR_RAW] = {qcom_vadc_scale_hw_calib_current_raw},
|
||||
[SCALE_HW_CALIB_PM5_CUR] = {qcom_vadc5_scale_hw_calib_current},
|
||||
[SCALE_HW_CALIB_THERM_100K_PULLUP] = {qcom_vadc_scale_hw_calib_therm},
|
||||
[SCALE_HW_CALIB_BATT_THERM_100K] = {
|
||||
qcom_vadc_scale_hw_calib_batt_therm_100},
|
||||
[SCALE_HW_CALIB_BATT_THERM_30K] = {
|
||||
qcom_vadc_scale_hw_calib_batt_therm_30},
|
||||
[SCALE_HW_CALIB_BATT_THERM_400K] = {
|
||||
qcom_vadc_scale_hw_calib_batt_therm_400},
|
||||
[SCALE_HW_CALIB_XOTHERM] = {qcom_vadc_scale_hw_calib_therm},
|
||||
[SCALE_HW_CALIB_THERM_100K_PU_PM7] = {
|
||||
qcom_vadc7_scale_hw_calib_therm},
|
||||
@@ -341,6 +731,15 @@ static struct qcom_adc5_scale_type scale_adc5_fn[] = {
|
||||
qcom_vadc7_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},
|
||||
[SCALE_HW_CALIB_PM5_SMB1398_TEMP] = {qcom_vadc_scale_hw_smb1398_temp},
|
||||
[SCALE_HW_CALIB_PM2250_S3_DIE_TEMP] = {qcom_vadc_scale_hw_pm2250_s3_die_temp},
|
||||
[SCALE_HW_CALIB_PM5_GEN3_BATT_THERM_100K] = {qcom_adc5_gen3_scale_hw_calib_batt_therm_100},
|
||||
[SCALE_HW_CALIB_PM5_GEN3_BATT_ID_100K] = {qcom_adc5_gen3_scale_hw_calib_batt_id_100},
|
||||
[SCALE_HW_CALIB_PM5_GEN3_USB_IN_I] = {qcom_adc5_gen3_scale_hw_calib_usb_in_current},
|
||||
[SCALE_HW_CALIB_PM7_SMB_TEMP] = {qcom_vadc_scale_hw_pm7_smb_temp},
|
||||
[SCALE_HW_CALIB_PM7_CHG_TEMP] = {qcom_vadc_scale_hw_pm7_chg_temp},
|
||||
[SCALE_HW_CALIB_PM5_GEN4_BATT_THERM_10K] = {qcom_adc5_gen4_scale_hw_calib_batt_therm_10},
|
||||
[SCALE_HW_CALIB_PM5_GEN4_BATT_ID_10K] = {qcom_adc5_gen4_scale_hw_calib_batt_id_10},
|
||||
};
|
||||
|
||||
static int qcom_vadc_map_voltage_temp(const struct vadc_map_pt *pts,
|
||||
@@ -530,20 +929,24 @@ static int qcom_vadc_scale_code_voltage_factor(u16 adc_code,
|
||||
return (int) voltage;
|
||||
}
|
||||
|
||||
static s64 adc_code_to_resistance(u16 adc_code, int pull_up, u16 full_scale_code)
|
||||
{
|
||||
/* Resistance = (ADC code * R_PULLUP) / (full_scale_code - ADC code) */
|
||||
return div64_s64((s64) adc_code * pull_up, full_scale_code - adc_code);
|
||||
}
|
||||
|
||||
static int qcom_vadc7_scale_hw_calib_therm(
|
||||
const struct u32_fract *prescale,
|
||||
const struct adc5_data *data,
|
||||
u16 adc_code, int *result_mdec)
|
||||
{
|
||||
s64 resistance = adc_code;
|
||||
s64 resistance;
|
||||
int ret, result;
|
||||
|
||||
if (adc_code >= RATIO_MAX_ADC7)
|
||||
if (adc_code >= data->full_scale_code_raw)
|
||||
return -EINVAL;
|
||||
|
||||
/* (ADC code * R_PULLUP (100Kohm)) / (full_scale_code - ADC code)*/
|
||||
resistance *= R_PU_100K;
|
||||
resistance = div64_s64(resistance, RATIO_MAX_ADC7 - adc_code);
|
||||
resistance = adc_code_to_resistance(adc_code, R_PU_100K, data->full_scale_code_raw);
|
||||
|
||||
ret = qcom_vadc_map_voltage_temp(adcmap7_100k,
|
||||
ARRAY_SIZE(adcmap7_100k),
|
||||
@@ -556,6 +959,69 @@ static int qcom_vadc7_scale_hw_calib_therm(
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int qcom_vadc_scale_hw_calib_current_raw(
|
||||
const struct u32_fract *prescale,
|
||||
const struct adc5_data *data,
|
||||
u16 adc_code, int *result_ua)
|
||||
{
|
||||
s64 temp;
|
||||
|
||||
if (!prescale->numerator)
|
||||
return -EINVAL;
|
||||
|
||||
temp = div_s64((s64)(s16)adc_code * prescale->denominator,
|
||||
prescale->numerator);
|
||||
*result_ua = (int) temp;
|
||||
pr_debug("raw adc_code: %#x result_ua: %d\n", adc_code, *result_ua);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int qcom_vadc_scale_hw_calib_current(
|
||||
const struct u32_fract *prescale,
|
||||
const struct adc5_data *data,
|
||||
u16 adc_code, int *result_ua)
|
||||
{
|
||||
u32 adc_vdd_ref_mv = 1875;
|
||||
s64 voltage;
|
||||
|
||||
if (!prescale->numerator)
|
||||
return -EINVAL;
|
||||
|
||||
/* (ADC code * vref_vadc (1.875V)) / full_scale_code */
|
||||
voltage = (s64)(s16) adc_code * adc_vdd_ref_mv * 1000;
|
||||
voltage = div_s64(voltage, data->full_scale_code_volt);
|
||||
voltage = div_s64(voltage * prescale->denominator, prescale->numerator);
|
||||
*result_ua = (int) voltage;
|
||||
pr_debug("adc_code: %#x result_ua: %d\n", adc_code, *result_ua);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int qcom_vadc5_scale_hw_calib_current(
|
||||
const struct u32_fract *prescale,
|
||||
const struct adc5_data *data,
|
||||
u16 adc_code, int *result_ua)
|
||||
{
|
||||
s64 voltage = 0, result = 0;
|
||||
bool positive = true;
|
||||
|
||||
if (adc_code & ADC5_USR_DATA_CHECK) {
|
||||
adc_code = ~adc_code + 1;
|
||||
positive = false;
|
||||
}
|
||||
|
||||
voltage = (s64)(s16) adc_code * data->full_scale_code_cur * 1000;
|
||||
voltage = div64_s64(voltage, VADC5_MAX_CODE);
|
||||
result = div64_s64(voltage * prescale->denominator, prescale->numerator);
|
||||
*result_ua = result;
|
||||
|
||||
if (!positive)
|
||||
*result_ua = -result;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int qcom_vadc_scale_hw_calib_volt(
|
||||
const struct u32_fract *prescale,
|
||||
const struct adc5_data *data,
|
||||
@@ -583,6 +1049,54 @@ static int qcom_vadc_scale_hw_calib_therm(
|
||||
voltage, result_mdec);
|
||||
}
|
||||
|
||||
static int qcom_vadc_scale_hw_calib_batt_therm_100(
|
||||
const struct u32_fract *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_batt_therm_100k,
|
||||
ARRAY_SIZE(adcmap_batt_therm_100k),
|
||||
voltage, result_mdec);
|
||||
}
|
||||
|
||||
static int qcom_vadc_scale_hw_calib_batt_therm_30(
|
||||
const struct u32_fract *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_batt_therm_30k,
|
||||
ARRAY_SIZE(adcmap_batt_therm_30k),
|
||||
voltage, result_mdec);
|
||||
}
|
||||
|
||||
static int qcom_vadc_scale_hw_calib_batt_therm_400(
|
||||
const struct u32_fract *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_batt_therm_400k,
|
||||
ARRAY_SIZE(adcmap_batt_therm_400k),
|
||||
voltage, result_mdec);
|
||||
}
|
||||
|
||||
static int qcom_vadc_scale_hw_calib_die_temp(
|
||||
const struct u32_fract *prescale,
|
||||
const struct adc5_data *data,
|
||||
@@ -610,6 +1124,47 @@ static int qcom_vadc7_scale_hw_calib_die_temp(
|
||||
voltage, result_mdec);
|
||||
}
|
||||
|
||||
static int qcom_vadc_scale_hw_pm7_chg_temp(
|
||||
const struct u32_fract *prescale,
|
||||
const struct adc5_data *data,
|
||||
u16 adc_code, int *result_mdec)
|
||||
{
|
||||
s64 temp;
|
||||
int result_uv;
|
||||
|
||||
result_uv = qcom_vadc_scale_code_voltage_factor(adc_code,
|
||||
prescale, data, 1);
|
||||
|
||||
/* T(C) = Vadc/0.0033 – 277.12 */
|
||||
temp = div_s64((30303LL * result_uv) - (27712 * 1000000LL), 100000);
|
||||
pr_debug("adc_code: %u result_uv: %d temp: %lld\n", adc_code, result_uv,
|
||||
temp);
|
||||
*result_mdec = temp > 0 ? temp : 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int qcom_vadc_scale_hw_pm7_smb_temp(
|
||||
const struct u32_fract *prescale,
|
||||
const struct adc5_data *data,
|
||||
u16 adc_code, int *result_mdec)
|
||||
{
|
||||
s64 temp;
|
||||
int result_uv;
|
||||
|
||||
result_uv = qcom_vadc_scale_code_voltage_factor(adc_code,
|
||||
prescale, data, 1);
|
||||
|
||||
/* T(C) = 25 + (25*Vadc - 24.885) / 0.0894 */
|
||||
temp = div_s64(((25000LL * result_uv) - (24885 * 1000000LL)) * 10000,
|
||||
894 * 1000000) + 25000;
|
||||
pr_debug("adc_code: %#x result_uv: %d temp: %lld\n", adc_code,
|
||||
result_uv, temp);
|
||||
*result_mdec = temp > 0 ? temp : 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int qcom_vadc_scale_hw_smb_temp(
|
||||
const struct u32_fract *prescale,
|
||||
const struct adc5_data *data,
|
||||
@@ -622,6 +1177,125 @@ static int qcom_vadc_scale_hw_smb_temp(
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int qcom_vadc_scale_hw_smb1398_temp(
|
||||
const struct u32_fract *prescale,
|
||||
const struct adc5_data *data,
|
||||
u16 adc_code, int *result_mdec)
|
||||
{
|
||||
s64 voltage = 0, adc_vdd_ref_mv = 1875;
|
||||
u64 temp;
|
||||
|
||||
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) {
|
||||
temp = voltage * prescale->denominator;
|
||||
temp *= 100;
|
||||
do_div(temp, prescale->numerator * PMIC5_SMB1398_TEMP_SCALE_FACTOR);
|
||||
voltage = temp;
|
||||
} else {
|
||||
voltage = 0;
|
||||
}
|
||||
|
||||
voltage = voltage - PMIC5_SMB1398_TEMP_CONSTANT;
|
||||
*result_mdec = voltage;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int qcom_vadc_scale_hw_pm2250_s3_die_temp(
|
||||
const struct u32_fract *prescale,
|
||||
const struct adc5_data *data,
|
||||
u16 adc_code, int *result_mdec)
|
||||
{
|
||||
s64 voltage = 0, adc_vdd_ref_mv = 1875;
|
||||
|
||||
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->denominator;
|
||||
voltage = div64_s64(voltage, prescale->numerator);
|
||||
} else {
|
||||
voltage = 0;
|
||||
}
|
||||
|
||||
voltage = PMIC5_PM2250_S3_DIE_TEMP_CONSTANT - voltage;
|
||||
voltage *= 100000;
|
||||
voltage = div64_s64(voltage, PMIC5_PM2250_S3_DIE_TEMP_SCALE_FACTOR);
|
||||
|
||||
*result_mdec = voltage;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int qcom_adc5_gen3_scale_hw_calib_batt_therm_100(
|
||||
const struct u32_fract *prescale,
|
||||
const struct adc5_data *data,
|
||||
u16 adc_code, int *result_mdec)
|
||||
{
|
||||
s64 resistance;
|
||||
int ret, result = 0;
|
||||
|
||||
if (adc_code >= data->full_scale_code_raw)
|
||||
return -EINVAL;
|
||||
|
||||
resistance = adc_code_to_resistance(adc_code, R_PU_100K, data->full_scale_code_raw);
|
||||
|
||||
ret = qcom_vadc_map_voltage_temp(adcmap_gen3_batt_therm_100k,
|
||||
ARRAY_SIZE(adcmap_gen3_batt_therm_100k),
|
||||
resistance, &result);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
*result_mdec = result;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int qcom_adc5_gen3_scale_hw_calib_batt_id_100(
|
||||
const struct u32_fract *prescale,
|
||||
const struct adc5_data *data,
|
||||
u16 adc_code, int *result_mdec)
|
||||
{
|
||||
if (adc_code >= data->full_scale_code_raw)
|
||||
return -EINVAL;
|
||||
|
||||
*result_mdec = (int)adc_code_to_resistance(adc_code, R_PU_100K, data->full_scale_code_raw);
|
||||
|
||||
return 0;
|
||||
};
|
||||
|
||||
static int qcom_adc5_gen3_scale_hw_calib_usb_in_current(
|
||||
const struct u32_fract *prescale,
|
||||
const struct adc5_data *data,
|
||||
u16 adc_code, int *result_ua)
|
||||
{
|
||||
s64 voltage = 0, result = 0;
|
||||
bool positive = true;
|
||||
|
||||
if (adc_code & ADC5_USR_DATA_CHECK) {
|
||||
adc_code = ~adc_code + 1;
|
||||
positive = false;
|
||||
}
|
||||
|
||||
voltage = (s64)(s16) adc_code * 1000000;
|
||||
voltage = div64_s64(voltage, PMIC5_GEN3_USB_IN_I_SCALE_FACTOR);
|
||||
result = div64_s64(voltage * prescale->denominator, prescale->numerator);
|
||||
*result_ua = (int)result;
|
||||
|
||||
if (!positive)
|
||||
*result_ua = -(int)result;
|
||||
|
||||
return 0;
|
||||
};
|
||||
|
||||
static int qcom_vadc_scale_hw_chg5_temp(
|
||||
const struct u32_fract *prescale,
|
||||
const struct adc5_data *data,
|
||||
@@ -634,6 +1308,132 @@ static int qcom_vadc_scale_hw_chg5_temp(
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int qcom_adc5_gen4_scale_hw_calib_batt_therm_10(
|
||||
const struct u32_fract *prescale,
|
||||
const struct adc5_data *data,
|
||||
u16 adc_code, int *result_mdec)
|
||||
{
|
||||
s64 resistance;
|
||||
int ret, result = 0;
|
||||
|
||||
if (adc_code >= data->full_scale_code_raw)
|
||||
return -EINVAL;
|
||||
|
||||
resistance = adc_code_to_resistance(adc_code, R_PU_10K, data->full_scale_code_raw);
|
||||
|
||||
ret = qcom_vadc_map_voltage_temp(adcmap_gen3_batt_therm_100k,
|
||||
ARRAY_SIZE(adcmap_gen3_batt_therm_100k),
|
||||
resistance, &result);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
*result_mdec = result;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int qcom_adc5_gen4_scale_hw_calib_batt_id_10(
|
||||
const struct u32_fract *prescale,
|
||||
const struct adc5_data *data,
|
||||
u16 adc_code, int *result_mdec)
|
||||
{
|
||||
if (adc_code >= data->full_scale_code_raw)
|
||||
return -EINVAL;
|
||||
|
||||
*result_mdec = (int)adc_code_to_resistance(adc_code, R_PU_10K, data->full_scale_code_raw);
|
||||
|
||||
return 0;
|
||||
};
|
||||
|
||||
void adc_tm_scale_therm_voltage_100k_gen3(struct adc_tm_config *param, const struct adc5_data *data)
|
||||
{
|
||||
int temp, ret;
|
||||
int64_t resistance = 0;
|
||||
|
||||
/*
|
||||
* High temperature maps to lower threshold voltage.
|
||||
* Same API can be used for resistance-temperature table
|
||||
*/
|
||||
resistance = qcom_vadc_map_temp_voltage(adcmap7_100k,
|
||||
ARRAY_SIZE(adcmap7_100k),
|
||||
param->high_thr_temp);
|
||||
|
||||
param->low_thr_voltage = resistance * data->full_scale_code_raw;
|
||||
param->low_thr_voltage = div64_s64(param->low_thr_voltage,
|
||||
(resistance + R_PU_100K));
|
||||
|
||||
/*
|
||||
* low_thr_voltage is ADC raw code corresponding to upper temperature
|
||||
* threshold.
|
||||
* Instead of returning the ADC raw code obtained at this point,we first
|
||||
* do a forward conversion on the (low voltage / high temperature) threshold code,
|
||||
* to temperature, to check if that code, when read by TM, would translate to
|
||||
* a temperature greater than or equal to the upper temperature limit (which is
|
||||
* expected). If it is instead lower than the upper limit (not expected for correct
|
||||
* TM functionality), we lower the raw code of the threshold written by 1
|
||||
* to ensure TM does see a violation when it reads raw code corresponding
|
||||
* to the upper limit temperature specified.
|
||||
*/
|
||||
ret = qcom_vadc7_scale_hw_calib_therm(NULL, data, param->low_thr_voltage, &temp);
|
||||
if (ret < 0)
|
||||
return;
|
||||
|
||||
if (temp < param->high_thr_temp)
|
||||
param->low_thr_voltage--;
|
||||
|
||||
/*
|
||||
* Low temperature maps to higher threshold voltage
|
||||
* Same API can be used for resistance-temperature table
|
||||
*/
|
||||
resistance = qcom_vadc_map_temp_voltage(adcmap7_100k,
|
||||
ARRAY_SIZE(adcmap7_100k),
|
||||
param->low_thr_temp);
|
||||
|
||||
param->high_thr_voltage = resistance * data->full_scale_code_raw;
|
||||
param->high_thr_voltage = div64_s64(param->high_thr_voltage,
|
||||
(resistance + R_PU_100K));
|
||||
|
||||
/*
|
||||
* high_thr_voltage is ADC raw code corresponding to lower temperature
|
||||
* threshold.
|
||||
* Similar to what is done above for low_thr voltage, we first
|
||||
* do a forward conversion on the (high voltage / low temperature)threshold code,
|
||||
* to temperature, to check if that code, when read by TM, would translate to a
|
||||
* temperature less than or equal to the lower temperature limit (which is expected).
|
||||
* If it is instead greater than the lower limit (not expected for correct
|
||||
* TM functionality), we increase the raw code of the threshold written by 1
|
||||
* to ensure TM does see a violation when it reads raw code corresponding
|
||||
* to the lower limit temperature specified.
|
||||
*/
|
||||
ret = qcom_vadc7_scale_hw_calib_therm(NULL, data, param->high_thr_voltage, &temp);
|
||||
if (ret < 0)
|
||||
return;
|
||||
|
||||
if (temp > param->low_thr_temp)
|
||||
param->high_thr_voltage++;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(adc_tm_scale_therm_voltage_100k_gen3);
|
||||
|
||||
int32_t adc_tm_absolute_rthr_gen3(struct adc_tm_config *tm_config)
|
||||
{
|
||||
int64_t low_thr = 0, high_thr = 0;
|
||||
|
||||
low_thr = tm_config->low_thr_voltage;
|
||||
low_thr *= ADC5_FULL_SCALE_CODE;
|
||||
|
||||
low_thr = div64_s64(low_thr, ADC_VDD_REF);
|
||||
tm_config->low_thr_voltage = low_thr;
|
||||
|
||||
high_thr = tm_config->high_thr_voltage;
|
||||
high_thr *= ADC5_FULL_SCALE_CODE;
|
||||
|
||||
high_thr = div64_s64(high_thr, ADC_VDD_REF);
|
||||
tm_config->high_thr_voltage = high_thr;
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(adc_tm_absolute_rthr_gen3);
|
||||
|
||||
int qcom_vadc_scale(enum vadc_scale_fn_type scaletype,
|
||||
const struct vadc_linear_graph *calib_graph,
|
||||
const struct u32_fract *prescale,
|
||||
|
361
drivers/iio/adc/qti-glink-adc.c
Normal file
361
drivers/iio/adc/qti-glink-adc.c
Normal file
@@ -0,0 +1,361 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/property.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/soc/qcom/qti_pmic_glink.h>
|
||||
|
||||
#define MSG_OWNER_SMB_ADC 32784
|
||||
#define MSG_TYPE_REQ_RESP 1
|
||||
#define SMB_ADC_READ_REQ_OP 0x49
|
||||
#define ADC_READ_WAIT_TIME_MS 1000
|
||||
|
||||
enum glink_adc_channel {
|
||||
GLINK_ADC_CHAN_IIN = 1,
|
||||
GLINK_ADC_CHAN_ICHG = 2,
|
||||
GLINK_ADC_CHAN_DIE_TEMP = 3,
|
||||
GLINK_ADC_CHAN_MAX
|
||||
};
|
||||
|
||||
enum smb_adc_read_status {
|
||||
SMB_ADC_READ_STATUS_SUCCESS = 0,
|
||||
SMB_ADC_READ_STATUS_ERR_NO_OPCODE = 0x100,
|
||||
SMB_ADC_READ_STATUS_ERR_FAILED = 0x200,
|
||||
SMB_ADC_READ_STATUS_ERR_NO_PMIC = 0x201,
|
||||
SMB_ADC_READ_STATUS_ERR_INVALID_PARAM = 0x202,
|
||||
};
|
||||
|
||||
struct smb_adc_read_req_msg {
|
||||
struct pmic_glink_hdr hdr;
|
||||
u32 bus_id;
|
||||
u32 pmic_id;
|
||||
u32 chan;
|
||||
};
|
||||
|
||||
struct smb_adc_read_resp_msg {
|
||||
struct pmic_glink_hdr hdr;
|
||||
u32 bus_id;
|
||||
u32 pmic_id;
|
||||
u32 chan;
|
||||
u32 raw_data;
|
||||
u32 conv_data;
|
||||
u32 status;
|
||||
};
|
||||
|
||||
struct glink_adc_dev {
|
||||
struct pmic_glink_client *client;
|
||||
struct device *dev;
|
||||
struct mutex lock;
|
||||
struct completion ack;
|
||||
struct iio_chan_spec *iio_chans;
|
||||
unsigned int nchannels;
|
||||
struct smb_adc_read_resp_msg read_msg;
|
||||
};
|
||||
|
||||
#define ADC_GLINK_CHAN(hwchan) FIELD_GET(GENMASK(7, 0), hwchan)
|
||||
#define ADC_GLINK_PMIC_ID(hwchan) FIELD_GET(GENMASK(15, 8), hwchan)
|
||||
#define ADC_GLINK_BUS_ID(hwchan) FIELD_GET(GENMASK(31, 16), hwchan)
|
||||
|
||||
static void glink_adc_handle_read_resp(struct glink_adc_dev *adc,
|
||||
struct smb_adc_read_resp_msg *read_resp,
|
||||
size_t len)
|
||||
{
|
||||
if (len != sizeof(*read_resp)) {
|
||||
dev_err(adc->dev, "Invalid read response, glink packet size=%zu\n",
|
||||
len);
|
||||
return;
|
||||
}
|
||||
|
||||
memcpy(&adc->read_msg, read_resp, sizeof(adc->read_msg));
|
||||
|
||||
complete(&adc->ack);
|
||||
}
|
||||
|
||||
static int glink_adc_callback(void *priv, void *data, size_t len)
|
||||
{
|
||||
struct glink_adc_dev *adc = priv;
|
||||
struct pmic_glink_hdr *hdr = data;
|
||||
|
||||
dev_dbg(adc->dev, "owner: %u type: %u opcode: %#x len: %zu\n",
|
||||
hdr->owner, hdr->type, hdr->opcode, len);
|
||||
|
||||
switch (hdr->opcode) {
|
||||
case SMB_ADC_READ_REQ_OP:
|
||||
glink_adc_handle_read_resp(adc, data, len);
|
||||
break;
|
||||
default:
|
||||
dev_err(adc->dev, "Unknown opcode %u\n", hdr->opcode);
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int glink_adc_read_channel(struct glink_adc_dev *adc,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *conv_data, int *raw_data)
|
||||
{
|
||||
struct smb_adc_read_req_msg msg = {{0}};
|
||||
int ret;
|
||||
|
||||
msg.hdr.owner = MSG_OWNER_SMB_ADC;
|
||||
msg.hdr.type = MSG_TYPE_REQ_RESP;
|
||||
msg.hdr.opcode = SMB_ADC_READ_REQ_OP;
|
||||
|
||||
msg.bus_id = ADC_GLINK_BUS_ID(chan->channel);
|
||||
msg.pmic_id = ADC_GLINK_PMIC_ID(chan->channel);
|
||||
msg.chan = ADC_GLINK_CHAN(chan->channel);
|
||||
|
||||
mutex_lock(&adc->lock);
|
||||
reinit_completion(&adc->ack);
|
||||
ret = pmic_glink_write(adc->client, &msg, sizeof(msg));
|
||||
if (ret)
|
||||
goto done;
|
||||
|
||||
ret = wait_for_completion_timeout(&adc->ack,
|
||||
msecs_to_jiffies(ADC_READ_WAIT_TIME_MS));
|
||||
if (!ret) {
|
||||
dev_err(adc->dev, "Error, ADC conversion timed out\n");
|
||||
ret = -ETIMEDOUT;
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (adc->read_msg.status != SMB_ADC_READ_STATUS_SUCCESS) {
|
||||
dev_err(adc->dev, "glink ADC read failed, bus_id=%u, pmic_id=%u, chan=%u, ret=%u\n",
|
||||
adc->read_msg.bus_id, adc->read_msg.pmic_id,
|
||||
adc->read_msg.chan, adc->read_msg.status);
|
||||
ret = -EIO;
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (conv_data)
|
||||
*conv_data = adc->read_msg.conv_data;
|
||||
if (raw_data)
|
||||
*raw_data = adc->read_msg.raw_data;
|
||||
|
||||
ret = 0;
|
||||
done:
|
||||
mutex_unlock(&adc->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int glink_adc_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val, int *val2, long mask)
|
||||
{
|
||||
struct glink_adc_dev *adc = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_PROCESSED:
|
||||
ret = glink_adc_read_channel(adc, chan, val, NULL);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return IIO_VAL_INT;
|
||||
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
ret = glink_adc_read_channel(adc, chan, NULL, val);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return IIO_VAL_INT;
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int glink_adc_fwnode_xlate(struct iio_dev *indio_dev,
|
||||
const struct fwnode_reference_args *iiospec)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < indio_dev->num_channels; i++) {
|
||||
if (indio_dev->channels[i].channel == iiospec->args[0])
|
||||
return i;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static const struct iio_info glink_adc_info = {
|
||||
.read_raw = glink_adc_read_raw,
|
||||
.fwnode_xlate = glink_adc_fwnode_xlate,
|
||||
};
|
||||
|
||||
#define GLINK_ADC_CHAN(_name, _type) \
|
||||
{ \
|
||||
.datasheet_name = _name, \
|
||||
.type = _type, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED) \
|
||||
| BIT(IIO_CHAN_INFO_RAW), \
|
||||
} \
|
||||
|
||||
|
||||
static const struct iio_chan_spec glink_adc_channels[] = {
|
||||
[GLINK_ADC_CHAN_IIN] = GLINK_ADC_CHAN("iin", IIO_CURRENT),
|
||||
[GLINK_ADC_CHAN_ICHG] = GLINK_ADC_CHAN("ichg", IIO_CURRENT),
|
||||
[GLINK_ADC_CHAN_DIE_TEMP] = GLINK_ADC_CHAN("die_temp", IIO_TEMP),
|
||||
};
|
||||
|
||||
static int glink_adc_get_dt_channel_data(struct glink_adc_dev *adc,
|
||||
struct fwnode_handle *fwnode,
|
||||
struct iio_chan_spec *iio_chan)
|
||||
{
|
||||
u32 reg, chan;
|
||||
int ret;
|
||||
|
||||
ret = fwnode_property_read_u32(fwnode, "reg", ®);
|
||||
if (ret < 0) {
|
||||
dev_err(adc->dev, "missing channel number %s, ret=%d\n",
|
||||
fwnode_get_name(fwnode), ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
chan = ADC_GLINK_CHAN(reg);
|
||||
if (chan == 0 || chan >= GLINK_ADC_CHAN_MAX ||
|
||||
!glink_adc_channels[chan].datasheet_name) {
|
||||
dev_err(adc->dev, "%s invalid channel number %u\n",
|
||||
fwnode_get_name(fwnode), chan);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
iio_chan->channel = reg;
|
||||
iio_chan->type = glink_adc_channels[chan].type;
|
||||
iio_chan->datasheet_name = glink_adc_channels[chan].datasheet_name;
|
||||
fwnode_property_read_string(fwnode, "label", &iio_chan->datasheet_name);
|
||||
iio_chan->extend_name = iio_chan->datasheet_name;
|
||||
iio_chan->info_mask_separate
|
||||
= glink_adc_channels[chan].info_mask_separate;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int glink_adc_get_dt_data(struct glink_adc_dev *adc)
|
||||
{
|
||||
struct iio_chan_spec *iio_chan;
|
||||
struct fwnode_handle *child;
|
||||
int ret;
|
||||
|
||||
adc->nchannels = device_get_child_node_count(adc->dev);
|
||||
if (!adc->nchannels) {
|
||||
dev_dbg(adc->dev, "no ADC channels specified\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
adc->iio_chans = devm_kcalloc(adc->dev, adc->nchannels,
|
||||
sizeof(*adc->iio_chans), GFP_KERNEL);
|
||||
if (!adc->iio_chans)
|
||||
return -ENOMEM;
|
||||
|
||||
iio_chan = adc->iio_chans;
|
||||
device_for_each_child_node(adc->dev, child) {
|
||||
ret = glink_adc_get_dt_channel_data(adc, child, iio_chan);
|
||||
if (ret < 0) {
|
||||
fwnode_handle_put(child);
|
||||
return ret;
|
||||
}
|
||||
|
||||
iio_chan++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int glink_adc_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct glink_adc_dev *adc;
|
||||
struct iio_dev *indio_dev;
|
||||
struct pmic_glink_client_data client_data = { };
|
||||
int ret;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*adc));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
adc = iio_priv(indio_dev);
|
||||
adc->dev = &pdev->dev;
|
||||
mutex_init(&adc->lock);
|
||||
init_completion(&adc->ack);
|
||||
platform_set_drvdata(pdev, adc);
|
||||
|
||||
client_data.id = MSG_OWNER_SMB_ADC;
|
||||
client_data.name = "adc";
|
||||
client_data.msg_cb = glink_adc_callback;
|
||||
client_data.priv = adc;
|
||||
|
||||
ret = glink_adc_get_dt_data(adc);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
adc->client = pmic_glink_register_client(&pdev->dev, &client_data);
|
||||
if (IS_ERR(adc->client)) {
|
||||
ret = PTR_ERR(adc->client);
|
||||
if (ret != -EPROBE_DEFER)
|
||||
dev_err(&pdev->dev, "Error registering glink ADC, ret=%d\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
indio_dev->dev.parent = &pdev->dev;
|
||||
indio_dev->dev.of_node = pdev->dev.of_node;
|
||||
indio_dev->name = pdev->name;
|
||||
indio_dev->info = &glink_adc_info;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->channels = adc->iio_chans;
|
||||
indio_dev->num_channels = adc->nchannels;
|
||||
|
||||
ret = devm_iio_device_register(&pdev->dev, indio_dev);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "iio device registration failed, ret=%d\n",
|
||||
ret);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
pmic_glink_unregister_client(adc->client);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int glink_adc_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct glink_adc_dev *adc = platform_get_drvdata(pdev);
|
||||
|
||||
pmic_glink_unregister_client(adc->client);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id glink_adc_match_table[] = {
|
||||
{ .compatible = "qcom,glink-adc", },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, glink_adc_match_table);
|
||||
|
||||
static struct platform_driver glink_adc_driver = {
|
||||
.driver = {
|
||||
.name = "glink_adc",
|
||||
.of_match_table = glink_adc_match_table,
|
||||
},
|
||||
.probe = glink_adc_probe,
|
||||
.remove = glink_adc_remove,
|
||||
};
|
||||
module_platform_driver(glink_adc_driver);
|
||||
|
||||
MODULE_DESCRIPTION("Glink ADC Driver");
|
||||
MODULE_LICENSE("GPL");
|
Reference in New Issue
Block a user