drivers/spi/spi-sifive.c

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

File Facts

System
Linux kernel
Corpus path
drivers/spi/spi-sifive.c
Extension
.c
Size
13593 bytes
Lines
465
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 sifive_spi {
	void __iomem      *regs;        /* virt. address of control registers */
	struct clk        *clk;         /* bus clock */
	unsigned int      fifo_depth;   /* fifo depth in words */
	u32               cs_inactive;  /* level of the CS pins when inactive */
	struct completion done;         /* wake-up from interrupt */
};

static void sifive_spi_write(struct sifive_spi *spi, int offset, u32 value)
{
	iowrite32(value, spi->regs + offset);
}

static u32 sifive_spi_read(struct sifive_spi *spi, int offset)
{
	return ioread32(spi->regs + offset);
}

static void sifive_spi_init(struct sifive_spi *spi)
{
	/* Watermark interrupts are disabled by default */
	sifive_spi_write(spi, SIFIVE_SPI_REG_IE, 0);

	/* Default watermark FIFO threshold values */
	sifive_spi_write(spi, SIFIVE_SPI_REG_TXMARK, 1);
	sifive_spi_write(spi, SIFIVE_SPI_REG_RXMARK, 0);

	/* Set CS/SCK Delays and Inactive Time to defaults */
	sifive_spi_write(spi, SIFIVE_SPI_REG_DELAY0,
			 SIFIVE_SPI_DELAY0_CSSCK(1) |
			 SIFIVE_SPI_DELAY0_SCKCS(1));
	sifive_spi_write(spi, SIFIVE_SPI_REG_DELAY1,
			 SIFIVE_SPI_DELAY1_INTERCS(1) |
			 SIFIVE_SPI_DELAY1_INTERXFR(0));

	/* Exit specialized memory-mapped SPI flash mode */
	sifive_spi_write(spi, SIFIVE_SPI_REG_FCTRL, 0);
}

static int
sifive_spi_prepare_message(struct spi_controller *host, struct spi_message *msg)
{
	struct sifive_spi *spi = spi_controller_get_devdata(host);
	struct spi_device *device = msg->spi;

	/* Update the chip select polarity */
	if (device->mode & SPI_CS_HIGH)
		spi->cs_inactive &= ~BIT(spi_get_chipselect(device, 0));
	else
		spi->cs_inactive |= BIT(spi_get_chipselect(device, 0));
	sifive_spi_write(spi, SIFIVE_SPI_REG_CSDEF, spi->cs_inactive);

	/* Select the correct device */
	sifive_spi_write(spi, SIFIVE_SPI_REG_CSID, spi_get_chipselect(device, 0));

	/* Set clock mode */
	sifive_spi_write(spi, SIFIVE_SPI_REG_SCKMODE,
			 device->mode & SIFIVE_SPI_SCKMODE_MODE_MASK);

	return 0;
}

static void sifive_spi_set_cs(struct spi_device *device, bool is_high)
{
	struct sifive_spi *spi = spi_controller_get_devdata(device->controller);

	/* Reverse polarity is handled by SCMR/CPOL. Not inverted CS. */
	if (device->mode & SPI_CS_HIGH)
		is_high = !is_high;

	sifive_spi_write(spi, SIFIVE_SPI_REG_CSMODE, is_high ?
			 SIFIVE_SPI_CSMODE_MODE_AUTO :
			 SIFIVE_SPI_CSMODE_MODE_HOLD);
}

static int
sifive_spi_prep_transfer(struct sifive_spi *spi, struct spi_device *device,
			 struct spi_transfer *t)
{
	u32 cr;
	unsigned int mode;

	/* Calculate and program the clock rate */
	cr = DIV_ROUND_UP(clk_get_rate(spi->clk) >> 1, t->speed_hz) - 1;
	cr &= SIFIVE_SPI_SCKDIV_DIV_MASK;
	sifive_spi_write(spi, SIFIVE_SPI_REG_SCKDIV, cr);

	mode = max_t(unsigned int, t->rx_nbits, t->tx_nbits);

	/* Set frame format */

Annotation

Implementation Notes