Loading drivers/iio/adc/qcom-spmi-adc5.c +332 −6 Original line number Diff line number Diff line Loading @@ -14,6 +14,7 @@ #include <linux/math64.h> #include <linux/module.h> #include <linux/of.h> #include <linux/of_address.h> #include <linux/platform_device.h> #include <linux/regmap.h> #include <linux/slab.h> Loading Loading @@ -89,6 +90,24 @@ #define ADC_APP_SID_MASK 0xf #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, ADC5_RATIOMETRIC_CAL, Loading Loading @@ -134,6 +153,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. Loading @@ -146,6 +167,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; Loading Loading @@ -175,17 +197,50 @@ static const struct vadc_prescale_ratio adc5_prescale_ratios[] = { 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) pr_err("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) pr_err("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) pr_err("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) pr_err("adc_cmn write to register %#x of length:%d failed, ret=%d\n", offset, len, ret); return ret; } static int adc5_prescaling_from_dt(u32 num, u32 den) Loading Loading @@ -242,11 +297,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; Loading Loading @@ -385,6 +440,52 @@ static int adc7_configure(struct adc5_chip *adc, return ret; } 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, Loading Loading @@ -494,6 +595,59 @@ static int adc7_do_conversion(struct adc5_chip *adc, 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) { pr_err("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) { pr_err("Reading ADC channel %s timed out\n", prop->datasheet_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)) { pr_err("ADC channel %s EOC bit not set, status=%#x\n", prop->datasheet_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; } static irqreturn_t adc5_isr(int irq, void *dev_id) { struct adc5_chip *adc = dev_id; Loading @@ -503,6 +657,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]; } pr_err("Invalid channel %02x\n", num); return NULL; } static int adc5_of_xlate(struct iio_dev *indio_dev, const struct of_phandle_args *iiospec) { Loading Loading @@ -609,6 +778,118 @@ static int adc5_read_raw(struct iio_dev *indio_dev, return 0; } 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, &adc5_prescale_ratios[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, .of_xlate = adc5_of_xlate, Loading @@ -619,6 +900,11 @@ static const struct iio_info adc7_info = { .of_xlate = adc7_of_xlate, }; static const struct iio_info adc7_sw_calib_info = { .read_raw = adc7_sw_calib_read_raw, .of_xlate = adc5_of_xlate, }; struct adc5_channels { const char *datasheet_name; unsigned int prescale_index; Loading Loading @@ -711,6 +997,8 @@ 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, Loading Loading @@ -904,6 +1192,8 @@ static int adc5_get_dt_channel_data(struct adc5_chip *adc, if (of_property_read_bool(node, "qcom,ratiometric")) prop->cal_method = ADC5_RATIOMETRIC_CAL; else if (of_property_read_bool(node, "qcom,no-cal")) prop->cal_method = ADC5_NO_CAL; else prop->cal_method = ADC5_ABSOLUTE_CAL; Loading Loading @@ -980,6 +1270,10 @@ 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_data_pmic, }, { .compatible = "qcom,spmi-adc-rev2", .data = &adc5_data_pmic_rev2, Loading Loading @@ -1062,8 +1356,10 @@ static int adc5_probe(struct platform_device *pdev) struct adc5_chip *adc; struct regmap *regmap; const char *irq_name; const __be32 *prop_addr; int ret, irq_eoc; u32 reg; u8 val; regmap = dev_get_regmap(dev->parent, NULL); if (!regmap) Loading @@ -1080,13 +1376,32 @@ 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) { pr_err("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); platform_set_drvdata(pdev, adc); if (of_device_is_compatible(node, "qcom,spmi-adc7")) { indio_dev->info = &adc7_info; adc->is_pmic7 = true; } else if (of_device_is_compatible(node, "qcom,spmi-adc7-sw-calib")) { if (!adc->cmn_base) { pr_err("ADC_CMN undefined\n"); return -ENODEV; } indio_dev->info = &adc7_sw_calib_info; adc->is_pmic7 = true; } else { indio_dev->info = &adc5_info; } Loading Loading @@ -1116,6 +1431,17 @@ static int adc5_probe(struct platform_device *pdev) return ret; } if (of_device_is_compatible(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; } indio_dev->dev.parent = dev; indio_dev->dev.of_node = node; indio_dev->name = pdev->name; Loading Loading
drivers/iio/adc/qcom-spmi-adc5.c +332 −6 Original line number Diff line number Diff line Loading @@ -14,6 +14,7 @@ #include <linux/math64.h> #include <linux/module.h> #include <linux/of.h> #include <linux/of_address.h> #include <linux/platform_device.h> #include <linux/regmap.h> #include <linux/slab.h> Loading Loading @@ -89,6 +90,24 @@ #define ADC_APP_SID_MASK 0xf #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, ADC5_RATIOMETRIC_CAL, Loading Loading @@ -134,6 +153,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. Loading @@ -146,6 +167,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; Loading Loading @@ -175,17 +197,50 @@ static const struct vadc_prescale_ratio adc5_prescale_ratios[] = { 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) pr_err("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) pr_err("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) pr_err("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) pr_err("adc_cmn write to register %#x of length:%d failed, ret=%d\n", offset, len, ret); return ret; } static int adc5_prescaling_from_dt(u32 num, u32 den) Loading Loading @@ -242,11 +297,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; Loading Loading @@ -385,6 +440,52 @@ static int adc7_configure(struct adc5_chip *adc, return ret; } 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, Loading Loading @@ -494,6 +595,59 @@ static int adc7_do_conversion(struct adc5_chip *adc, 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) { pr_err("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) { pr_err("Reading ADC channel %s timed out\n", prop->datasheet_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)) { pr_err("ADC channel %s EOC bit not set, status=%#x\n", prop->datasheet_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; } static irqreturn_t adc5_isr(int irq, void *dev_id) { struct adc5_chip *adc = dev_id; Loading @@ -503,6 +657,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]; } pr_err("Invalid channel %02x\n", num); return NULL; } static int adc5_of_xlate(struct iio_dev *indio_dev, const struct of_phandle_args *iiospec) { Loading Loading @@ -609,6 +778,118 @@ static int adc5_read_raw(struct iio_dev *indio_dev, return 0; } 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, &adc5_prescale_ratios[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, .of_xlate = adc5_of_xlate, Loading @@ -619,6 +900,11 @@ static const struct iio_info adc7_info = { .of_xlate = adc7_of_xlate, }; static const struct iio_info adc7_sw_calib_info = { .read_raw = adc7_sw_calib_read_raw, .of_xlate = adc5_of_xlate, }; struct adc5_channels { const char *datasheet_name; unsigned int prescale_index; Loading Loading @@ -711,6 +997,8 @@ 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, Loading Loading @@ -904,6 +1192,8 @@ static int adc5_get_dt_channel_data(struct adc5_chip *adc, if (of_property_read_bool(node, "qcom,ratiometric")) prop->cal_method = ADC5_RATIOMETRIC_CAL; else if (of_property_read_bool(node, "qcom,no-cal")) prop->cal_method = ADC5_NO_CAL; else prop->cal_method = ADC5_ABSOLUTE_CAL; Loading Loading @@ -980,6 +1270,10 @@ 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_data_pmic, }, { .compatible = "qcom,spmi-adc-rev2", .data = &adc5_data_pmic_rev2, Loading Loading @@ -1062,8 +1356,10 @@ static int adc5_probe(struct platform_device *pdev) struct adc5_chip *adc; struct regmap *regmap; const char *irq_name; const __be32 *prop_addr; int ret, irq_eoc; u32 reg; u8 val; regmap = dev_get_regmap(dev->parent, NULL); if (!regmap) Loading @@ -1080,13 +1376,32 @@ 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) { pr_err("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); platform_set_drvdata(pdev, adc); if (of_device_is_compatible(node, "qcom,spmi-adc7")) { indio_dev->info = &adc7_info; adc->is_pmic7 = true; } else if (of_device_is_compatible(node, "qcom,spmi-adc7-sw-calib")) { if (!adc->cmn_base) { pr_err("ADC_CMN undefined\n"); return -ENODEV; } indio_dev->info = &adc7_sw_calib_info; adc->is_pmic7 = true; } else { indio_dev->info = &adc5_info; } Loading Loading @@ -1116,6 +1431,17 @@ static int adc5_probe(struct platform_device *pdev) return ret; } if (of_device_is_compatible(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; } indio_dev->dev.parent = dev; indio_dev->dev.of_node = node; indio_dev->name = pdev->name; Loading