drivers/spi/spi-virtio.c

Source file repositories/reference/linux-study-clean/drivers/spi/spi-virtio.c

File Facts

System
Linux kernel
Corpus path
drivers/spi/spi-virtio.c
Extension
.c
Size
12386 bytes
Lines
429
Domain
Driver Families
Bucket
drivers/spi
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 virtio_spi_req {
	struct completion completion;
	const u8 *tx_buf;
	u8 *rx_buf;
	struct spi_transfer_head transfer_head	____cacheline_aligned;
	struct spi_transfer_result result;
};

struct virtio_spi_priv {
	/* The virtio device we're associated with */
	struct virtio_device *vdev;
	/* Pointer to the virtqueue */
	struct virtqueue *vq;
	/* Copy of config space mode_func_supported */
	u32 mode_func_supported;
	/* Copy of config space max_freq_hz */
	u32 max_freq_hz;
};

static void virtio_spi_msg_done(struct virtqueue *vq)
{
	struct virtio_spi_req *req;
	unsigned int len;

	while ((req = virtqueue_get_buf(vq, &len)))
		complete(&req->completion);
}

/*
 * virtio_spi_set_delays - Set delay parameters for SPI transfer
 *
 * This function sets various delay parameters for SPI transfer,
 * including delay after CS asserted, timing intervals between
 * adjacent words within a transfer, delay before and after CS
 * deasserted. It converts these delay parameters to nanoseconds
 * using spi_delay_to_ns and stores the results in spi_transfer_head
 * structure.
 * If the conversion fails, the function logs a warning message and
 * returns an error code.
 *       .   .      .    .    .   .   .   .   .   .
 * Delay + A +      + B  +    + C + D + E + F + A +
 *       .   .      .    .    .   .   .   .   .   .
 *    ___.   .      .    .    .   .   .___.___.   .
 * CS#   |___.______.____.____.___.___|   .   |___._____________
 *       .   .      .    .    .   .   .   .   .   .
 *       .   .      .    .    .   .   .   .   .   .
 * SCLK__.___.___NNN_____NNN__.___.___.___.___.___.___NNN_______
 *
 * NOTE: 1st transfer has two words, the delay between these two words are
 * 'B' in the diagram.
 *
 * A => struct spi_device -> cs_setup
 * B => max{struct spi_transfer -> word_delay, struct spi_device -> word_delay}
 *   Note: spi_device and spi_transfer both have word_delay, Linux
 *         choose the bigger one, refer to _spi_xfer_word_delay_update function
 * C => struct spi_transfer -> delay
 * D => struct spi_device -> cs_hold
 * E => struct spi_device -> cs_inactive
 * F => struct spi_transfer -> cs_change_delay
 *
 * So the corresponding relationship:
 * A   <===> cs_setup_ns (after CS asserted)
 * B   <===> word_delay_ns (delay between adjacent words within a transfer)
 * C+D <===> cs_delay_hold_ns (before CS deasserted)
 * E+F <===> cs_change_delay_inactive_ns (after CS deasserted, these two
 * values are also recommended in the Linux driver to be added up)
 */
static int virtio_spi_set_delays(struct spi_transfer_head *th,
				 struct spi_device *spi,
				 struct spi_transfer *xfer)
{
	int cs_setup;
	int cs_word_delay_xfer;
	int cs_word_delay_spi;
	int delay;
	int cs_hold;
	int cs_inactive;
	int cs_change_delay;

	cs_setup = spi_delay_to_ns(&spi->cs_setup, xfer);
	if (cs_setup < 0) {
		dev_warn(&spi->dev, "Cannot convert cs_setup\n");
		return cs_setup;
	}
	th->cs_setup_ns = cpu_to_le32(cs_setup);

	cs_word_delay_xfer = spi_delay_to_ns(&xfer->word_delay, xfer);
	if (cs_word_delay_xfer < 0) {
		dev_warn(&spi->dev, "Cannot convert cs_word_delay_xfer\n");
		return cs_word_delay_xfer;

Annotation

Implementation Notes