drivers/iio/adc/mxs-lradc-adc.c

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

File Facts

System
Linux kernel
Corpus path
drivers/iio/adc/mxs-lradc-adc.c
Extension
.c
Size
23068 bytes
Lines
830
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 mxs_lradc_scale {
	unsigned int		integer;
	unsigned int		nano;
};

struct mxs_lradc_adc {
	struct mxs_lradc	*lradc;
	struct device		*dev;

	void __iomem		*base;
	/* Maximum of 8 channels + 8 byte ts */
	u32			buffer[10] __aligned(8);
	struct iio_trigger	*trig;
	struct completion	completion;
	spinlock_t		lock;

	const u32		*vref_mv;
	struct mxs_lradc_scale	scale_avail[LRADC_MAX_TOTAL_CHANS][2];
	unsigned long		is_divided;
};


/* Raw I/O operations */
static int mxs_lradc_adc_read_single(struct iio_dev *iio_dev, int chan,
				     int *val)
{
	struct mxs_lradc_adc *adc = iio_priv(iio_dev);
	struct mxs_lradc *lradc = adc->lradc;
	int ret;

	/*
	 * See if there is no buffered operation in progress. If there is simply
	 * bail out. This can be improved to support both buffered and raw IO at
	 * the same time, yet the code becomes horribly complicated. Therefore I
	 * applied KISS principle here.
	 */
	if (!iio_device_claim_direct(iio_dev))
		return -EBUSY;

	reinit_completion(&adc->completion);

	/*
	 * No buffered operation in progress, map the channel and trigger it.
	 * Virtual channel 0 is always used here as the others are always not
	 * used if doing raw sampling.
	 */
	if (lradc->soc == IMX28_LRADC)
		writel(LRADC_CTRL1_LRADC_IRQ_EN(0),
		       adc->base + LRADC_CTRL1 + STMP_OFFSET_REG_CLR);
	writel(0x1, adc->base + LRADC_CTRL0 + STMP_OFFSET_REG_CLR);

	/* Enable / disable the divider per requirement */
	if (test_bit(chan, &adc->is_divided))
		writel(1 << LRADC_CTRL2_DIVIDE_BY_TWO_OFFSET,
		       adc->base + LRADC_CTRL2 + STMP_OFFSET_REG_SET);
	else
		writel(1 << LRADC_CTRL2_DIVIDE_BY_TWO_OFFSET,
		       adc->base + LRADC_CTRL2 + STMP_OFFSET_REG_CLR);

	/* Clean the slot's previous content, then set new one. */
	writel(LRADC_CTRL4_LRADCSELECT_MASK(0),
	       adc->base + LRADC_CTRL4 + STMP_OFFSET_REG_CLR);
	writel(chan, adc->base + LRADC_CTRL4 + STMP_OFFSET_REG_SET);

	writel(0, adc->base + LRADC_CH(0));

	/* Enable the IRQ and start sampling the channel. */
	writel(LRADC_CTRL1_LRADC_IRQ_EN(0),
	       adc->base + LRADC_CTRL1 + STMP_OFFSET_REG_SET);
	writel(BIT(0), adc->base + LRADC_CTRL0 + STMP_OFFSET_REG_SET);

	/* Wait for completion on the channel, 1 second max. */
	ret = wait_for_completion_killable_timeout(&adc->completion, HZ);
	if (!ret)
		ret = -ETIMEDOUT;
	if (ret < 0)
		goto err;

	/* Read the data. */
	*val = readl(adc->base + LRADC_CH(0)) & LRADC_CH_VALUE_MASK;
	ret = IIO_VAL_INT;

err:
	writel(LRADC_CTRL1_LRADC_IRQ_EN(0),
	       adc->base + LRADC_CTRL1 + STMP_OFFSET_REG_CLR);

	iio_device_release_direct(iio_dev);

	return ret;
}

Annotation

Implementation Notes