drivers/spi/spi-mtk-nor.c

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

File Facts

System
Linux kernel
Corpus path
drivers/spi/spi-mtk-nor.c
Extension
.c
Size
25896 bytes
Lines
1010
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 mtk_nor_caps {
	u8 dma_bits;

	/* extra_dummy_bit is adding for the IP of new SoCs.
	 * Some new SoCs modify the timing of fetching registers' values
	 * and IDs of nor flash, they need a extra_dummy_bit which can add
	 * more clock cycles for fetching data.
	 */
	u8 extra_dummy_bit;
};

struct mtk_nor {
	struct spi_controller *ctlr;
	struct device *dev;
	void __iomem *base;
	u8 *buffer;
	dma_addr_t buffer_dma;
	struct clk *spi_clk;
	struct clk *ctlr_clk;
	struct clk *axi_clk;
	struct clk *axi_s_clk;
	unsigned int spi_freq;
	bool wbuf_en;
	bool has_irq;
	bool high_dma;
	struct completion op_done;
	const struct mtk_nor_caps *caps;
};

static inline void mtk_nor_rmw(struct mtk_nor *sp, u32 reg, u32 set, u32 clr)
{
	u32 val = readl(sp->base + reg);

	val &= ~clr;
	val |= set;
	writel(val, sp->base + reg);
}

static inline int mtk_nor_cmd_exec(struct mtk_nor *sp, u32 cmd, ulong clk)
{
	ulong delay = CLK_TO_US(sp, clk);
	u32 reg;
	int ret;

	writel(cmd, sp->base + MTK_NOR_REG_CMD);
	ret = readl_poll_timeout(sp->base + MTK_NOR_REG_CMD, reg, !(reg & cmd),
				 delay / 3, (delay + 1) * 200);
	if (ret < 0)
		dev_err(sp->dev, "command %u timeout.\n", cmd);
	return ret;
}

static void mtk_nor_reset(struct mtk_nor *sp)
{
	mtk_nor_rmw(sp, MTK_NOR_REG_CG_DIS, 0, MTK_NOR_SFC_SW_RST);
	mb(); /* flush previous writes */
	mtk_nor_rmw(sp, MTK_NOR_REG_CG_DIS, MTK_NOR_SFC_SW_RST, 0);
	mb(); /* flush previous writes */
	writel(MTK_NOR_ENABLE_SF_CMD, sp->base + MTK_NOR_REG_WP);
}

static void mtk_nor_set_addr(struct mtk_nor *sp, const struct spi_mem_op *op)
{
	u32 addr = op->addr.val;
	int i;

	for (i = 0; i < 3; i++) {
		writeb(addr & 0xff, sp->base + MTK_NOR_REG_RADR(i));
		addr >>= 8;
	}
	if (op->addr.nbytes == 4) {
		writeb(addr & 0xff, sp->base + MTK_NOR_REG_RADR3);
		mtk_nor_rmw(sp, MTK_NOR_REG_BUSCFG, MTK_NOR_4B_ADDR, 0);
	} else {
		mtk_nor_rmw(sp, MTK_NOR_REG_BUSCFG, 0, MTK_NOR_4B_ADDR);
	}
}

static bool need_bounce(struct mtk_nor *sp, const struct spi_mem_op *op)
{
	return ((uintptr_t)op->data.buf.in & MTK_NOR_DMA_ALIGN_MASK);
}

static bool mtk_nor_match_read(const struct spi_mem_op *op)
{
	int dummy = 0;

	if (op->dummy.nbytes)
		dummy = op->dummy.nbytes * BITS_PER_BYTE / op->dummy.buswidth;

Annotation

Implementation Notes