xref: /rk3399_rockchip-uboot/drivers/spi/rockchip_sfc.c (revision 5b4dcfe0cf498cddcdb1a04ca74d5da5a81cf325)
1c1cd6cb6SAndy Yan /*
2c6d59f03SAndy Yan  * SFC driver for rockchip
3c1cd6cb6SAndy Yan  *
4c6d59f03SAndy Yan  * (C) Copyright 2017 Rockchip Electronics Co., Ltd
5c1cd6cb6SAndy Yan  * Yifeng.zhao, Software Engineering, <zhao0116@gmail.com>.
6c1cd6cb6SAndy Yan  *
7c1cd6cb6SAndy Yan  * SPDX-License-Identifier:     GPL-2.0+
8c1cd6cb6SAndy Yan  */
9c1cd6cb6SAndy Yan 
10c1cd6cb6SAndy Yan #include <common.h>
1137911cf6SAndy Yan #include <bouncebuf.h>
12c1cd6cb6SAndy Yan #include <clk.h>
13c1cd6cb6SAndy Yan #include <dm.h>
14c1cd6cb6SAndy Yan #include <dt-structs.h>
15c1cd6cb6SAndy Yan #include <errno.h>
16c1cd6cb6SAndy Yan #include <spi.h>
17c1cd6cb6SAndy Yan #include <linux/errno.h>
18c1cd6cb6SAndy Yan #include <asm/io.h>
19c1cd6cb6SAndy Yan #include <asm/arch/clock.h>
20c1cd6cb6SAndy Yan #include <asm/arch/periph.h>
21c1cd6cb6SAndy Yan #include <dm/pinctrl.h>
22c1cd6cb6SAndy Yan 
23c1cd6cb6SAndy Yan DECLARE_GLOBAL_DATA_PTR;
24c1cd6cb6SAndy Yan 
2539b850deSJon Lin #ifdef SFC_DEBUG
2639b850deSJon Lin #define SFC_DBG printf
2739b850deSJon Lin #else
2839b850deSJon Lin #define SFC_DBG(args...)
2939b850deSJon Lin #endif
3039b850deSJon Lin 
31c6d59f03SAndy Yan struct rockchip_sfc_reg {
32c6d59f03SAndy Yan 	u32 ctrl;
33c6d59f03SAndy Yan 	u32 imr;
34c6d59f03SAndy Yan 	u32 iclr;
35c6d59f03SAndy Yan 	u32 ftlr;
36c6d59f03SAndy Yan 	u32 rcvr;
37c6d59f03SAndy Yan 	u32 ax;
38c6d59f03SAndy Yan 	u32 abit;
39c6d59f03SAndy Yan 	u32 isr;
40c6d59f03SAndy Yan 	u32 fsr;
41c6d59f03SAndy Yan 	u32 sr;
42c6d59f03SAndy Yan 	u32 risr;
43fa413375SJon Lin 	u32 ver;
44fa413375SJon Lin 	u32 reserved[20];
45c6d59f03SAndy Yan 	u32 dmatr;
46c6d59f03SAndy Yan 	u32 dmaaddr;
47fa413375SJon Lin 	u32 len_ctrl;
48fa413375SJon Lin 	u32 len_ext;
49fa413375SJon Lin 	u32 reserved1[28];
50c6d59f03SAndy Yan 	u32 cmd;
51c6d59f03SAndy Yan 	u32 addr;
52c6d59f03SAndy Yan 	u32 data;
53c6d59f03SAndy Yan };
5439b850deSJon Lin 
55c6d59f03SAndy Yan check_member(rockchip_sfc_reg, data, 0x108);
56c6d59f03SAndy Yan 
57c6d59f03SAndy Yan /*SFC_CTRL*/
58c6d59f03SAndy Yan #define SFC_DATA_WIDTH_SHIFT	12
59c6d59f03SAndy Yan #define SFC_DATA_WIDTH_MASK	GENMASK(13, 12)
60c6d59f03SAndy Yan #define SFC_ADDR_WIDTH_SHIFT	10
61c6d59f03SAndy Yan #define SFC_ADDR_WIDTH_MASK	GENMASK(11, 10)
62c6d59f03SAndy Yan #define SFC_CMD_WIDTH_SHIFT	8
63c6d59f03SAndy Yan #define SFC_CMD_WIDTH_MASK	GENMASK(9, 8)
64c6d59f03SAndy Yan #define SFC_DATA_SHIFT_NEGETIVE	BIT(1)
65c6d59f03SAndy Yan 
66c6d59f03SAndy Yan /*SFC_CMD*/
67c6d59f03SAndy Yan #define SFC_DUMMY_BITS_SHIFT	8
68c6d59f03SAndy Yan #define SFC_RW_SHIFT		12
69c6d59f03SAndy Yan #define SFC_WR			1
70c6d59f03SAndy Yan #define SFC_RD			0
71c6d59f03SAndy Yan #define SFC_ADDR_BITS_SHIFT	14
72c6d59f03SAndy Yan #define SFC_ADDR_BITS_MASK	GENMASK(15, 14)
73c6d59f03SAndy Yan #define SFC_ADDR_0BITS		0
74c6d59f03SAndy Yan #define SFC_ADDR_24BITS		1
75c6d59f03SAndy Yan #define SFC_ADDR_32BITS		2
76c6d59f03SAndy Yan #define SFC_ADDR_XBITS		3
77c6d59f03SAndy Yan #define SFC_TRB_SHIFT		(16)
78c6d59f03SAndy Yan #define SFC_TRB_MASK		GENMASK(29, 16)
79c6d59f03SAndy Yan 
80c6d59f03SAndy Yan /* Dma start trigger signal. Auto cleared after write */
81c6d59f03SAndy Yan #define SFC_DMA_START		BIT(0)
82c6d59f03SAndy Yan 
83c6d59f03SAndy Yan #define SFC_RESET		BIT(0)
84c6d59f03SAndy Yan 
85c6d59f03SAndy Yan /*SFC_FSR*/
86c6d59f03SAndy Yan #define SFC_RXLV_SHIFT		(16)
87c6d59f03SAndy Yan #define SFC_RXLV_MASK		GENMASK(20, 16)
88c6d59f03SAndy Yan #define SFC_TXLV_SHIFT		(8)
89c6d59f03SAndy Yan #define SFC_TXLV_MASK		GENMASK(12, 8)
90c6d59f03SAndy Yan #define SFC_RX_FULL		BIT(3)	/* rx fifo full */
91c6d59f03SAndy Yan #define SFC_RX_EMPTY		BIT(2)	/* rx fifo empty */
92c6d59f03SAndy Yan #define SFC_TX_EMPTY		BIT(1)	/* tx fifo empty */
93c6d59f03SAndy Yan #define SFC_TX_FULL		BIT(0)	/* tx fifo full */
94c6d59f03SAndy Yan 
95c6d59f03SAndy Yan #define SFC_BUSY		BIT(0)	/* sfc busy flag */
96c6d59f03SAndy Yan 
97c6d59f03SAndy Yan /*SFC_RISR*/
98c6d59f03SAndy Yan #define DMA_FINISH_INT		BIT(7)        /* dma interrupt */
99c6d59f03SAndy Yan #define SPI_ERR_INT		BIT(6)        /* Nspi error interrupt */
100c6d59f03SAndy Yan #define AHB_ERR_INT		BIT(5)        /* Ahb bus error interrupt */
101c6d59f03SAndy Yan #define TRANS_FINISH_INT	BIT(4)        /* Transfer finish interrupt */
102c6d59f03SAndy Yan #define TX_EMPTY_INT		BIT(3)        /* Tx fifo empty interrupt */
103c6d59f03SAndy Yan #define TX_OF_INT		BIT(2)        /* Tx fifo overflow interrupt */
104c6d59f03SAndy Yan #define RX_UF_INT		BIT(1)        /* Rx fifo underflow interrupt */
105c6d59f03SAndy Yan #define RX_FULL_INT		BIT(0)        /* Rx fifo full interrupt */
106c6d59f03SAndy Yan 
107fa413375SJon Lin #define SFC_MAX_TRB_VER3	(512 * 31)
108fa413375SJon Lin #define SFC_MAX_TRB_VER4	(0xFFFFFFFF)
109c6d59f03SAndy Yan 
11065c35614SJon Lin #define SFC_MAX_RATE		(100 * 1000 * 1000)
111ae52cbcbSJon Lin #define SFC_DEFAULT_RATE	(80 * 1000 * 1000)
112ae52cbcbSJon Lin #define SFC_MIN_RATE		(10 * 1000 * 1000)
113ae52cbcbSJon Lin 
114fa413375SJon Lin #define SFC_VER_3		0x3
115fa413375SJon Lin #define SFC_VER_4		0x4
116fa413375SJon Lin 
117c1cd6cb6SAndy Yan enum rockchip_sfc_if_type {
118c1cd6cb6SAndy Yan 	IF_TYPE_STD,
119c1cd6cb6SAndy Yan 	IF_TYPE_DUAL,
120c1cd6cb6SAndy Yan 	IF_TYPE_QUAD,
121c1cd6cb6SAndy Yan };
122c1cd6cb6SAndy Yan 
123c1cd6cb6SAndy Yan struct rockchip_sfc_platdata {
124c1cd6cb6SAndy Yan 	s32 frequency;
12514b86dc9SJon Lin 	void *base;
126c1cd6cb6SAndy Yan };
127c1cd6cb6SAndy Yan 
128c1cd6cb6SAndy Yan struct rockchip_sfc {
129c1cd6cb6SAndy Yan 	struct rockchip_sfc_reg *regbase;
130c1cd6cb6SAndy Yan 	struct clk clk;
131c1cd6cb6SAndy Yan 	unsigned int max_freq;
132c1cd6cb6SAndy Yan 	unsigned int mode;
133c1cd6cb6SAndy Yan 	unsigned int speed_hz;
134fa413375SJon Lin 	u32 max_iosize;
1357ddc1c35SJon Lin 	bool prepare;
1367ddc1c35SJon Lin 	u32 last_prepare_size;
137c1cd6cb6SAndy Yan 	u32 cmd;
138c1cd6cb6SAndy Yan 	u32 addr;
139da4954b7SAndy Yan 	u8 addr_bits;
14088ea3acbSJon Lin 	u8 addr_xbits_ext;
141da4954b7SAndy Yan 	u8 dummy_bits;
142da4954b7SAndy Yan 	u8 rw;
143da4954b7SAndy Yan 	u32 trb;
144c1cd6cb6SAndy Yan };
145c1cd6cb6SAndy Yan 
146c1cd6cb6SAndy Yan static int rockchip_sfc_ofdata_to_platdata(struct udevice *bus)
147c1cd6cb6SAndy Yan {
148c1cd6cb6SAndy Yan 	struct rockchip_sfc_platdata *plat = dev_get_platdata(bus);
149*5b4dcfe0SJason Zhu 
150*5b4dcfe0SJason Zhu 	plat->base = dev_read_addr_ptr(bus);
151*5b4dcfe0SJason Zhu #if CONFIG_IS_ENABLED(CLK)
152c1cd6cb6SAndy Yan 	struct rockchip_sfc *sfc = dev_get_priv(bus);
153c1cd6cb6SAndy Yan 	int ret;
154c1cd6cb6SAndy Yan 
155c1cd6cb6SAndy Yan 	ret = clk_get_by_index(bus, 0, &sfc->clk);
156c1cd6cb6SAndy Yan 	if (ret < 0) {
15714b86dc9SJon Lin 		printf("Could not get clock for %s: %d\n", bus->name, ret);
158c1cd6cb6SAndy Yan 		return ret;
159c1cd6cb6SAndy Yan 	}
160*5b4dcfe0SJason Zhu #endif
161c1cd6cb6SAndy Yan 
162c1cd6cb6SAndy Yan 	return 0;
163c1cd6cb6SAndy Yan }
164c1cd6cb6SAndy Yan 
165fa413375SJon Lin u32 rockchip_sfc_get_version(struct rockchip_sfc *sfc)
166fa413375SJon Lin {
167fa413375SJon Lin 	struct rockchip_sfc_reg *regs = sfc->regbase;
168fa413375SJon Lin 
169fa413375SJon Lin 	return  (u32)(readl(&regs->ver) & 0xFFFF);
170fa413375SJon Lin }
171fa413375SJon Lin 
172c1cd6cb6SAndy Yan static int rockchip_sfc_probe(struct udevice *bus)
173c1cd6cb6SAndy Yan {
174c1cd6cb6SAndy Yan 	struct rockchip_sfc_platdata *plat = dev_get_platdata(bus);
175c1cd6cb6SAndy Yan 	struct rockchip_sfc *sfc = dev_get_priv(bus);
176fa413375SJon Lin 	struct rockchip_sfc_reg *regs;
177ae52cbcbSJon Lin 	struct dm_spi_bus *dm_spi_bus;
178c1cd6cb6SAndy Yan 
179ae52cbcbSJon Lin 	dm_spi_bus = bus->uclass_priv;
180ae52cbcbSJon Lin 	dm_spi_bus->max_hz = plat->frequency;
181c1cd6cb6SAndy Yan 	sfc->regbase = (struct rockchip_sfc_reg *)plat->base;
18231e5d7a3SJon Lin 	sfc->max_freq = SFC_MAX_RATE;
18331e5d7a3SJon Lin 	sfc->speed_hz = SFC_DEFAULT_RATE;
184*5b4dcfe0SJason Zhu #if CONFIG_IS_ENABLED(CLK)
18531e5d7a3SJon Lin 	clk_set_rate(&sfc->clk, sfc->speed_hz);
186*5b4dcfe0SJason Zhu #endif
187fa413375SJon Lin 	regs = sfc->regbase;
188fa413375SJon Lin 	if (rockchip_sfc_get_version(sfc) >= SFC_VER_4) {
189fa413375SJon Lin 		sfc->max_iosize = SFC_MAX_TRB_VER4;
190fa413375SJon Lin 		writel(1, &regs->len_ctrl);
191fa413375SJon Lin 	} else {
192fa413375SJon Lin 		sfc->max_iosize = SFC_MAX_TRB_VER3;
193fa413375SJon Lin 	}
194fa413375SJon Lin 
195c1cd6cb6SAndy Yan 	return 0;
196c1cd6cb6SAndy Yan }
197c1cd6cb6SAndy Yan 
198c1cd6cb6SAndy Yan static int rockchip_sfc_reset(struct rockchip_sfc *sfc)
199c1cd6cb6SAndy Yan {
200c1cd6cb6SAndy Yan 	struct rockchip_sfc_reg *regs = sfc->regbase;
201c1cd6cb6SAndy Yan 	int tbase = get_timer(0);
202c1cd6cb6SAndy Yan 	u32 rcvr;
203c1cd6cb6SAndy Yan 	int ret = 0;
204c1cd6cb6SAndy Yan 
205c1cd6cb6SAndy Yan 	writel(SFC_RESET, &regs->rcvr);
206c1cd6cb6SAndy Yan 	do {
207c1cd6cb6SAndy Yan 		rcvr = readl(&regs->rcvr);
208c1cd6cb6SAndy Yan 		if (get_timer(tbase) > 1000) {
209c1cd6cb6SAndy Yan 			debug("sfc reset timeout\n");
210c1cd6cb6SAndy Yan 			ret =  -ETIMEDOUT;
211c1cd6cb6SAndy Yan 			break;
212c1cd6cb6SAndy Yan 		}
213c1cd6cb6SAndy Yan 		udelay(1);
214c1cd6cb6SAndy Yan 	} while (rcvr);
215c1cd6cb6SAndy Yan 
216c1cd6cb6SAndy Yan 	writel(0xFFFFFFFF, &regs->iclr);
217c1cd6cb6SAndy Yan 
218c1cd6cb6SAndy Yan 	debug("sfc reset\n");
219c1cd6cb6SAndy Yan 
220c1cd6cb6SAndy Yan 	return ret;
221c1cd6cb6SAndy Yan }
222c1cd6cb6SAndy Yan 
2237ddc1c35SJon Lin static int rockchip_sfc_dma_xfer_wait_finished(struct rockchip_sfc *sfc)
2247ddc1c35SJon Lin {
2257ddc1c35SJon Lin 	struct rockchip_sfc_reg *regs = sfc->regbase;
2267ddc1c35SJon Lin 	int timeout = sfc->last_prepare_size * 10;
2277ddc1c35SJon Lin 	unsigned long tbase;
2287ddc1c35SJon Lin 	int ret = 0;
2297ddc1c35SJon Lin 	int risr;
2307ddc1c35SJon Lin 
2317ddc1c35SJon Lin 	tbase = get_timer(0);
2327ddc1c35SJon Lin 	do {
2337ddc1c35SJon Lin 		udelay(1);
2347ddc1c35SJon Lin 		risr = readl(&regs->risr);
2357ddc1c35SJon Lin 		if (get_timer(tbase) > timeout) {
2367ddc1c35SJon Lin 			debug("dma timeout\n");
2377ddc1c35SJon Lin 			ret = -ETIMEDOUT;
2387ddc1c35SJon Lin 			break;
2397ddc1c35SJon Lin 		}
2407ddc1c35SJon Lin 	} while (!(risr & TRANS_FINISH_INT));
2417ddc1c35SJon Lin 	sfc->last_prepare_size = 0;
2427ddc1c35SJon Lin 
2437ddc1c35SJon Lin 	return ret;
2447ddc1c35SJon Lin }
2457ddc1c35SJon Lin 
246915fcf0cSAndy Yan /* The SFC_CTRL register is a global control register,
247915fcf0cSAndy Yan  * when the controller is in busy state(SFC_SR),
248915fcf0cSAndy Yan  * SFC_CTRL cannot be set.
249915fcf0cSAndy Yan  */
250915fcf0cSAndy Yan static int rockchip_sfc_wait_idle(struct rockchip_sfc *sfc,
251915fcf0cSAndy Yan 				  u32 timeout_ms)
252915fcf0cSAndy Yan {
253915fcf0cSAndy Yan 	struct rockchip_sfc_reg *regs = sfc->regbase;
254915fcf0cSAndy Yan 	unsigned long tbase = get_timer(0);
255915fcf0cSAndy Yan 	u32 sr, fsr;
256915fcf0cSAndy Yan 
2577ddc1c35SJon Lin 	if (sfc->last_prepare_size && rockchip_sfc_dma_xfer_wait_finished(sfc))
2587ddc1c35SJon Lin 		return -ETIMEDOUT;
2597ddc1c35SJon Lin 
260915fcf0cSAndy Yan 	while (1) {
261915fcf0cSAndy Yan 		sr = readl(&regs->sr);
262915fcf0cSAndy Yan 		fsr = readl(&regs->fsr);
26339b850deSJon Lin 		if ((fsr & SFC_TX_EMPTY) &&
26439b850deSJon Lin 		    (fsr & SFC_RX_EMPTY) &&
26539b850deSJon Lin 		    !(sr & SFC_BUSY))
266915fcf0cSAndy Yan 			break;
267915fcf0cSAndy Yan 		if (get_timer(tbase) > timeout_ms) {
268915fcf0cSAndy Yan 			printf("waite sfc idle timeout(sr:0x%08x fsr:0x%08x)\n",
269915fcf0cSAndy Yan 			       sr, fsr);
270915fcf0cSAndy Yan 			rockchip_sfc_reset(sfc);
271915fcf0cSAndy Yan 			return -ETIMEDOUT;
272915fcf0cSAndy Yan 		}
273915fcf0cSAndy Yan 		udelay(100);
274915fcf0cSAndy Yan 	}
275915fcf0cSAndy Yan 
276915fcf0cSAndy Yan 	return 0;
277915fcf0cSAndy Yan }
278915fcf0cSAndy Yan 
279c1cd6cb6SAndy Yan static u8 rockchip_sfc_get_if_type(struct rockchip_sfc *sfc)
280c1cd6cb6SAndy Yan {
281c1cd6cb6SAndy Yan 	int type = IF_TYPE_STD;
282c1cd6cb6SAndy Yan 
283da4954b7SAndy Yan 	if (sfc->rw == SFC_WR) {
284c1cd6cb6SAndy Yan 		if (sfc->mode & SPI_TX_QUAD)
285c1cd6cb6SAndy Yan 			type = IF_TYPE_QUAD;
286c1cd6cb6SAndy Yan 		else if (sfc->mode & SPI_TX_DUAL)
287c1cd6cb6SAndy Yan 			type = IF_TYPE_DUAL;
288c1cd6cb6SAndy Yan 		else
289c1cd6cb6SAndy Yan 			type = IF_TYPE_STD;
290c1cd6cb6SAndy Yan 	} else {
291c1cd6cb6SAndy Yan 		if (sfc->mode & SPI_RX_QUAD)
292c1cd6cb6SAndy Yan 			type = IF_TYPE_QUAD;
293c1cd6cb6SAndy Yan 		else if (sfc->mode & SPI_RX_DUAL)
294c1cd6cb6SAndy Yan 			type = IF_TYPE_DUAL;
295c1cd6cb6SAndy Yan 		else
296c1cd6cb6SAndy Yan 			type = IF_TYPE_STD;
297c1cd6cb6SAndy Yan 	}
298c1cd6cb6SAndy Yan 
299c1cd6cb6SAndy Yan 	return type;
300c1cd6cb6SAndy Yan }
301c1cd6cb6SAndy Yan 
302da4954b7SAndy Yan static void rockchip_sfc_setup_xfer(struct rockchip_sfc *sfc, u32 trb)
303c1cd6cb6SAndy Yan {
304c1cd6cb6SAndy Yan 	struct rockchip_sfc_reg *regs = sfc->regbase;
305fa413375SJon Lin 	u32 val;
306c1cd6cb6SAndy Yan 	u8 data_width = IF_TYPE_STD;
307c1cd6cb6SAndy Yan 
308fa413375SJon Lin 	rockchip_sfc_wait_idle(sfc, 10);
30939b850deSJon Lin 
31039b850deSJon Lin 	if (sfc->addr_bits == SFC_ADDR_24BITS ||
31139b850deSJon Lin 	    sfc->addr_bits == SFC_ADDR_32BITS)
312c1cd6cb6SAndy Yan 		data_width = rockchip_sfc_get_if_type(sfc);
313c1cd6cb6SAndy Yan 
314fa413375SJon Lin 	SFC_DBG("--- sfc.addr_bit %x\n", sfc->addr_bits);
315113ced8fSJon Lin 	if (sfc->addr_bits == SFC_ADDR_XBITS)
31688ea3acbSJon Lin 		writel(sfc->addr_xbits_ext - 1, &regs->abit);
31788ea3acbSJon Lin 
318fa413375SJon Lin 	if (rockchip_sfc_get_version(sfc) >= SFC_VER_4) {
319fa413375SJon Lin 		SFC_DBG("--- sfc.len_ext %x\n", trb);
320fa413375SJon Lin 		writel(trb, &regs->len_ext);
321fa413375SJon Lin 	}
322c1cd6cb6SAndy Yan 
323fa413375SJon Lin 	val = 0x02;
324fa413375SJon Lin 	val |= (data_width << SFC_DATA_WIDTH_SHIFT);
325915fcf0cSAndy Yan 
32639b850deSJon Lin 	SFC_DBG("--- sfc.ctrl %x\n", val);
327c1cd6cb6SAndy Yan 	writel(val, &regs->ctrl);
328da4954b7SAndy Yan 
329da4954b7SAndy Yan 	val = sfc->cmd;
330fa413375SJon Lin 	val |= (trb & 0x3fff) << SFC_TRB_SHIFT;
331da4954b7SAndy Yan 	val |= sfc->rw << SFC_RW_SHIFT;
332da4954b7SAndy Yan 	val |= sfc->addr_bits << SFC_ADDR_BITS_SHIFT;
333da4954b7SAndy Yan 	val |= sfc->dummy_bits << SFC_DUMMY_BITS_SHIFT;
334da4954b7SAndy Yan 
33539b850deSJon Lin 	SFC_DBG("--- sfc.cmd %x\n", val);
336da4954b7SAndy Yan 	writel(val, &regs->cmd);
337da4954b7SAndy Yan 
33839b850deSJon Lin 	if (sfc->addr_bits & SFC_ADDR_XBITS) {
33939b850deSJon Lin 		SFC_DBG("--- sfc.addr %x\n", sfc->addr);
340c1cd6cb6SAndy Yan 		writel(sfc->addr, &regs->addr);
341c1cd6cb6SAndy Yan 	}
34239b850deSJon Lin }
343c1cd6cb6SAndy Yan 
344fa413375SJon Lin static int rockchip_sfc_dma_xfer(struct rockchip_sfc *sfc, void *buffer,
345fa413375SJon Lin 				 size_t trb)
346c1cd6cb6SAndy Yan {
347c1cd6cb6SAndy Yan 	struct rockchip_sfc_reg *regs = sfc->regbase;
34837911cf6SAndy Yan 	struct bounce_buffer bb;
34937911cf6SAndy Yan 	unsigned int bb_flags;
350fa413375SJon Lin 	int timeout = trb * 1000;
351c1cd6cb6SAndy Yan 	int ret = 0;
352c1cd6cb6SAndy Yan 	int risr;
353c1cd6cb6SAndy Yan 	unsigned long tbase;
354c1cd6cb6SAndy Yan 
35537911cf6SAndy Yan 	if (sfc->rw == SFC_WR)
35637911cf6SAndy Yan 		bb_flags = GEN_BB_READ;
35737911cf6SAndy Yan 	else
35837911cf6SAndy Yan 		bb_flags = GEN_BB_WRITE;
35937911cf6SAndy Yan 
36030f161d1SAndy Yan 	ret = bounce_buffer_start(&bb, buffer, trb, bb_flags);
36137911cf6SAndy Yan 	if (ret)
36237911cf6SAndy Yan 		return ret;
36330f161d1SAndy Yan 
36437911cf6SAndy Yan 	rockchip_sfc_setup_xfer(sfc, bb.len_aligned);
365c1cd6cb6SAndy Yan 
366c1cd6cb6SAndy Yan 	writel(0xFFFFFFFF, &regs->iclr);
36714b86dc9SJon Lin 	writel((unsigned long)bb.bounce_buffer, &regs->dmaaddr);
368c1cd6cb6SAndy Yan 	writel(SFC_DMA_START, &regs->dmatr);
369c1cd6cb6SAndy Yan 
370c1cd6cb6SAndy Yan 	tbase = get_timer(0);
371c1cd6cb6SAndy Yan 	do {
372c1cd6cb6SAndy Yan 		udelay(1);
373c1cd6cb6SAndy Yan 		risr = readl(&regs->risr);
374c1cd6cb6SAndy Yan 		if (get_timer(tbase) > timeout) {
375c1cd6cb6SAndy Yan 			debug("dma timeout\n");
376c1cd6cb6SAndy Yan 			ret = -ETIMEDOUT;
377c1cd6cb6SAndy Yan 			break;
378c1cd6cb6SAndy Yan 		}
379c1cd6cb6SAndy Yan 	} while (!(risr & TRANS_FINISH_INT));
380c1cd6cb6SAndy Yan 
381c1cd6cb6SAndy Yan 	writel(0xFFFFFFFF, &regs->iclr);
382c1cd6cb6SAndy Yan 
38337911cf6SAndy Yan 	bounce_buffer_stop(&bb);
38437911cf6SAndy Yan 
385c1cd6cb6SAndy Yan 	return ret;
386c1cd6cb6SAndy Yan }
387c1cd6cb6SAndy Yan 
3887ddc1c35SJon Lin static int rockchip_sfc_dma_xfer_prepare(struct rockchip_sfc *sfc,
3897ddc1c35SJon Lin 					 void *buffer, size_t trb)
3907ddc1c35SJon Lin {
3917ddc1c35SJon Lin 	struct rockchip_sfc_reg *regs = sfc->regbase;
3927ddc1c35SJon Lin 
3937ddc1c35SJon Lin 	SFC_DBG("sfc_dma_xfer_prepar enter\n");
3947ddc1c35SJon Lin 
3957ddc1c35SJon Lin 	rockchip_sfc_setup_xfer(sfc, trb);
3967ddc1c35SJon Lin 	sfc->last_prepare_size = trb;
3977ddc1c35SJon Lin 
3987ddc1c35SJon Lin 	flush_dcache_range((unsigned long)buffer,
3997ddc1c35SJon Lin 			   (unsigned long)buffer + trb);
4007ddc1c35SJon Lin 
4017ddc1c35SJon Lin 	writel(0xFFFFFFFF, &regs->iclr);
4027ddc1c35SJon Lin 	writel((unsigned long)buffer, &regs->dmaaddr);
4037ddc1c35SJon Lin 	writel(SFC_DMA_START, &regs->dmatr);
4047ddc1c35SJon Lin 
4057ddc1c35SJon Lin 	return 0;
4067ddc1c35SJon Lin }
4077ddc1c35SJon Lin 
408da4954b7SAndy Yan static int rockchip_sfc_wait_fifo_ready(struct rockchip_sfc *sfc, int rw,
409c1cd6cb6SAndy Yan 					u32 timeout)
410c1cd6cb6SAndy Yan {
411c1cd6cb6SAndy Yan 	struct rockchip_sfc_reg *regs = sfc->regbase;
412c1cd6cb6SAndy Yan 	unsigned long tbase = get_timer(0);
413c1cd6cb6SAndy Yan 	u8 level;
414c1cd6cb6SAndy Yan 	u32 fsr;
415c1cd6cb6SAndy Yan 
416c1cd6cb6SAndy Yan 	do {
417c1cd6cb6SAndy Yan 		fsr = readl(&regs->fsr);
418da4954b7SAndy Yan 		if (rw == SFC_WR)
419c1cd6cb6SAndy Yan 			level = (fsr & SFC_TXLV_MASK) >> SFC_TXLV_SHIFT;
420c1cd6cb6SAndy Yan 		else
421c1cd6cb6SAndy Yan 			level = (fsr & SFC_RXLV_MASK) >> SFC_RXLV_SHIFT;
422c1cd6cb6SAndy Yan 		if (get_timer(tbase) > timeout)
423c1cd6cb6SAndy Yan 			return -ETIMEDOUT;
424c1cd6cb6SAndy Yan 		udelay(1);
425c1cd6cb6SAndy Yan 	} while (!level);
426c1cd6cb6SAndy Yan 
427c1cd6cb6SAndy Yan 	return level;
428c1cd6cb6SAndy Yan }
429c1cd6cb6SAndy Yan 
430da4954b7SAndy Yan static int rockchip_sfc_write_fifo(struct rockchip_sfc *sfc, u32 *buf, u32 len)
431c1cd6cb6SAndy Yan {
432c1cd6cb6SAndy Yan 	struct rockchip_sfc_reg *regs = sfc->regbase;
433c1cd6cb6SAndy Yan 	u32 bytes = len & 0x3;
434c1cd6cb6SAndy Yan 	u32 words = len >> 2;
435915fcf0cSAndy Yan 	int tx_level = 0;
436c1cd6cb6SAndy Yan 	u32 val = 0;
437c1cd6cb6SAndy Yan 	u8 count;
438c1cd6cb6SAndy Yan 
439c1cd6cb6SAndy Yan 	while (words) {
440da4954b7SAndy Yan 		tx_level = rockchip_sfc_wait_fifo_ready(sfc, SFC_WR, 1000);
441c1cd6cb6SAndy Yan 		if (tx_level <= 0)
442c1cd6cb6SAndy Yan 			return tx_level;
443915fcf0cSAndy Yan 		count = min(words, (u32)tx_level);
444c1cd6cb6SAndy Yan 		writesl(&regs->data, buf, count);
445c1cd6cb6SAndy Yan 		buf += count;
446c1cd6cb6SAndy Yan 		words -= count;
447c1cd6cb6SAndy Yan 	}
448c1cd6cb6SAndy Yan 
449da4954b7SAndy Yan 	/* handle the last non 4byte aligned bytes */
450c1cd6cb6SAndy Yan 	if (bytes) {
451da4954b7SAndy Yan 		tx_level = rockchip_sfc_wait_fifo_ready(sfc, SFC_WR, 1000);
452c1cd6cb6SAndy Yan 		if (tx_level <= 0)
453c1cd6cb6SAndy Yan 			return tx_level;
454c1cd6cb6SAndy Yan 		memcpy(&val, buf, bytes);
455c1cd6cb6SAndy Yan 		writel(val, &regs->data);
456c1cd6cb6SAndy Yan 	}
457c1cd6cb6SAndy Yan 
458c1cd6cb6SAndy Yan 	return 0;
459c1cd6cb6SAndy Yan }
460c1cd6cb6SAndy Yan 
461da4954b7SAndy Yan static int rockchip_sfc_read_fifo(struct rockchip_sfc *sfc, u32 *buf, u32 len)
462c1cd6cb6SAndy Yan {
463c1cd6cb6SAndy Yan 	struct rockchip_sfc_reg *regs = sfc->regbase;
464c1cd6cb6SAndy Yan 	u32 bytes = len & 0x3;
465c1cd6cb6SAndy Yan 	u32 words = len >> 2;
466915fcf0cSAndy Yan 	int rx_level = 0;
467c1cd6cb6SAndy Yan 	u32 count;
468c1cd6cb6SAndy Yan 	u32 val;
469c1cd6cb6SAndy Yan 
470c1cd6cb6SAndy Yan 	while (words) {
471da4954b7SAndy Yan 		rx_level = rockchip_sfc_wait_fifo_ready(sfc, SFC_RD, 1000);
472c1cd6cb6SAndy Yan 		if (rx_level <= 0)
473c1cd6cb6SAndy Yan 			return rx_level;
474915fcf0cSAndy Yan 		count = min(words, (u32)rx_level);
475c1cd6cb6SAndy Yan 		readsl(&regs->data, buf, count);
476c1cd6cb6SAndy Yan 		buf += count;
477c1cd6cb6SAndy Yan 		words -= count;
478c1cd6cb6SAndy Yan 	}
479c1cd6cb6SAndy Yan 
480da4954b7SAndy Yan 	/* handle the last non 4 bytes aligned bytes */
481c1cd6cb6SAndy Yan 	if (bytes) {
482da4954b7SAndy Yan 		rx_level = rockchip_sfc_wait_fifo_ready(sfc, SFC_RD, 1000);
483c1cd6cb6SAndy Yan 		if (rx_level <= 0)
484c1cd6cb6SAndy Yan 			return rx_level;
485c1cd6cb6SAndy Yan 		val = readl(&regs->data);
486c1cd6cb6SAndy Yan 		memcpy(buf, &val, bytes);
487c1cd6cb6SAndy Yan 	}
488c1cd6cb6SAndy Yan 
489c1cd6cb6SAndy Yan 	return 0;
490c1cd6cb6SAndy Yan }
491c1cd6cb6SAndy Yan 
49230f161d1SAndy Yan static int rockchip_sfc_pio_xfer(struct rockchip_sfc *sfc, void *buf, u32 len)
493c1cd6cb6SAndy Yan {
494c1cd6cb6SAndy Yan 	int ret = 0;
495c1cd6cb6SAndy Yan 
496da4954b7SAndy Yan 	rockchip_sfc_setup_xfer(sfc, len);
49730f161d1SAndy Yan 
498c1cd6cb6SAndy Yan 	if (len) {
499da4954b7SAndy Yan 		if (sfc->rw == SFC_WR)
50030f161d1SAndy Yan 			ret = rockchip_sfc_write_fifo(sfc, (u32 *)buf, len);
501c1cd6cb6SAndy Yan 		else
50230f161d1SAndy Yan 			ret = rockchip_sfc_read_fifo(sfc, (u32 *)buf, len);
503c1cd6cb6SAndy Yan 	}
504c1cd6cb6SAndy Yan 
505c1cd6cb6SAndy Yan 	return ret;
506c1cd6cb6SAndy Yan }
507c1cd6cb6SAndy Yan 
50830f161d1SAndy Yan static int rockchip_sfc_read(struct rockchip_sfc *sfc, u32 offset,
50930f161d1SAndy Yan 			     void *buf, size_t len)
510c1cd6cb6SAndy Yan {
5116bf64646SAndy Yan 	u32 dma_trans;
51230f161d1SAndy Yan 	u32 trb;
51330f161d1SAndy Yan 	u8 bytes;
51430f161d1SAndy Yan 	int ret;
515c1cd6cb6SAndy Yan 
51637911cf6SAndy Yan 	if (len >= ARCH_DMA_MINALIGN) {
51730f161d1SAndy Yan 		bytes = len & (ARCH_DMA_MINALIGN - 1);
5186bf64646SAndy Yan 		dma_trans = len - bytes;
5196bf64646SAndy Yan 	} else {
5206bf64646SAndy Yan 		dma_trans = 0;
5216bf64646SAndy Yan 		bytes = len;
5226bf64646SAndy Yan 	}
5236bf64646SAndy Yan 
52430f161d1SAndy Yan 	while (dma_trans) {
525fa413375SJon Lin 		trb = min_t(size_t, dma_trans, sfc->max_iosize);
5267ddc1c35SJon Lin 		if (sfc->prepare)
5277ddc1c35SJon Lin 			ret = rockchip_sfc_dma_xfer_prepare(sfc, buf, len);
5287ddc1c35SJon Lin 		else
52930f161d1SAndy Yan 			ret = rockchip_sfc_dma_xfer(sfc, buf, trb);
53030f161d1SAndy Yan 		if (ret < 0)
53130f161d1SAndy Yan 			return ret;
53230f161d1SAndy Yan 		dma_trans -= trb;
53330f161d1SAndy Yan 		sfc->addr += trb;
53430f161d1SAndy Yan 		buf += trb;
5356bf64646SAndy Yan 	}
5366bf64646SAndy Yan 
5376bf64646SAndy Yan 	/*
53830f161d1SAndy Yan 	 * transfer the last non dma anligned byte by pio mode
5396bf64646SAndy Yan 	 */
54030f161d1SAndy Yan 	if (bytes)
5416bf64646SAndy Yan 		ret = rockchip_sfc_pio_xfer(sfc, buf, bytes);
542c1cd6cb6SAndy Yan 
54330f161d1SAndy Yan 	return 0;
54430f161d1SAndy Yan }
54530f161d1SAndy Yan 
54630f161d1SAndy Yan static int rockchip_sfc_write(struct rockchip_sfc *sfc, u32 offset,
54730f161d1SAndy Yan 			      void *buf, size_t len)
54830f161d1SAndy Yan {
549fa413375SJon Lin 	if (len > sfc->max_iosize) {
55030f161d1SAndy Yan 		printf("out of the max sfc trb");
55130f161d1SAndy Yan 		return -EINVAL;
55230f161d1SAndy Yan 	}
55330f161d1SAndy Yan 
55430f161d1SAndy Yan 	if (len && !(len & (ARCH_DMA_MINALIGN - 1)))
55530f161d1SAndy Yan 		return rockchip_sfc_dma_xfer(sfc, buf, len);
55630f161d1SAndy Yan 	else
55730f161d1SAndy Yan 		return rockchip_sfc_pio_xfer(sfc, buf, len);
55830f161d1SAndy Yan 
55930f161d1SAndy Yan 	return 0;
56030f161d1SAndy Yan }
56130f161d1SAndy Yan 
56230f161d1SAndy Yan static int rockchip_sfc_do_xfer(struct rockchip_sfc *sfc, void *buf, size_t len)
56330f161d1SAndy Yan {
56430f161d1SAndy Yan 	if (sfc->rw)
56530f161d1SAndy Yan 		return rockchip_sfc_write(sfc, sfc->addr, buf, len);
56630f161d1SAndy Yan 	else
56730f161d1SAndy Yan 		return rockchip_sfc_read(sfc, sfc->addr, buf, len);
568c1cd6cb6SAndy Yan }
569c1cd6cb6SAndy Yan 
570c1cd6cb6SAndy Yan static int rockchip_sfc_xfer(struct udevice *dev, unsigned int bitlen,
571c1cd6cb6SAndy Yan 			     const void *dout, void *din, unsigned long flags)
572c1cd6cb6SAndy Yan {
573c1cd6cb6SAndy Yan 	struct udevice *bus = dev->parent;
574c1cd6cb6SAndy Yan 	struct rockchip_sfc *sfc = dev_get_priv(bus);
575c1cd6cb6SAndy Yan 	int len = bitlen >> 3;
576c1cd6cb6SAndy Yan 	u8 *pcmd = (u8 *)dout;
57730f161d1SAndy Yan 	void *data_buf;
578c1cd6cb6SAndy Yan 	int ret = 0;
579c1cd6cb6SAndy Yan 
580c1cd6cb6SAndy Yan 	if (flags & SPI_XFER_BEGIN) {
581c1cd6cb6SAndy Yan 		sfc->cmd = pcmd[0];
5826e121371SJon Lin 		switch (len) {
5836e121371SJon Lin 		case 6: /* Nor >16MB 0x6b dummy op */
5846e121371SJon Lin 			sfc->addr_bits = SFC_ADDR_32BITS;
5856e121371SJon Lin 			sfc->dummy_bits = 8;
5866e121371SJon Lin 			sfc->addr = pcmd[4] | (pcmd[3] << 8) | (pcmd[2] << 16) | (pcmd[1] << 24);
5876e121371SJon Lin 			break;
5886e121371SJon Lin 		case 5: /* Nor <=16MB 0x6b dummy op, Nor >16MB no dummy op */
58998730755SJon Lin 			if (sfc->cmd == 0x6b) {
59098730755SJon Lin 				sfc->addr_bits = SFC_ADDR_24BITS;
59198730755SJon Lin 				sfc->dummy_bits = 8;
59298730755SJon Lin 				sfc->addr = pcmd[3] | (pcmd[2] << 8) | (pcmd[1] << 16);
59398730755SJon Lin 			} else {
5946e121371SJon Lin 				sfc->addr_bits = SFC_ADDR_32BITS;
5956e121371SJon Lin 				sfc->dummy_bits = 0;
5966e121371SJon Lin 				sfc->addr = pcmd[4] | (pcmd[3] << 8) | (pcmd[2] << 16) | (pcmd[1] << 24);
59798730755SJon Lin 			}
5986e121371SJon Lin 			break;
5996e121371SJon Lin 		case 4: /* Nand erase and read, Nor <=16MB no dummy op */
6006e121371SJon Lin 			sfc->addr_bits = SFC_ADDR_24BITS;
6016e121371SJon Lin 			sfc->dummy_bits = 0;
602c1cd6cb6SAndy Yan 			sfc->addr = pcmd[3] | (pcmd[2] << 8) | (pcmd[1] << 16);
6036e121371SJon Lin 			break;
6046e121371SJon Lin 		case 3: /* Nand prog,  */
6056e121371SJon Lin 			sfc->addr_bits = SFC_ADDR_XBITS;
60688ea3acbSJon Lin 			sfc->addr_xbits_ext = 16;
60739b850deSJon Lin 			sfc->dummy_bits = 0;
60839b850deSJon Lin 			sfc->addr = pcmd[2] | pcmd[1] << 8;
6096e121371SJon Lin 			break;
6106e121371SJon Lin 		case 2: /* Nand read/write feature */
6116e121371SJon Lin 			sfc->addr_bits = SFC_ADDR_XBITS;
61288ea3acbSJon Lin 			sfc->addr_xbits_ext = 8;
61339b850deSJon Lin 			sfc->dummy_bits = 0;
61439b850deSJon Lin 			sfc->addr = pcmd[1];
6156e121371SJon Lin 			break;
6166e121371SJon Lin 		default: /* Nand/Nor Read/Write status */
6176e121371SJon Lin 			sfc->addr_bits = SFC_ADDR_0BITS;
618da4954b7SAndy Yan 			sfc->dummy_bits = 0;
619da4954b7SAndy Yan 			sfc->addr = 0;
6206e121371SJon Lin 			break;
621c1cd6cb6SAndy Yan 		}
622fa413375SJon Lin 		SFC_DBG("%s %d %x %d %d %x\n", __func__, len, sfc->cmd,
623fa413375SJon Lin 			sfc->addr_bits, sfc->dummy_bits, sfc->addr);
624c1cd6cb6SAndy Yan 	}
625c1cd6cb6SAndy Yan 	if (flags & SPI_XFER_END) {
626da4954b7SAndy Yan 		if (din) {
627da4954b7SAndy Yan 			sfc->rw = SFC_RD;
62830f161d1SAndy Yan 			data_buf = din;
62930f161d1SAndy Yan 		} else {
630da4954b7SAndy Yan 			sfc->rw = SFC_WR;
63130f161d1SAndy Yan 			data_buf = (void *)dout;
632c1cd6cb6SAndy Yan 		}
63330f161d1SAndy Yan 
63430f161d1SAndy Yan 		if (flags == (SPI_XFER_BEGIN | SPI_XFER_END)) {
63530f161d1SAndy Yan 			len = 0;
63630f161d1SAndy Yan 			data_buf = NULL;
63730f161d1SAndy Yan 		}
63830f161d1SAndy Yan 
6397ddc1c35SJon Lin 		sfc->prepare = flags & SPI_XFER_PREPARE ? true : false;
6407ddc1c35SJon Lin 
641cbd9216dSJon Lin 		if (sfc->cmd == 0x9f && len == 4) {
642cbd9216dSJon Lin 			/* SPI Nand read id */
643cbd9216dSJon Lin 			sfc->addr_bits = SFC_ADDR_XBITS;
644cbd9216dSJon Lin 			sfc->addr_xbits_ext = 8;
645cbd9216dSJon Lin 			sfc->dummy_bits = 0;
646cbd9216dSJon Lin 			sfc->addr = 0;
647cbd9216dSJon Lin 			((u8 *)data_buf)[0] = 0xff;
648cbd9216dSJon Lin 			ret = rockchip_sfc_do_xfer(sfc, &((u8 *)data_buf)[1], 3);
649cbd9216dSJon Lin 		} else {
65030f161d1SAndy Yan 			ret = rockchip_sfc_do_xfer(sfc, data_buf, len);
651da4954b7SAndy Yan 		}
652cbd9216dSJon Lin 	}
653c1cd6cb6SAndy Yan 
654c1cd6cb6SAndy Yan 	return ret;
655c1cd6cb6SAndy Yan }
656c1cd6cb6SAndy Yan 
657c1cd6cb6SAndy Yan static int rockchip_sfc_set_speed(struct udevice *bus, uint speed)
658c1cd6cb6SAndy Yan {
659c1cd6cb6SAndy Yan 	struct rockchip_sfc *sfc = dev_get_priv(bus);
660c1cd6cb6SAndy Yan 
661c1cd6cb6SAndy Yan 	if (speed > sfc->max_freq)
662c1cd6cb6SAndy Yan 		speed = sfc->max_freq;
663c1cd6cb6SAndy Yan 
664c1cd6cb6SAndy Yan 	sfc->speed_hz = speed;
665*5b4dcfe0SJason Zhu #if CONFIG_IS_ENABLED(CLK)
666ae52cbcbSJon Lin 	clk_set_rate(&sfc->clk, sfc->speed_hz);
667ae52cbcbSJon Lin 	SFC_DBG("%s clk= %ld\n", __func__, clk_get_rate(&sfc->clk));
668*5b4dcfe0SJason Zhu #endif
669c1cd6cb6SAndy Yan 	return 0;
670c1cd6cb6SAndy Yan }
671c1cd6cb6SAndy Yan 
672c1cd6cb6SAndy Yan static int rockchip_sfc_set_mode(struct udevice *bus, uint mode)
673c1cd6cb6SAndy Yan {
674c1cd6cb6SAndy Yan 	struct rockchip_sfc *sfc = dev_get_priv(bus);
675c1cd6cb6SAndy Yan 
676c1cd6cb6SAndy Yan 	sfc->mode = mode;
677c1cd6cb6SAndy Yan 
678c1cd6cb6SAndy Yan 	return 0;
679c1cd6cb6SAndy Yan }
680c1cd6cb6SAndy Yan 
681c1cd6cb6SAndy Yan static const struct dm_spi_ops rockchip_sfc_ops = {
682c1cd6cb6SAndy Yan 	.xfer		= rockchip_sfc_xfer,
683c1cd6cb6SAndy Yan 	.set_speed	= rockchip_sfc_set_speed,
684c1cd6cb6SAndy Yan 	.set_mode	= rockchip_sfc_set_mode,
685c1cd6cb6SAndy Yan };
686c1cd6cb6SAndy Yan 
687c1cd6cb6SAndy Yan static const struct udevice_id rockchip_sfc_ids[] = {
688c1cd6cb6SAndy Yan 	{ .compatible = "rockchip,sfc" },
689c1cd6cb6SAndy Yan 	{ }
690c1cd6cb6SAndy Yan };
691c1cd6cb6SAndy Yan 
692c1cd6cb6SAndy Yan U_BOOT_DRIVER(rockchip_sfc_driver) = {
693c1cd6cb6SAndy Yan 	.name	= "rockchip_sfc",
694c1cd6cb6SAndy Yan 	.id	= UCLASS_SPI,
695c1cd6cb6SAndy Yan 	.of_match = rockchip_sfc_ids,
696c1cd6cb6SAndy Yan 	.ops	= &rockchip_sfc_ops,
697c1cd6cb6SAndy Yan 	.ofdata_to_platdata = rockchip_sfc_ofdata_to_platdata,
698c1cd6cb6SAndy Yan 	.platdata_auto_alloc_size = sizeof(struct rockchip_sfc_platdata),
699c1cd6cb6SAndy Yan 	.priv_auto_alloc_size = sizeof(struct rockchip_sfc),
700c1cd6cb6SAndy Yan 	.probe	= rockchip_sfc_probe,
701c1cd6cb6SAndy Yan };
702