drivers/spi/spi-sg2044-nor.c

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

File Facts

System
Linux kernel
Corpus path
drivers/spi/spi-sg2044-nor.c
Extension
.c
Size
13769 bytes
Lines
511
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 sg204x_spifmc_chip_info {
	bool has_opt_reg;
	u32 rd_fifo_int_trigger_level;
};

struct sg2044_spifmc {
	struct spi_controller *ctrl;
	void __iomem *io_base;
	struct device *dev;
	struct mutex lock;
	struct clk *clk;
	const struct sg204x_spifmc_chip_info *chip_info;
};

static int sg2044_spifmc_wait_int(struct sg2044_spifmc *spifmc, u8 int_type)
{
	u32 stat;

	return readl_poll_timeout(spifmc->io_base + SPIFMC_INT_STS, stat,
				  (stat & int_type), 0, 1000000);
}

static int sg2044_spifmc_wait_xfer_size(struct sg2044_spifmc *spifmc,
					int xfer_size)
{
	u8 stat;

	return readl_poll_timeout(spifmc->io_base + SPIFMC_FIFO_PT, stat,
				  ((stat & 0xf) == xfer_size), 1, 1000000);
}

static u32 sg2044_spifmc_init_reg(struct sg2044_spifmc *spifmc)
{
	u32 reg;

	reg = readl(spifmc->io_base + SPIFMC_TRAN_CSR);
	reg &= ~(SPIFMC_TRAN_CSR_TRAN_MODE_MASK |
		 SPIFMC_TRAN_CSR_FAST_MODE |
		 SPIFMC_TRAN_CSR_BUS_WIDTH_MASK |
		 SPIFMC_TRAN_CSR_DMA_EN |
		 SPIFMC_TRAN_CSR_ADDR_BYTES_MASK |
		 SPIFMC_TRAN_CSR_WITH_CMD |
		 SPIFMC_TRAN_CSR_FIFO_TRG_LVL_MASK);

	writel(reg, spifmc->io_base + SPIFMC_TRAN_CSR);

	return reg;
}

static ssize_t sg2044_spifmc_read_64k(struct sg2044_spifmc *spifmc,
				      const struct spi_mem_op *op, loff_t from,
				      size_t len, u_char *buf)
{
	int xfer_size, offset;
	u32 reg;
	int ret;
	int i;

	reg = sg2044_spifmc_init_reg(spifmc);
	reg |= (op->addr.nbytes + op->dummy.nbytes) << SPIFMC_TRAN_CSR_ADDR_BYTES_SHIFT;
	reg |= spifmc->chip_info->rd_fifo_int_trigger_level;
	reg |= SPIFMC_TRAN_CSR_WITH_CMD;
	reg |= SPIFMC_TRAN_CSR_TRAN_MODE_RX;

	writel(0, spifmc->io_base + SPIFMC_FIFO_PT);
	writeb(op->cmd.opcode, spifmc->io_base + SPIFMC_FIFO_PORT);

	for (i = op->addr.nbytes - 1; i >= 0; i--)
		writeb((from >> i * 8) & 0xff, spifmc->io_base + SPIFMC_FIFO_PORT);

	for (i = 0; i < op->dummy.nbytes; i++)
		writeb(0xff, spifmc->io_base + SPIFMC_FIFO_PORT);

	writel(len, spifmc->io_base + SPIFMC_TRAN_NUM);
	writel(0, spifmc->io_base + SPIFMC_INT_STS);
	reg |= SPIFMC_TRAN_CSR_GO_BUSY;
	writel(reg, spifmc->io_base + SPIFMC_TRAN_CSR);

	ret = sg2044_spifmc_wait_int(spifmc, SPIFMC_INT_RD_FIFO);
	if (ret < 0)
		return ret;

	offset = 0;
	while (offset < len) {
		xfer_size = min_t(size_t, SPIFMC_MAX_FIFO_DEPTH, len - offset);

		ret = sg2044_spifmc_wait_xfer_size(spifmc, xfer_size);
		if (ret < 0)
			return ret;

Annotation

Implementation Notes