// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2016-2017, 2019, The Linux Foundation. All rights reserved. * Copyright (c) 2022 Linaro Limited. * Author: Casey Connolly <casey.connolly@linaro.org> * * This driver is for the Round Robin ADC found in the pmi8998 and pm660 PMICs.
*/
/** * struct rradc_channel - rradc channel data * @label: channel label * @lsb: Channel least significant byte * @status: Channel status address * @size: number of bytes to read * @trigger_addr: Trigger address, trigger is only used on some channels * @trigger_mask: Trigger mask * @scale_fn: Post process callback for channels which can't be exposed * as offset + scale.
*/ struct rradc_channel { constchar *label;
u8 lsb;
u8 status; int size; int trigger_addr; int trigger_mask; int (*scale_fn)(struct rradc_chip *chip, u16 adc_code, int *result);
};
staticint rradc_read(struct rradc_chip *chip, u16 addr, __le16 *buf, int len)
{ int ret, retry_cnt = 0;
__le16 data_check[RR_ADC_CHAN_MAX_CONTINUOUS_BUFFER_LEN / 2];
if (len > RR_ADC_CHAN_MAX_CONTINUOUS_BUFFER_LEN) {
dev_err(chip->dev, "Can't read more than %d bytes, but asked to read %d bytes.\n",
RR_ADC_CHAN_MAX_CONTINUOUS_BUFFER_LEN, len); return -EINVAL;
}
while (retry_cnt < RR_ADC_COHERENT_CHECK_RETRY) {
ret = regmap_bulk_read(chip->regmap, chip->base + addr, buf,
len); if (ret < 0) {
dev_err(chip->dev, "rr_adc reg 0x%x failed :%d\n", addr,
ret); return ret;
}
/* * These functions explicitly cast int64_t to int. * They will never overflow, as the values are small enough.
*/ staticint rradc_post_process_batt_id(struct rradc_chip *chip, u16 adc_code, int *result_ohms)
{
uint32_t current_value;
int64_t r_id;
staticint rradc_enable_continuous_mode(struct rradc_chip *chip)
{ int ret;
/* Clear channel log */
ret = regmap_set_bits(chip->regmap, chip->base + RR_ADC_LOG,
RR_ADC_LOG_CLR_CTRL); if (ret < 0) {
dev_err(chip->dev, "log ctrl update to clear failed:%d\n", ret); return ret;
}
ret = regmap_clear_bits(chip->regmap, chip->base + RR_ADC_LOG,
RR_ADC_LOG_CLR_CTRL); if (ret < 0) {
dev_err(chip->dev, "log ctrl update to not clear failed:%d\n",
ret); return ret;
}
/* Switch to continuous mode */
ret = regmap_set_bits(chip->regmap, chip->base + RR_ADC_CTL,
RR_ADC_CTL_CONTINUOUS_SEL); if (ret < 0)
dev_err(chip->dev, "Update to continuous mode failed:%d\n",
ret);
return ret;
}
staticint rradc_disable_continuous_mode(struct rradc_chip *chip)
{ int ret;
/* Switch to non continuous mode */
ret = regmap_clear_bits(chip->regmap, chip->base + RR_ADC_CTL,
RR_ADC_CTL_CONTINUOUS_SEL); if (ret < 0)
dev_err(chip->dev, "Update to non-continuous mode failed:%d\n",
ret);
/* BATT_ID STS bit does not get set initially */ switch (chan_address) { case RR_ADC_BATT_ID:
mask = RR_ADC_STS_CHANNEL_STS; break; default:
mask = RR_ADC_STS_CHANNEL_READING_MASK; break;
}
ret = regmap_read(chip->regmap, chip->base + chan->status, &status); if (ret < 0 || !(status & mask)) returnfalse;
if (chan->trigger_mask == 0) {
dev_err(chip->dev, "Channel doesn't have a trigger mask\n"); return -EINVAL;
}
ret = regmap_set_bits(chip->regmap, chip->base + chan->trigger_addr,
chan->trigger_mask); if (ret < 0) {
dev_err(chip->dev, "Failed to apply trigger for channel '%s' ret=%d\n",
iio_chan->extend_name, ret); return ret;
}
ret = rradc_enable_continuous_mode(chip); if (ret < 0) {
dev_err(chip->dev, "Failed to switch to continuous mode\n"); goto disable_trigger;
}
/* * The wait/sleep values were found through trial and error, * this is mostly for the battery ID channel which takes some * time to settle.
*/ for (i = 0; i < 5; i++) { if (rradc_is_ready(chip, chan_address)) break;
usleep_range(50000, 50000 + 500);
}
if (i == 5) {
dev_err(chip->dev, "Channel '%s' is not ready\n",
iio_chan->extend_name);
ret = -ETIMEDOUT;
}
switch (chan_address) { case RR_ADC_BATT_ID:
ret = rradc_prepare_batt_id_conversion(chip, chan_address, data); if (ret < 0) {
dev_err(chip->dev, "Battery ID conversion failed:%d\n",
ret); goto unlock_out;
} break;
case RR_ADC_USBIN_V: case RR_ADC_DIE_TEMP:
ret = rradc_read_status_in_cont_mode(chip, chan_address); if (ret < 0) {
dev_err(chip->dev, "Error reading in continuous mode:%d\n", ret); goto unlock_out;
} break; default: if (!rradc_is_ready(chip, chan_address)) { /* * Usually this means the channel isn't attached, for example * the in_voltage_usbin_v_input channel will not be ready if * no USB cable is attached
*/
dev_dbg(chip->dev, "channel '%s' is not ready\n",
iio_chan->extend_name);
ret = -ENODATA; goto unlock_out;
} break;
}
ret = rradc_read(chip, chan->lsb, buf, chan->size); if (ret) {
dev_err(chip->dev, "read data failed\n"); goto unlock_out;
}
/* * For the battery ID we read the register for every ID ADC and then * see which one is actually connected.
*/ if (chan_address == RR_ADC_BATT_ID) {
u16 batt_id_150 = le16_to_cpu(buf[2]);
u16 batt_id_15 = le16_to_cpu(buf[1]);
u16 batt_id_5 = le16_to_cpu(buf[0]);
if (!batt_id_150 && !batt_id_15 && !batt_id_5) {
dev_err(chip->dev, "Invalid batt_id values with all zeros\n");
ret = -EINVAL; goto unlock_out;
}
if (batt_id_150 <= RR_ADC_BATT_ID_RANGE) {
*data = batt_id_150;
chip->batt_id_data = 150;
} elseif (batt_id_15 <= RR_ADC_BATT_ID_RANGE) {
*data = batt_id_15;
chip->batt_id_data = 15;
} else {
*data = batt_id_5;
chip->batt_id_data = 5;
}
} else { /* * All of the other channels are either 1 or 2 bytes. * We can rely on the second byte being 0 for 1-byte channels.
*/
*data = le16_to_cpu(buf[0]);
}
unlock_out:
mutex_unlock(&chip->conversion_lock);
return ret;
}
staticint rradc_read_scale(struct rradc_chip *chip, int chan_address, int *val, int *val2)
{
int64_t fab_offset, fab_slope; int ret;
ret = rradc_get_fab_coeff(chip, &fab_offset, &fab_slope); if (ret < 0) {
dev_err(chip->dev, "Unable to get fab id coefficients\n"); return -EINVAL;
}
switch (chan_address) { case RR_ADC_SKIN_TEMP:
*val = MILLI;
*val2 = RR_ADC_BATT_THERM_LSB_K; return IIO_VAL_FRACTIONAL; case RR_ADC_USBIN_I:
*val = RR_ADC_CURR_USBIN_INPUT_FACTOR_MIL *
RR_ADC_FS_VOLTAGE_MV;
*val2 = RR_ADC_CHAN_MSB; return IIO_VAL_FRACTIONAL; case RR_ADC_DCIN_I:
*val = RR_ADC_CURR_INPUT_FACTOR * RR_ADC_FS_VOLTAGE_MV;
*val2 = RR_ADC_CHAN_MSB; return IIO_VAL_FRACTIONAL; case RR_ADC_USBIN_V: case RR_ADC_DCIN_V:
*val = RR_ADC_VOLT_INPUT_FACTOR * RR_ADC_FS_VOLTAGE_MV * MILLI;
*val2 = RR_ADC_CHAN_MSB; return IIO_VAL_FRACTIONAL; case RR_ADC_GPIO:
*val = RR_ADC_GPIO_FS_RANGE;
*val2 = RR_ADC_CHAN_MSB; return IIO_VAL_FRACTIONAL; case RR_ADC_CHG_TEMP: /* * We divide val2 by MILLI instead of multiplying val * to avoid an integer overflow.
*/
*val = -RR_ADC_TEMP_FS_VOLTAGE_NUM;
*val2 = div64_s64(RR_ADC_TEMP_FS_VOLTAGE_DEN * RR_ADC_CHAN_MSB *
fab_slope,
MILLI);
/* * The -1 is to compensate for lost precision. * It should actually be -0.7906976744186046. * This works out to every value being off * by about +0.091 degrees C after applying offset and scale.
*/
*val = (int)(offset1 - offset2 - 1); return IIO_VAL_INT; case RR_ADC_DIE_TEMP:
offset1 = -RR_ADC_DIE_TEMP_OFFSET *
(int64_t)RR_ADC_TEMP_FS_VOLTAGE_DEN *
(int64_t)RR_ADC_CHAN_MSB;
offset1 = div64_s64(offset1, RR_ADC_TEMP_FS_VOLTAGE_NUM);
/* * The result is -339, it should be -338.69789, this results * in the calculated die temp being off by * -0.004 - -0.0175 degrees C
*/
*val = (int)(offset1 - offset2); return IIO_VAL_INT; default: break;
} return -EINVAL;
}
staticint rradc_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan_spec, int *val, int *val2, long mask)
{ struct rradc_chip *chip = iio_priv(indio_dev); conststruct rradc_channel *chan; int ret;
u16 adc_code;
ret = device_property_read_u32(dev, "reg", &chip->base); if (ret < 0) {
dev_err(chip->dev, "Couldn't find reg address, ret = %d\n",
ret); return ret;
}
batt_id_delay = -1;
ret = device_property_read_u32(dev, "qcom,batt-id-delay-ms",
&batt_id_delay); if (!ret) { for (i = 0; i < RRADC_BATT_ID_DELAY_MAX; i++) { if (batt_id_delay == batt_id_delays[i]) break;
} if (i == RRADC_BATT_ID_DELAY_MAX)
batt_id_delay = -1;
}
if (batt_id_delay >= 0) {
batt_id_delay = FIELD_PREP(BATT_ID_SETTLE_MASK, batt_id_delay);
ret = regmap_set_bits(chip->regmap,
chip->base + RR_ADC_BATT_ID_CFG,
batt_id_delay); if (ret < 0) {
dev_err(chip->dev, "BATT_ID settling time config failed:%d\n",
ret);
}
}
/* Get the PMIC revision, we need it to handle some varying coefficients */
chip->pmic = qcom_pmic_get(chip->dev); if (IS_ERR(chip->pmic)) {
dev_err(chip->dev, "Unable to get reference to PMIC device\n"); return PTR_ERR(chip->pmic);
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.