xref: /rk3399_rockchip-uboot/drivers/rkflash/sfc.c (revision 49eba1a38af4a5e597bddd957f12dae2a3d6b9f9)
1ad309a88SDingqiang Lin /*
2ad309a88SDingqiang Lin  * Copyright (c) 2018 Fuzhou Rockchip Electronics Co., Ltd
3ad309a88SDingqiang Lin  *
4ba0501acSDingqiang Lin  * SPDX-License-Identifier:	GPL-2.0
5ad309a88SDingqiang Lin  */
6ad309a88SDingqiang Lin 
7ad309a88SDingqiang Lin #include <common.h>
8ad309a88SDingqiang Lin #include <linux/delay.h>
9ad309a88SDingqiang Lin #include <bouncebuf.h>
10ad309a88SDingqiang Lin #include <asm/io.h>
11ad309a88SDingqiang Lin 
12ad309a88SDingqiang Lin #include "sfc.h"
13ad309a88SDingqiang Lin 
14534d4d2fSJon Lin #define SFC_MAX_IOSIZE_VER3		(1024 * 8)
15534d4d2fSJon Lin #define SFC_MAX_IOSIZE_VER4		(0xFFFFFFFF)
16534d4d2fSJon Lin 
17ad309a88SDingqiang Lin static void __iomem *g_sfc_reg;
18ad309a88SDingqiang Lin 
sfc_reset(void)19ad309a88SDingqiang Lin static void sfc_reset(void)
20ad309a88SDingqiang Lin {
21ad309a88SDingqiang Lin 	int timeout = 10000;
22ad309a88SDingqiang Lin 
23ad309a88SDingqiang Lin 	writel(SFC_RESET, g_sfc_reg + SFC_RCVR);
24*49eba1a3SJon Lin 
25ad309a88SDingqiang Lin 	while ((readl(g_sfc_reg + SFC_RCVR) == SFC_RESET) && (timeout > 0)) {
26ad309a88SDingqiang Lin 		sfc_delay(1);
27ad309a88SDingqiang Lin 		timeout--;
28ad309a88SDingqiang Lin 	}
29*49eba1a3SJon Lin 
30ad309a88SDingqiang Lin 	writel(0xFFFFFFFF, g_sfc_reg + SFC_ICLR);
31ad309a88SDingqiang Lin }
32ad309a88SDingqiang Lin 
sfc_get_version(void)33ad309a88SDingqiang Lin u16 sfc_get_version(void)
34ad309a88SDingqiang Lin {
35ad309a88SDingqiang Lin 	return  (u32)(readl(g_sfc_reg + SFC_VER) & 0xffff);
36ad309a88SDingqiang Lin }
37ad309a88SDingqiang Lin 
sfc_get_max_iosize(void)38534d4d2fSJon Lin u32 sfc_get_max_iosize(void)
39534d4d2fSJon Lin {
40534d4d2fSJon Lin 	if (sfc_get_version() >= SFC_VER_4)
41534d4d2fSJon Lin 		return SFC_MAX_IOSIZE_VER4;
42534d4d2fSJon Lin 	else
43534d4d2fSJon Lin 		return SFC_MAX_IOSIZE_VER3;
44534d4d2fSJon Lin }
45534d4d2fSJon Lin 
sfc_set_delay_lines(u16 cells)46*49eba1a3SJon Lin void sfc_set_delay_lines(u16 cells)
47*49eba1a3SJon Lin {
48*49eba1a3SJon Lin 	u16 cell_max = SCLK_SMP_SEL_MAX_V4;
49*49eba1a3SJon Lin 
50*49eba1a3SJon Lin 	if (sfc_get_version() >= SFC_VER_5)
51*49eba1a3SJon Lin 		cell_max = SCLK_SMP_SEL_MAX_V5;
52*49eba1a3SJon Lin 
53*49eba1a3SJon Lin 	if (cells > cell_max)
54*49eba1a3SJon Lin 		cells = cell_max;
55*49eba1a3SJon Lin 
56*49eba1a3SJon Lin 	writel(SCLK_SMP_SEL_EN | cells, g_sfc_reg + SFC_DLL_CTRL0);
57*49eba1a3SJon Lin }
58*49eba1a3SJon Lin 
sfc_disable_delay_lines(void)59*49eba1a3SJon Lin void sfc_disable_delay_lines(void)
60*49eba1a3SJon Lin {
61*49eba1a3SJon Lin 	writel(0, g_sfc_reg + SFC_DLL_CTRL0);
62*49eba1a3SJon Lin }
63*49eba1a3SJon Lin 
sfc_init(void __iomem * reg_addr)64ad309a88SDingqiang Lin int sfc_init(void __iomem *reg_addr)
65ad309a88SDingqiang Lin {
66ad309a88SDingqiang Lin 	g_sfc_reg = reg_addr;
67ad309a88SDingqiang Lin 	writel(0, g_sfc_reg + SFC_CTRL);
68*49eba1a3SJon Lin 
69534d4d2fSJon Lin 	if (sfc_get_version() >= SFC_VER_4)
70534d4d2fSJon Lin 		writel(1, g_sfc_reg + SFC_LEN_CTRL);
71ad309a88SDingqiang Lin 
72ad309a88SDingqiang Lin 	return SFC_OK;
73ad309a88SDingqiang Lin }
74ad309a88SDingqiang Lin 
sfc_clean_irq(void)75ad309a88SDingqiang Lin void sfc_clean_irq(void)
76ad309a88SDingqiang Lin {
77ad309a88SDingqiang Lin 	writel(0xFFFFFFFF, g_sfc_reg + SFC_ICLR);
78ad309a88SDingqiang Lin 	writel(0xFFFFFFFF, g_sfc_reg + SFC_IMR);
79ad309a88SDingqiang Lin }
80ad309a88SDingqiang Lin 
sfc_request(struct rk_sfc_op * op,u32 addr,void * data,u32 size)8158463f4dSJon Lin int sfc_request(struct rk_sfc_op *op, u32 addr, void *data, u32 size)
82ad309a88SDingqiang Lin {
83ad309a88SDingqiang Lin 	int ret = SFC_OK;
84ad309a88SDingqiang Lin 	union SFCCMD_DATA cmd;
85ad309a88SDingqiang Lin 	int reg;
86ad309a88SDingqiang Lin 	int timeout = 0;
87ad309a88SDingqiang Lin 
88ad309a88SDingqiang Lin 	reg = readl(g_sfc_reg + SFC_FSR);
89*49eba1a3SJon Lin 
90ad309a88SDingqiang Lin 	if (!(reg & SFC_TXEMPTY) || !(reg & SFC_RXEMPTY) ||
91ad309a88SDingqiang Lin 	    (readl(g_sfc_reg + SFC_SR) & SFC_BUSY))
92ad309a88SDingqiang Lin 		sfc_reset();
93ad309a88SDingqiang Lin 
9458463f4dSJon Lin 	cmd.d32 = op->sfcmd.d32;
95*49eba1a3SJon Lin 
96ad309a88SDingqiang Lin 	if (cmd.b.addrbits == SFC_ADDR_XBITS) {
97ad309a88SDingqiang Lin 		union SFCCTRL_DATA ctrl;
98ad309a88SDingqiang Lin 
9958463f4dSJon Lin 		ctrl.d32 = op->sfctrl.d32;
100*49eba1a3SJon Lin 
101ad309a88SDingqiang Lin 		if (!ctrl.b.addrbits)
102ad309a88SDingqiang Lin 			return SFC_PARAM_ERR;
103*49eba1a3SJon Lin 
104ad309a88SDingqiang Lin 		/* Controller plus 1 automatically */
105ad309a88SDingqiang Lin 		writel(ctrl.b.addrbits - 1, g_sfc_reg + SFC_ABIT);
106ad309a88SDingqiang Lin 	}
107*49eba1a3SJon Lin 
108ad309a88SDingqiang Lin 	/* shift in the data at negedge sclk_out */
10958463f4dSJon Lin 	op->sfctrl.d32 |= 0x2;
11058463f4dSJon Lin 	cmd.b.datasize = size;
111*49eba1a3SJon Lin 
112534d4d2fSJon Lin 	if (sfc_get_version() >= SFC_VER_4)
113534d4d2fSJon Lin 		writel(size, g_sfc_reg + SFC_LEN_EXT);
114534d4d2fSJon Lin 	else
115534d4d2fSJon Lin 		cmd.b.datasize = size;
116ad309a88SDingqiang Lin 
11758463f4dSJon Lin 	writel(op->sfctrl.d32, g_sfc_reg + SFC_CTRL);
11858463f4dSJon Lin 	writel(cmd.d32, g_sfc_reg + SFC_CMD);
119*49eba1a3SJon Lin 
120ad309a88SDingqiang Lin 	if (cmd.b.addrbits)
121ad309a88SDingqiang Lin 		writel(addr, g_sfc_reg + SFC_ADDR);
122*49eba1a3SJon Lin 
12358463f4dSJon Lin 	if (!size)
124ad309a88SDingqiang Lin 		goto exit_wait;
125*49eba1a3SJon Lin 
12658463f4dSJon Lin 	if (op->sfctrl.b.enbledma) {
127ad309a88SDingqiang Lin 		struct bounce_buffer bb;
128ad309a88SDingqiang Lin 		unsigned int bb_flags;
129ad309a88SDingqiang Lin 
130ad309a88SDingqiang Lin 		if (cmd.b.rw == SFC_WRITE)
131ad309a88SDingqiang Lin 			bb_flags = GEN_BB_READ;
132ad309a88SDingqiang Lin 		else
133ad309a88SDingqiang Lin 			bb_flags = GEN_BB_WRITE;
134ad309a88SDingqiang Lin 
13558463f4dSJon Lin 		ret = bounce_buffer_start(&bb, data, size, bb_flags);
136ad309a88SDingqiang Lin 		if (ret)
137ad309a88SDingqiang Lin 			return ret;
138ad309a88SDingqiang Lin 
139ad309a88SDingqiang Lin 		writel(0xFFFFFFFF, g_sfc_reg + SFC_ICLR);
14058463f4dSJon Lin 		writel(~((u32)DMA_INT), g_sfc_reg + SFC_IMR);
1411bb49bc4SDingqiang Lin 		writel((unsigned long)bb.bounce_buffer, g_sfc_reg + SFC_DMA_ADDR);
142ad309a88SDingqiang Lin 		writel(SFC_DMA_START, g_sfc_reg + SFC_DMA_TRIGGER);
143ad309a88SDingqiang Lin 
14458463f4dSJon Lin 		timeout = size * 10;
145*49eba1a3SJon Lin 
146534d4d2fSJon Lin 		while ((readl(g_sfc_reg + SFC_SR) & SFC_BUSY) &&
147ad309a88SDingqiang Lin 		       (timeout-- > 0))
148ad309a88SDingqiang Lin 			sfc_delay(1);
149*49eba1a3SJon Lin 
150ad309a88SDingqiang Lin 		writel(0xFFFFFFFF, g_sfc_reg + SFC_ICLR);
151*49eba1a3SJon Lin 
152ad309a88SDingqiang Lin 		if (timeout <= 0)
153ad309a88SDingqiang Lin 			ret = SFC_WAIT_TIMEOUT;
154ad309a88SDingqiang Lin 		bounce_buffer_stop(&bb);
155ad309a88SDingqiang Lin 	} else {
156ad309a88SDingqiang Lin 		u32 i, words, count, bytes;
157ad309a88SDingqiang Lin 		union SFCFSR_DATA    fifostat;
158ad309a88SDingqiang Lin 		u32 *p_data = (u32 *)data;
159ad309a88SDingqiang Lin 
160ad309a88SDingqiang Lin 		if (cmd.b.rw == SFC_WRITE) {
16158463f4dSJon Lin 			words  = (size + 3) >> 2;
162*49eba1a3SJon Lin 
163ad309a88SDingqiang Lin 			while (words) {
164ad309a88SDingqiang Lin 				fifostat.d32 = readl(g_sfc_reg + SFC_FSR);
165*49eba1a3SJon Lin 
166ad309a88SDingqiang Lin 				if (fifostat.b.txlevel > 0) {
167ad309a88SDingqiang Lin 					count = words < fifostat.b.txlevel ?
168ad309a88SDingqiang Lin 						words : fifostat.b.txlevel;
169*49eba1a3SJon Lin 
170ad309a88SDingqiang Lin 					for (i = 0; i < count; i++) {
171ad309a88SDingqiang Lin 						writel(*p_data++,
172ad309a88SDingqiang Lin 						       g_sfc_reg + SFC_DATA);
173ad309a88SDingqiang Lin 						words--;
174ad309a88SDingqiang Lin 					}
175*49eba1a3SJon Lin 
176ad309a88SDingqiang Lin 					if (words == 0)
177ad309a88SDingqiang Lin 						break;
178*49eba1a3SJon Lin 
179ad309a88SDingqiang Lin 					timeout = 0;
180ad309a88SDingqiang Lin 				} else {
181ad309a88SDingqiang Lin 					sfc_delay(1);
182*49eba1a3SJon Lin 
183ad309a88SDingqiang Lin 					if (timeout++ > 10000) {
184ad309a88SDingqiang Lin 						ret = SFC_TX_TIMEOUT;
185ad309a88SDingqiang Lin 						break;
186ad309a88SDingqiang Lin 					}
187ad309a88SDingqiang Lin 				}
188ad309a88SDingqiang Lin 			}
189ad309a88SDingqiang Lin 		} else {
190ad309a88SDingqiang Lin 			/* SFC_READ == cmd.b.rw */
19158463f4dSJon Lin 			bytes = size & 0x3;
19258463f4dSJon Lin 			words = size >> 2;
193*49eba1a3SJon Lin 
194ad309a88SDingqiang Lin 			while (words) {
195ad309a88SDingqiang Lin 				fifostat.d32 = readl(g_sfc_reg + SFC_FSR);
196*49eba1a3SJon Lin 
197ad309a88SDingqiang Lin 				if (fifostat.b.rxlevel > 0) {
198ad309a88SDingqiang Lin 					u32 count;
199ad309a88SDingqiang Lin 
200ad309a88SDingqiang Lin 					count = words < fifostat.b.rxlevel ?
201ad309a88SDingqiang Lin 						words : fifostat.b.rxlevel;
202ad309a88SDingqiang Lin 
203ad309a88SDingqiang Lin 					for (i = 0; i < count; i++) {
204ad309a88SDingqiang Lin 						*p_data++ = readl(g_sfc_reg +
205ad309a88SDingqiang Lin 								  SFC_DATA);
206ad309a88SDingqiang Lin 						words--;
207ad309a88SDingqiang Lin 					}
208*49eba1a3SJon Lin 
209ad309a88SDingqiang Lin 					if (words == 0)
210ad309a88SDingqiang Lin 						break;
211*49eba1a3SJon Lin 
212ad309a88SDingqiang Lin 					timeout = 0;
213ad309a88SDingqiang Lin 				} else {
214ad309a88SDingqiang Lin 					sfc_delay(1);
215*49eba1a3SJon Lin 
216ad309a88SDingqiang Lin 					if (timeout++ > 10000) {
217ad309a88SDingqiang Lin 						ret = SFC_RX_TIMEOUT;
218ad309a88SDingqiang Lin 						break;
219ad309a88SDingqiang Lin 					}
220ad309a88SDingqiang Lin 				}
221ad309a88SDingqiang Lin 			}
222ad309a88SDingqiang Lin 
223ad309a88SDingqiang Lin 			timeout = 0;
224*49eba1a3SJon Lin 
225ad309a88SDingqiang Lin 			while (bytes) {
226ad309a88SDingqiang Lin 				fifostat.d32 = readl(g_sfc_reg + SFC_FSR);
227*49eba1a3SJon Lin 
228ad309a88SDingqiang Lin 				if (fifostat.b.rxlevel > 0) {
229ad309a88SDingqiang Lin 					u8 *p_data1 = (u8 *)p_data;
230ad309a88SDingqiang Lin 
231ad309a88SDingqiang Lin 					words = readl(g_sfc_reg + SFC_DATA);
232*49eba1a3SJon Lin 
233ad309a88SDingqiang Lin 					for (i = 0; i < bytes; i++)
234ad309a88SDingqiang Lin 						p_data1[i] =
235ad309a88SDingqiang Lin 							(u8)((words >> (i * 8)) & 0xFF);
236*49eba1a3SJon Lin 
237ad309a88SDingqiang Lin 					break;
238ad309a88SDingqiang Lin 				}
239ad309a88SDingqiang Lin 
240ad309a88SDingqiang Lin 				sfc_delay(1);
241*49eba1a3SJon Lin 
242ad309a88SDingqiang Lin 				if (timeout++ > 10000) {
243ad309a88SDingqiang Lin 					ret = SFC_RX_TIMEOUT;
244ad309a88SDingqiang Lin 					break;
245ad309a88SDingqiang Lin 				}
246ad309a88SDingqiang Lin 			}
247ad309a88SDingqiang Lin 		}
248ad309a88SDingqiang Lin 	}
249ad309a88SDingqiang Lin 
250ad309a88SDingqiang Lin exit_wait:
251ad309a88SDingqiang Lin 	timeout = 0;    /* wait cmd or data send complete */
252*49eba1a3SJon Lin 
25358463f4dSJon Lin 	while (readl(g_sfc_reg + SFC_SR) & SFC_BUSY) {
254ad309a88SDingqiang Lin 		sfc_delay(1);
255*49eba1a3SJon Lin 
256ad309a88SDingqiang Lin 		if (timeout++ > 100000) {         /* wait 100ms */
257ad309a88SDingqiang Lin 			ret = SFC_TX_TIMEOUT;
258ad309a88SDingqiang Lin 			break;
259ad309a88SDingqiang Lin 		}
260ad309a88SDingqiang Lin 	}
261*49eba1a3SJon Lin 
262ad309a88SDingqiang Lin 	sfc_delay(1); /* CS# High Time (read/write) >100ns */
263ad309a88SDingqiang Lin 	return ret;
264ad309a88SDingqiang Lin }
265