Add samsung specific changes

This commit is contained in:
2025-08-11 14:29:00 +02:00
parent c66122e619
commit 4d134a1294
2688 changed files with 1127995 additions and 11475 deletions

View File

@@ -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

View File

@@ -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

File diff suppressed because it is too large Load Diff

View File

@@ -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,
};

View File

@@ -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,

View 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", &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");