drivers/iio/adc/mt6360-adc.c

Source file repositories/reference/linux-study-clean/drivers/iio/adc/mt6360-adc.c

File Facts

System
Linux kernel
Corpus path
drivers/iio/adc/mt6360-adc.c
Extension
.c
Size
10047 bytes
Lines
373
Domain
Driver Families
Bucket
drivers/iio
Inferred role
Driver Families: implementation source
Status
source implementation candidate

Why This File Exists

Repeatable hardware-adapter layer. Deep compatibility for every driver is out of scope; this atlas records patterns, probe lifecycles, bus glue, IRQ/DMA usage, and links back to core abstractions.

Dependency Surface

Detected Declarations

Annotated Snippet

struct mt6360_adc_data {
	struct device *dev;
	struct regmap *regmap;
	/* Due to only one set of ADC control, this lock is used to prevent the race condition */
	struct mutex adc_lock;
	ktime_t last_off_timestamps[MT6360_CHAN_MAX];
};

static int mt6360_adc_read_channel(struct mt6360_adc_data *mad, int channel, int *val)
{
	__be16 adc_enable;
	u8 rpt[3];
	ktime_t predict_end_t, timeout;
	unsigned int pre_wait_time;
	int ret;

	mutex_lock(&mad->adc_lock);

	/* Select the preferred ADC channel */
	ret = regmap_update_bits(mad->regmap, MT6360_REG_PMUADCRPT1, MT6360_PREFERCH_MASK,
				 channel << MT6360_PREFERCH_SHFT);
	if (ret)
		goto out_adc_lock;

	adc_enable = cpu_to_be16(MT6360_ADCEN_MASK | BIT(channel));
	ret = regmap_raw_write(mad->regmap, MT6360_REG_PMUADCCFG, &adc_enable, sizeof(adc_enable));
	if (ret)
		goto out_adc_lock;

	predict_end_t = ktime_add_ms(mad->last_off_timestamps[channel], 2 * ADC_WAIT_TIME_MS);

	if (ktime_after(ktime_get(), predict_end_t))
		pre_wait_time = ADC_WAIT_TIME_MS;
	else
		pre_wait_time = 3 * ADC_WAIT_TIME_MS;

	if (msleep_interruptible(pre_wait_time)) {
		ret = -ERESTARTSYS;
		goto out_adc_conv;
	}

	timeout = ktime_add_ms(ktime_get(), ADC_CONV_TIMEOUT_MS);
	while (true) {
		ret = regmap_raw_read(mad->regmap, MT6360_REG_PMUADCRPT1, rpt, sizeof(rpt));
		if (ret)
			goto out_adc_conv;

		/*
		 * There are two functions, ZCV and TypeC OTP, running ADC VBAT and TS in
		 * background, and ADC samples are taken on a fixed frequency no matter read the
		 * previous one or not.
		 * To avoid conflict, We set minimum time threshold after enable ADC and
		 * check report channel is the same.
		 * The worst case is run the same ADC twice and background function is also running,
		 * ADC conversion sequence is desire channel before start ADC, background ADC,
		 * desire channel after start ADC.
		 * So the minimum correct data is three times of typical conversion time.
		 */
		if ((rpt[0] & MT6360_RPTCH_MASK) == channel)
			break;

		if (ktime_compare(ktime_get(), timeout) > 0) {
			ret = -ETIMEDOUT;
			goto out_adc_conv;
		}

		usleep_range(ADC_LOOP_TIME_US / 2, ADC_LOOP_TIME_US);
	}

	*val = get_unaligned_be16(&rpt[1]);
	ret = IIO_VAL_INT;

out_adc_conv:
	/* Only keep ADC enable */
	adc_enable = cpu_to_be16(MT6360_ADCEN_MASK);
	regmap_raw_write(mad->regmap, MT6360_REG_PMUADCCFG, &adc_enable, sizeof(adc_enable));
	mad->last_off_timestamps[channel] = ktime_get();
	/* Config prefer channel to NO_PREFER */
	regmap_update_bits(mad->regmap, MT6360_REG_PMUADCRPT1, MT6360_PREFERCH_MASK,
			   MT6360_NO_PREFER << MT6360_PREFERCH_SHFT);
out_adc_lock:
	mutex_unlock(&mad->adc_lock);

	return ret;
}

static int mt6360_adc_read_scale(struct mt6360_adc_data *mad, int channel, int *val, int *val2)
{
	unsigned int regval;
	int ret;

Annotation

Implementation Notes