// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (C) ST-Ericsson SA 2010 * * Author: Arun R Murthy <arun.murthy@stericsson.com> * Author: Daniel Willerud <daniel.willerud@stericsson.com> * Author: Johan Palsson <johan.palsson@stericsson.com> * Author: M'boumba Cedric Madianga * Author: Linus Walleij <linus.walleij@linaro.org> * * AB8500 General Purpose ADC driver. The AB8500 uses reference voltages: * VinVADC, and VADC relative to GND to do its job. It monitors main and backup * battery voltages, AC (mains) voltage, USB cable voltage, as well as voltages * representing the temperature of the chip die and battery, accessory * detection by resistance measurements using relative voltages and GSM burst * information. * * Some of the voltages are measured on external pins on the IC, such as * battery temperature or "ADC aux" 1 and 2. Other voltages are internal rails * from other parts of the ASIC such as main charger voltage, main and battery * backup voltage or USB VBUS voltage. For this reason drivers for other * parts of the system are required to obtain handles to the ADC to do work * for them and the IIO driver provides arbitration among these consumers.
*/ #include <linux/init.h> #include <linux/bits.h> #include <linux/iio/iio.h> #include <linux/iio/sysfs.h> #include <linux/device.h> #include <linux/interrupt.h> #include <linux/spinlock.h> #include <linux/delay.h> #include <linux/pm_runtime.h> #include <linux/platform_device.h> #include <linux/completion.h> #include <linux/regulator/consumer.h> #include <linux/random.h> #include <linux/err.h> #include <linux/slab.h> #include <linux/mfd/abx500.h> #include <linux/mfd/abx500/ab8500.h>
/* GPADC register offsets and bit definitions */
#define AB8500_GPADC_CTRL1_REG 0x00 /* GPADC control register 1 bits */ #define AB8500_GPADC_CTRL1_DISABLE 0x00 #define AB8500_GPADC_CTRL1_ENABLE BIT(0) #define AB8500_GPADC_CTRL1_TRIG_ENA BIT(1) #define AB8500_GPADC_CTRL1_START_SW_CONV BIT(2) #define AB8500_GPADC_CTRL1_BTEMP_PULL_UP BIT(3) /* 0 = use rising edge, 1 = use falling edge */ #define AB8500_GPADC_CTRL1_TRIG_EDGE BIT(4) /* 0 = use VTVOUT, 1 = use VRTC as pull-up supply for battery temp NTC */ #define AB8500_GPADC_CTRL1_PUPSUPSEL BIT(5) #define AB8500_GPADC_CTRL1_BUF_ENA BIT(6) #define AB8500_GPADC_CTRL1_ICHAR_ENA BIT(7)
#define AB8500_GPADC_CTRL2_REG 0x01 #define AB8500_GPADC_CTRL3_REG 0x02 /* * GPADC control register 2 and 3 bits * the bit layout is the same for SW and HW conversion set-up
*/ #define AB8500_GPADC_CTRL2_AVG_1 0x00 #define AB8500_GPADC_CTRL2_AVG_4 BIT(5) #define AB8500_GPADC_CTRL2_AVG_8 BIT(6) #define AB8500_GPADC_CTRL2_AVG_16 (BIT(5) | BIT(6))
enum ab8500_gpadc_channel {
AB8500_GPADC_CHAN_UNUSED = 0x00,
AB8500_GPADC_CHAN_BAT_CTRL = 0x01,
AB8500_GPADC_CHAN_BAT_TEMP = 0x02, /* This is not used on AB8505 */
AB8500_GPADC_CHAN_MAIN_CHARGER = 0x03,
AB8500_GPADC_CHAN_ACC_DET_1 = 0x04,
AB8500_GPADC_CHAN_ACC_DET_2 = 0x05,
AB8500_GPADC_CHAN_ADC_AUX_1 = 0x06,
AB8500_GPADC_CHAN_ADC_AUX_2 = 0x07,
AB8500_GPADC_CHAN_VBAT_A = 0x08,
AB8500_GPADC_CHAN_VBUS = 0x09,
AB8500_GPADC_CHAN_MAIN_CHARGER_CURRENT = 0x0a,
AB8500_GPADC_CHAN_USB_CHARGER_CURRENT = 0x0b,
AB8500_GPADC_CHAN_BACKUP_BAT = 0x0c, /* Only on AB8505 */
AB8505_GPADC_CHAN_DIE_TEMP = 0x0d,
AB8500_GPADC_CHAN_ID = 0x0e,
AB8500_GPADC_CHAN_INTERNAL_TEST_1 = 0x0f,
AB8500_GPADC_CHAN_INTERNAL_TEST_2 = 0x10,
AB8500_GPADC_CHAN_INTERNAL_TEST_3 = 0x11, /* FIXME: Applicable to all ASIC variants? */
AB8500_GPADC_CHAN_XTAL_TEMP = 0x12,
AB8500_GPADC_CHAN_VBAT_TRUE_MEAS = 0x13, /* FIXME: Doesn't seem to work with pure AB8500 */
AB8500_GPADC_CHAN_BAT_CTRL_AND_IBAT = 0x1c,
AB8500_GPADC_CHAN_VBAT_MEAS_AND_IBAT = 0x1d,
AB8500_GPADC_CHAN_VBAT_TRUE_MEAS_AND_IBAT = 0x1e,
AB8500_GPADC_CHAN_BAT_TEMP_AND_IBAT = 0x1f, /* * Virtual channel used only for ibat conversion to ampere. * Battery current conversion (ibat) cannot be requested as a * single conversion but it is always requested in combination * with other input requests.
*/
AB8500_GPADC_CHAN_IBAT_VIRTUAL = 0xFF,
};
/* GPADC constants from AB8540 spec */ #define AB8500_ADC_CH_IBAT_MIN (-6000) /* mA range measured by ADC for ibat */ #define AB8500_ADC_CH_IBAT_MAX 6000 #define AB8500_ADC_CH_IBAT_MIN_V (-60) /* mV range measured by ADC for ibat */ #define AB8500_ADC_CH_IBAT_MAX_V 60 #define AB8500_GPADC_IBAT_VDROP_L (-56) /* mV */ #define AB8500_GPADC_IBAT_VDROP_H 56
/* This is used to not lose precision when dividing to get gain and offset */ #define AB8500_GPADC_CALIB_SCALE 1000 /* * Number of bits shift used to not lose precision * when dividing to get ibat gain.
*/ #define AB8500_GPADC_CALIB_SHIFT_IBAT 20
/* Time in ms before disabling regulator */ #define AB8500_GPADC_AUTOSUSPEND_DELAY 1
/** * struct ab8500_adc_cal_data - Table for storing gain and offset for the * calibrated ADC channels * @gain: Gain of the ADC channel * @offset: Offset of the ADC channel * @otp_calib_hi: Calibration from OTP * @otp_calib_lo: Calibration from OTP
*/ struct ab8500_adc_cal_data {
s64 gain;
s64 offset;
u16 otp_calib_hi;
u16 otp_calib_lo;
};
/** * struct ab8500_gpadc_chan_info - per-channel GPADC info * @name: name of the channel * @id: the internal AB8500 ID number for the channel * @hardware_control: indicate that we want to use hardware ADC control * on this channel, the default is software ADC control. Hardware control * is normally only used to test the battery voltage during GSM bursts * and needs a hardware trigger on the GPADCTrig pin of the ASIC. * @falling_edge: indicate that we want to trigger on falling edge * rather than rising edge, rising edge is the default * @avg_sample: how many samples to average: must be 1, 4, 8 or 16. * @trig_timer: how long to wait for the trigger, in 32kHz periods: * 0 .. 255 periods
*/ struct ab8500_gpadc_chan_info { constchar *name;
u8 id; bool hardware_control; bool falling_edge;
u8 avg_sample;
u8 trig_timer;
};
/** * struct ab8500_gpadc - AB8500 GPADC device information * @dev: pointer to the containing device * @ab8500: pointer to the parent AB8500 device * @chans: internal per-channel information container * @nchans: number of channels * @complete: pointer to the completion that indicates * the completion of an gpadc conversion cycle * @vddadc: pointer to the regulator supplying VDDADC * @irq_sw: interrupt number that is used by gpadc for software ADC conversion * @irq_hw: interrupt number that is used by gpadc for hardware ADC conversion * @cal_data: array of ADC calibration data structs
*/ struct ab8500_gpadc { struct device *dev; struct ab8500 *ab8500; struct ab8500_gpadc_chan_info *chans; unsignedint nchans; struct completion complete; struct regulator *vddadc; int irq_sw; int irq_hw; struct ab8500_adc_cal_data cal_data[AB8500_CAL_NR];
};
for (i = 0; i < gpadc->nchans; i++) {
ch = &gpadc->chans[i]; if (ch->id == chan) break;
} if (i == gpadc->nchans) return NULL;
return ch;
}
/** * ab8500_gpadc_ad_to_voltage() - Convert a raw ADC value to a voltage * @gpadc: GPADC instance * @ch: the sampled channel this raw value is coming from * @ad_value: the raw value
*/ staticint ab8500_gpadc_ad_to_voltage(struct ab8500_gpadc *gpadc, enum ab8500_gpadc_channel ch, int ad_value)
{ int res;
switch (ch) { case AB8500_GPADC_CHAN_MAIN_CHARGER: /* No calibration data available: just interpolate */ if (!gpadc->cal_data[AB8500_CAL_VMAIN].gain) {
res = AB8500_ADC_CH_CHG_V_MIN + (AB8500_ADC_CH_CHG_V_MAX -
AB8500_ADC_CH_CHG_V_MIN) * ad_value /
AB8500_ADC_RESOLUTION; break;
} /* Here we can use calibration */
res = (int) (ad_value * gpadc->cal_data[AB8500_CAL_VMAIN].gain +
gpadc->cal_data[AB8500_CAL_VMAIN].offset) / AB8500_GPADC_CALIB_SCALE; break;
case AB8500_GPADC_CHAN_BAT_CTRL: case AB8500_GPADC_CHAN_BAT_TEMP: case AB8500_GPADC_CHAN_ACC_DET_1: case AB8500_GPADC_CHAN_ADC_AUX_1: case AB8500_GPADC_CHAN_ADC_AUX_2: case AB8500_GPADC_CHAN_XTAL_TEMP: /* No calibration data available: just interpolate */ if (!gpadc->cal_data[AB8500_CAL_BTEMP].gain) {
res = AB8500_ADC_CH_BTEMP_MIN + (AB8500_ADC_CH_BTEMP_MAX -
AB8500_ADC_CH_BTEMP_MIN) * ad_value /
AB8500_ADC_RESOLUTION; break;
} /* Here we can use calibration */
res = (int) (ad_value * gpadc->cal_data[AB8500_CAL_BTEMP].gain +
gpadc->cal_data[AB8500_CAL_BTEMP].offset) / AB8500_GPADC_CALIB_SCALE; break;
case AB8500_GPADC_CHAN_VBAT_A: case AB8500_GPADC_CHAN_VBAT_TRUE_MEAS: /* No calibration data available: just interpolate */ if (!gpadc->cal_data[AB8500_CAL_VBAT].gain) {
res = AB8500_ADC_CH_VBAT_MIN + (AB8500_ADC_CH_VBAT_MAX -
AB8500_ADC_CH_VBAT_MIN) * ad_value /
AB8500_ADC_RESOLUTION; break;
} /* Here we can use calibration */
res = (int) (ad_value * gpadc->cal_data[AB8500_CAL_VBAT].gain +
gpadc->cal_data[AB8500_CAL_VBAT].offset) / AB8500_GPADC_CALIB_SCALE; break;
case AB8505_GPADC_CHAN_DIE_TEMP:
res = AB8500_ADC_CH_DIETEMP_MIN +
(AB8500_ADC_CH_DIETEMP_MAX - AB8500_ADC_CH_DIETEMP_MIN) * ad_value /
AB8500_ADC_RESOLUTION; break;
case AB8500_GPADC_CHAN_ACC_DET_2:
res = AB8500_ADC_CH_ACCDET2_MIN +
(AB8500_ADC_CH_ACCDET2_MAX - AB8500_ADC_CH_ACCDET2_MIN) * ad_value /
AB8500_ADC_RESOLUTION; break;
case AB8500_GPADC_CHAN_VBUS:
res = AB8500_ADC_CH_CHG_V_MIN +
(AB8500_ADC_CH_CHG_V_MAX - AB8500_ADC_CH_CHG_V_MIN) * ad_value /
AB8500_ADC_RESOLUTION; break;
case AB8500_GPADC_CHAN_MAIN_CHARGER_CURRENT: case AB8500_GPADC_CHAN_USB_CHARGER_CURRENT:
res = AB8500_ADC_CH_CHG_I_MIN +
(AB8500_ADC_CH_CHG_I_MAX - AB8500_ADC_CH_CHG_I_MIN) * ad_value /
AB8500_ADC_RESOLUTION; break;
case AB8500_GPADC_CHAN_BACKUP_BAT:
res = AB8500_ADC_CH_BKBAT_MIN +
(AB8500_ADC_CH_BKBAT_MAX - AB8500_ADC_CH_BKBAT_MIN) * ad_value /
AB8500_ADC_RESOLUTION; break;
case AB8500_GPADC_CHAN_IBAT_VIRTUAL: /* No calibration data available: just interpolate */ if (!gpadc->cal_data[AB8500_CAL_IBAT].gain) {
res = AB8500_ADC_CH_IBAT_MIN + (AB8500_ADC_CH_IBAT_MAX -
AB8500_ADC_CH_IBAT_MIN) * ad_value /
AB8500_ADC_RESOLUTION; break;
} /* Here we can use calibration */
res = (int) (ad_value * gpadc->cal_data[AB8500_CAL_IBAT].gain +
gpadc->cal_data[AB8500_CAL_IBAT].offset)
>> AB8500_GPADC_CALIB_SHIFT_IBAT; break;
default:
dev_err(gpadc->dev, "unknown channel ID: %d, not possible to convert\n",
ch);
res = -EINVAL; break;
/* check if conversion is supported */ if ((gpadc->irq_sw <= 0) && !ch->hardware_control) return -ENOTSUPP; if ((gpadc->irq_hw <= 0) && ch->hardware_control) return -ENOTSUPP;
/* Enable vddadc by grabbing PM runtime */
pm_runtime_get_sync(gpadc->dev);
/* Check if ADC is not busy, lock and proceed */ do {
ret = abx500_get_register_interruptible(gpadc->dev,
AB8500_GPADC, AB8500_GPADC_STAT_REG, &val); if (ret < 0) goto out; if (!(val & AB8500_GPADC_STAT_BUSY)) break;
msleep(20);
} while (++looplimit < 10); if (looplimit >= 10 && (val & AB8500_GPADC_STAT_BUSY)) {
dev_err(gpadc->dev, "gpadc_conversion: GPADC busy");
ret = -EINVAL; goto out;
}
/* Select the channel source and set average samples */ switch (ch->avg_sample) { case 1:
ctrl23 = ch->id | AB8500_GPADC_CTRL2_AVG_1; break; case 4:
ctrl23 = ch->id | AB8500_GPADC_CTRL2_AVG_4; break; case 8:
ctrl23 = ch->id | AB8500_GPADC_CTRL2_AVG_8; break; default:
ctrl23 = ch->id | AB8500_GPADC_CTRL2_AVG_16; break;
}
if (ch->hardware_control) {
ret = abx500_set_register_interruptible(gpadc->dev,
AB8500_GPADC, AB8500_GPADC_CTRL3_REG, ctrl23);
ctrl1 |= AB8500_GPADC_CTRL1_TRIG_ENA; if (ch->falling_edge)
ctrl1 |= AB8500_GPADC_CTRL1_TRIG_EDGE;
} else {
ret = abx500_set_register_interruptible(gpadc->dev,
AB8500_GPADC, AB8500_GPADC_CTRL2_REG, ctrl23);
} if (ret < 0) {
dev_err(gpadc->dev, "gpadc_conversion: set avg samples failed\n"); goto out;
}
/* * Enable ADC, buffering, select rising edge and enable ADC path * charging current sense if it needed, ABB 3.0 needs some special * treatment too.
*/ switch (ch->id) { case AB8500_GPADC_CHAN_MAIN_CHARGER_CURRENT: case AB8500_GPADC_CHAN_USB_CHARGER_CURRENT:
ctrl1 |= AB8500_GPADC_CTRL1_BUF_ENA |
AB8500_GPADC_CTRL1_ICHAR_ENA; break; case AB8500_GPADC_CHAN_BAT_TEMP: if (!is_ab8500_2p0_or_earlier(gpadc->ab8500)) {
ctrl1 |= AB8500_GPADC_CTRL1_BUF_ENA |
AB8500_GPADC_CTRL1_BTEMP_PULL_UP; /* * Delay might be needed for ABB8500 cut 3.0, if not, * remove when hardware will be available
*/
delay_min = 1000; /* Delay in micro seconds */
delay_max = 10000; /* large range optimises sleepmode */ break;
}
fallthrough; default:
ctrl1 |= AB8500_GPADC_CTRL1_BUF_ENA; break;
}
/* Write configuration to control register 1 */
ret = abx500_set_register_interruptible(gpadc->dev,
AB8500_GPADC, AB8500_GPADC_CTRL1_REG, ctrl1); if (ret < 0) {
dev_err(gpadc->dev, "gpadc_conversion: set Control register failed\n"); goto out;
}
if (delay_min != 0)
usleep_range(delay_min, delay_max);
/* Wait for completion of conversion */ if (!wait_for_completion_timeout(&gpadc->complete,
completion_timeout)) {
dev_err(gpadc->dev, "timeout didn't receive GPADC conv interrupt\n");
ret = -EINVAL; goto out;
}
/* Read the converted RAW data */
ret = abx500_get_register_interruptible(gpadc->dev,
AB8500_GPADC, data_low_addr, &low_data); if (ret < 0) {
dev_err(gpadc->dev, "gpadc_conversion: read low data failed\n"); goto out;
}
ret = abx500_get_register_interruptible(gpadc->dev,
AB8500_GPADC, data_high_addr, &high_data); if (ret < 0) {
dev_err(gpadc->dev, "gpadc_conversion: read high data failed\n"); goto out;
}
/* Check if double conversion is required */ if ((ch->id == AB8500_GPADC_CHAN_BAT_CTRL_AND_IBAT) ||
(ch->id == AB8500_GPADC_CHAN_VBAT_MEAS_AND_IBAT) ||
(ch->id == AB8500_GPADC_CHAN_VBAT_TRUE_MEAS_AND_IBAT) ||
(ch->id == AB8500_GPADC_CHAN_BAT_TEMP_AND_IBAT)) {
if (ch->hardware_control) { /* not supported */
ret = -ENOTSUPP;
dev_err(gpadc->dev, "gpadc_conversion: only SW double conversion supported\n"); goto out;
} else { /* Read the converted RAW data 2 */
ret = abx500_get_register_interruptible(gpadc->dev,
AB8500_GPADC, AB8540_GPADC_MANDATA2L_REG,
&low_data2); if (ret < 0) {
dev_err(gpadc->dev, "gpadc_conversion: read sw low data 2 failed\n"); goto out;
}
ret = abx500_get_register_interruptible(gpadc->dev,
AB8500_GPADC, AB8540_GPADC_MANDATA2H_REG,
&high_data2); if (ret < 0) {
dev_err(gpadc->dev, "gpadc_conversion: read sw high data 2 failed\n"); goto out;
} if (ibat != NULL) {
*ibat = (high_data2 << 8) | low_data2;
} else {
dev_warn(gpadc->dev, "gpadc_conversion: ibat not stored\n");
}
/* This eventually drops the regulator */
pm_runtime_mark_last_busy(gpadc->dev);
pm_runtime_put_autosuspend(gpadc->dev);
return (high_data << 8) | low_data;
out: /* * It has shown to be needed to turn off the GPADC if an error occurs, * otherwise we might have problem when waiting for the busy bit in the * GPADC status register to go low. In V1.1 there wait_for_completion * seems to timeout when waiting for an interrupt.. Not seen in V2.0
*/
(void) abx500_set_register_interruptible(gpadc->dev, AB8500_GPADC,
AB8500_GPADC_CTRL1_REG, AB8500_GPADC_CTRL1_DISABLE);
pm_runtime_put(gpadc->dev);
dev_err(gpadc->dev, "gpadc_conversion: Failed to AD convert channel %d\n", ch->id);
return ret;
}
/** * ab8500_bm_gpadcconvend_handler() - isr for gpadc conversion completion * @irq: irq number * @data: pointer to the data passed during request irq * * This is a interrupt service routine for gpadc conversion completion. * Notifies the gpadc completion is completed and the converted raw value * can be read from the registers. * Returns IRQ status(IRQ_HANDLED)
*/ static irqreturn_t ab8500_bm_gpadcconvend_handler(int irq, void *data)
{ struct ab8500_gpadc *gpadc = data;
staticvoid ab8500_gpadc_read_calibration_data(struct ab8500_gpadc *gpadc)
{ int i; int ret[ARRAY_SIZE(otp_cal_regs)];
u8 gpadc_cal[ARRAY_SIZE(otp_cal_regs)]; int ret_otp4[ARRAY_SIZE(otp4_cal_regs)];
u8 gpadc_otp4[ARRAY_SIZE(otp4_cal_regs)]; int vmain_high, vmain_low; int btemp_high, btemp_low; int vbat_high, vbat_low; int ibat_high, ibat_low;
s64 V_gain, V_offset, V2A_gain, V2A_offset;
/* First we read all OTP registers and store the error code */ for (i = 0; i < ARRAY_SIZE(otp_cal_regs); i++) {
ret[i] = abx500_get_register_interruptible(gpadc->dev,
AB8500_OTP_EMUL, otp_cal_regs[i], &gpadc_cal[i]); if (ret[i] < 0) { /* Continue anyway: maybe the other registers are OK */
dev_err(gpadc->dev, "%s: read otp reg 0x%02x failed\n",
__func__, otp_cal_regs[i]);
} else { /* Put this in the entropy pool as device-unique */
add_device_randomness(&ret[i], sizeof(ret[i]));
}
}
ret = regulator_enable(gpadc->vddadc); if (ret)
dev_err(dev, "Failed to enable vddadc: %d\n", ret);
return ret;
}
/** * ab8500_gpadc_parse_channel() - process devicetree channel configuration * @dev: pointer to containing device * @fwnode: fw node for the channel to configure * @ch: channel info to fill in * @iio_chan: IIO channel specification to fill in * * The devicetree will set up the channel for use with the specific device, * and define usage for things like AUX GPADC inputs more precisely.
*/ staticint ab8500_gpadc_parse_channel(struct device *dev, struct fwnode_handle *fwnode, struct ab8500_gpadc_chan_info *ch, struct iio_chan_spec *iio_chan)
{ constchar *name = fwnode_get_name(fwnode);
u32 chan; int ret;
ret = fwnode_property_read_u32(fwnode, "reg", &chan); if (ret) {
dev_err(dev, "invalid channel number %s\n", name); return ret;
} if (chan > AB8500_GPADC_CHAN_BAT_TEMP_AND_IBAT) {
dev_err(dev, "%s channel number out of range %d\n", name, chan); return -EINVAL;
}
iio_chan->channel = chan;
iio_chan->datasheet_name = name;
iio_chan->indexed = 1;
iio_chan->address = chan;
iio_chan->info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
BIT(IIO_CHAN_INFO_PROCESSED); /* Most are voltages (also temperatures), some are currents */ if ((chan == AB8500_GPADC_CHAN_MAIN_CHARGER_CURRENT) ||
(chan == AB8500_GPADC_CHAN_USB_CHARGER_CURRENT))
iio_chan->type = IIO_CURRENT; else
iio_chan->type = IIO_VOLTAGE;
/** * ab8500_gpadc_parse_channels() - Parse the GPADC channels from DT * @gpadc: the GPADC to configure the channels for * @chans_parsed: the IIO channels we parsed * @nchans_parsed: the number of IIO channels we parsed
*/ staticint ab8500_gpadc_parse_channels(struct ab8500_gpadc *gpadc, struct iio_chan_spec **chans_parsed, unsignedint *nchans_parsed)
{ struct ab8500_gpadc_chan_info *ch; struct iio_chan_spec *iio_chans; unsignedint nchans; int i;
nchans = device_get_child_node_count(gpadc->dev); if (!nchans) {
dev_err(gpadc->dev, "no channel children\n"); return -ENODEV;
}
dev_info(gpadc->dev, "found %d ADC channels\n", nchans);
iio_chans = devm_kcalloc(gpadc->dev, nchans, sizeof(*iio_chans), GFP_KERNEL); if (!iio_chans) return -ENOMEM;
gpadc->chans = devm_kcalloc(gpadc->dev, nchans, sizeof(*gpadc->chans), GFP_KERNEL); if (!gpadc->chans) return -ENOMEM;
i = 0;
device_for_each_child_node_scoped(gpadc->dev, child) { struct iio_chan_spec *iio_chan; int ret;
if (gpadc->irq_hw) {
ret = devm_request_threaded_irq(dev, gpadc->irq_hw, NULL,
ab8500_bm_gpadcconvend_handler, IRQF_NO_SUSPEND | IRQF_ONESHOT, "ab8500-gpadc-hw", gpadc); if (ret < 0) {
dev_err(dev, "Failed to request hw conversion irq: %d\n",
gpadc->irq_hw); return ret;
}
}
/* The VTVout LDO used to power the AB8500 GPADC */
gpadc->vddadc = devm_regulator_get(dev, "vddadc"); if (IS_ERR(gpadc->vddadc)) return dev_err_probe(dev, PTR_ERR(gpadc->vddadc), "failed to get vddadc\n");
ret = regulator_enable(gpadc->vddadc); if (ret) {
dev_err(dev, "failed to enable vddadc: %d\n", ret); return ret;
}
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.