drivers/iio/adc/mcp320x.c

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

File Facts

System
Linux kernel
Corpus path
drivers/iio/adc/mcp320x.c
Extension
.c
Size
13375 bytes
Lines
510
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 mcp320x_chip_info {
	const struct iio_chan_spec *channels;
	unsigned int num_channels;
	unsigned int resolution;
	unsigned int conv_time; /* usec */
};

/**
 * struct mcp320x - Microchip SPI ADC instance
 * @spi: SPI slave (parent of the IIO device)
 * @msg: SPI message to select a channel and receive a value from the ADC
 * @transfer: SPI transfers used by @msg
 * @start_conv_msg: SPI message to start a conversion by briefly asserting CS
 * @start_conv_transfer: SPI transfer used by @start_conv_msg
 * @reg: regulator generating Vref
 * @lock: protects read sequences
 * @chip_info: ADC properties
 * @tx_buf: buffer for @transfer[0] (not used on single-channel converters)
 * @rx_buf: buffer for @transfer[1]
 */
struct mcp320x {
	struct spi_device *spi;
	struct spi_message msg;
	struct spi_transfer transfer[2];
	struct spi_message start_conv_msg;
	struct spi_transfer start_conv_transfer;

	struct regulator *reg;
	struct mutex lock;
	const struct mcp320x_chip_info *chip_info;

	u8 tx_buf __aligned(IIO_DMA_MINALIGN);
	u8 rx_buf[4];
};

static int mcp320x_channel_to_tx_data(int device_index,
			const unsigned int channel, bool differential)
{
	int start_bit = 1;

	switch (device_index) {
	case mcp3002:
	case mcp3202:
		return ((start_bit << 4) | (!differential << 3) |
							(channel << 2));
	case mcp3004:
	case mcp3204:
	case mcp3008:
	case mcp3208:
		return ((start_bit << 6) | (!differential << 5) |
							(channel << 2));
	default:
		return -EINVAL;
	}
}

static int mcp320x_adc_conversion(struct mcp320x *adc, u8 channel,
				  bool differential, int device_index, int *val)
{
	int ret;

	if (adc->chip_info->conv_time) {
		ret = spi_sync(adc->spi, &adc->start_conv_msg);
		if (ret < 0)
			return ret;

		usleep_range(adc->chip_info->conv_time,
			     adc->chip_info->conv_time + 100);
	}

	memset(&adc->rx_buf, 0, sizeof(adc->rx_buf));
	if (adc->chip_info->num_channels > 1)
		adc->tx_buf = mcp320x_channel_to_tx_data(device_index, channel,
							 differential);

	ret = spi_sync(adc->spi, &adc->msg);
	if (ret < 0)
		return ret;

	switch (device_index) {
	case mcp3001:
		*val = (adc->rx_buf[0] << 5 | adc->rx_buf[1] >> 3);
		return 0;
	case mcp3002:
	case mcp3004:
	case mcp3008:
		*val = (adc->rx_buf[0] << 2 | adc->rx_buf[1] >> 6);
		return 0;
	case mcp3201:
		*val = (adc->rx_buf[0] << 7 | adc->rx_buf[1] >> 1);

Annotation

Implementation Notes