xref: /rk3399_rockchip-uboot/drivers/rkflash/sfc.c (revision 534d4d2fe4a5e261fa0525a60d163d753d1a4013)
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 
14*534d4d2fSJon Lin #define SFC_MAX_IOSIZE_VER3		(1024 * 8)
15*534d4d2fSJon Lin #define SFC_MAX_IOSIZE_VER4		(0xFFFFFFFF)
16*534d4d2fSJon Lin 
17ad309a88SDingqiang Lin static void __iomem *g_sfc_reg;
18ad309a88SDingqiang Lin 
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);
24ad309a88SDingqiang Lin 	while ((readl(g_sfc_reg + SFC_RCVR) == SFC_RESET) && (timeout > 0)) {
25ad309a88SDingqiang Lin 		sfc_delay(1);
26ad309a88SDingqiang Lin 		timeout--;
27ad309a88SDingqiang Lin 	}
28ad309a88SDingqiang Lin 	writel(0xFFFFFFFF, g_sfc_reg + SFC_ICLR);
29ad309a88SDingqiang Lin }
30ad309a88SDingqiang Lin 
31ad309a88SDingqiang Lin u16 sfc_get_version(void)
32ad309a88SDingqiang Lin {
33ad309a88SDingqiang Lin 	return  (u32)(readl(g_sfc_reg + SFC_VER) & 0xffff);
34ad309a88SDingqiang Lin }
35ad309a88SDingqiang Lin 
36*534d4d2fSJon Lin u32 sfc_get_max_iosize(void)
37*534d4d2fSJon Lin {
38*534d4d2fSJon Lin 	if (sfc_get_version() >= SFC_VER_4)
39*534d4d2fSJon Lin 		return SFC_MAX_IOSIZE_VER4;
40*534d4d2fSJon Lin 	else
41*534d4d2fSJon Lin 		return SFC_MAX_IOSIZE_VER3;
42*534d4d2fSJon Lin }
43*534d4d2fSJon Lin 
44ad309a88SDingqiang Lin int sfc_init(void __iomem *reg_addr)
45ad309a88SDingqiang Lin {
46ad309a88SDingqiang Lin 	g_sfc_reg = reg_addr;
47ad309a88SDingqiang Lin 	sfc_reset();
48ad309a88SDingqiang Lin 	writel(0, g_sfc_reg + SFC_CTRL);
49*534d4d2fSJon Lin 	if (sfc_get_version() >= SFC_VER_4)
50*534d4d2fSJon Lin 		writel(1, g_sfc_reg + SFC_LEN_CTRL);
51ad309a88SDingqiang Lin 
52ad309a88SDingqiang Lin 	return SFC_OK;
53ad309a88SDingqiang Lin }
54ad309a88SDingqiang Lin 
55ad309a88SDingqiang Lin void sfc_clean_irq(void)
56ad309a88SDingqiang Lin {
57ad309a88SDingqiang Lin 	writel(0xFFFFFFFF, g_sfc_reg + SFC_ICLR);
58ad309a88SDingqiang Lin 	writel(0xFFFFFFFF, g_sfc_reg + SFC_IMR);
59ad309a88SDingqiang Lin }
60ad309a88SDingqiang Lin 
6158463f4dSJon Lin int sfc_request(struct rk_sfc_op *op, u32 addr, void *data, u32 size)
62ad309a88SDingqiang Lin {
63ad309a88SDingqiang Lin 	int ret = SFC_OK;
64ad309a88SDingqiang Lin 	union SFCCMD_DATA cmd;
65ad309a88SDingqiang Lin 	int reg;
66ad309a88SDingqiang Lin 	int timeout = 0;
67ad309a88SDingqiang Lin 
68ad309a88SDingqiang Lin 	reg = readl(g_sfc_reg + SFC_FSR);
69ad309a88SDingqiang Lin 	if (!(reg & SFC_TXEMPTY) || !(reg & SFC_RXEMPTY) ||
70ad309a88SDingqiang Lin 	    (readl(g_sfc_reg + SFC_SR) & SFC_BUSY))
71ad309a88SDingqiang Lin 		sfc_reset();
72ad309a88SDingqiang Lin 
7358463f4dSJon Lin 	cmd.d32 = op->sfcmd.d32;
74ad309a88SDingqiang Lin 	if (cmd.b.addrbits == SFC_ADDR_XBITS) {
75ad309a88SDingqiang Lin 		union SFCCTRL_DATA ctrl;
76ad309a88SDingqiang Lin 
7758463f4dSJon Lin 		ctrl.d32 = op->sfctrl.d32;
78ad309a88SDingqiang Lin 		if (!ctrl.b.addrbits)
79ad309a88SDingqiang Lin 			return SFC_PARAM_ERR;
80ad309a88SDingqiang Lin 		/* Controller plus 1 automatically */
81ad309a88SDingqiang Lin 		writel(ctrl.b.addrbits - 1, g_sfc_reg + SFC_ABIT);
82ad309a88SDingqiang Lin 	}
83ad309a88SDingqiang Lin 	/* shift in the data at negedge sclk_out */
8458463f4dSJon Lin 	op->sfctrl.d32 |= 0x2;
8558463f4dSJon Lin 	cmd.b.datasize = size;
86*534d4d2fSJon Lin 	if (sfc_get_version() >= SFC_VER_4)
87*534d4d2fSJon Lin 		writel(size, g_sfc_reg + SFC_LEN_EXT);
88*534d4d2fSJon Lin 	else
89*534d4d2fSJon Lin 		cmd.b.datasize = size;
90ad309a88SDingqiang Lin 
9158463f4dSJon Lin 	writel(op->sfctrl.d32, g_sfc_reg + SFC_CTRL);
9258463f4dSJon Lin 	writel(cmd.d32, g_sfc_reg + SFC_CMD);
93ad309a88SDingqiang Lin 	if (cmd.b.addrbits)
94ad309a88SDingqiang Lin 		writel(addr, g_sfc_reg + SFC_ADDR);
9558463f4dSJon Lin 	if (!size)
96ad309a88SDingqiang Lin 		goto exit_wait;
9758463f4dSJon Lin 	if (op->sfctrl.b.enbledma) {
98ad309a88SDingqiang Lin 		struct bounce_buffer bb;
99ad309a88SDingqiang Lin 		unsigned int bb_flags;
100ad309a88SDingqiang Lin 
101ad309a88SDingqiang Lin 		if (cmd.b.rw == SFC_WRITE)
102ad309a88SDingqiang Lin 			bb_flags = GEN_BB_READ;
103ad309a88SDingqiang Lin 		else
104ad309a88SDingqiang Lin 			bb_flags = GEN_BB_WRITE;
105ad309a88SDingqiang Lin 
10658463f4dSJon Lin 		ret = bounce_buffer_start(&bb, data, size, bb_flags);
107ad309a88SDingqiang Lin 		if (ret)
108ad309a88SDingqiang Lin 			return ret;
109ad309a88SDingqiang Lin 
110ad309a88SDingqiang Lin 		writel(0xFFFFFFFF, g_sfc_reg + SFC_ICLR);
11158463f4dSJon Lin 		writel(~((u32)DMA_INT), g_sfc_reg + SFC_IMR);
1121bb49bc4SDingqiang Lin 		writel((unsigned long)bb.bounce_buffer, g_sfc_reg + SFC_DMA_ADDR);
113ad309a88SDingqiang Lin 		writel(SFC_DMA_START, g_sfc_reg + SFC_DMA_TRIGGER);
114ad309a88SDingqiang Lin 
11558463f4dSJon Lin 		timeout = size * 10;
116*534d4d2fSJon Lin 		while ((readl(g_sfc_reg + SFC_SR) & SFC_BUSY) &&
117ad309a88SDingqiang Lin 		       (timeout-- > 0))
118ad309a88SDingqiang Lin 			sfc_delay(1);
119ad309a88SDingqiang Lin 		writel(0xFFFFFFFF, g_sfc_reg + SFC_ICLR);
120ad309a88SDingqiang Lin 		if (timeout <= 0)
121ad309a88SDingqiang Lin 			ret = SFC_WAIT_TIMEOUT;
122ad309a88SDingqiang Lin 		bounce_buffer_stop(&bb);
123ad309a88SDingqiang Lin 	} else {
124ad309a88SDingqiang Lin 		u32 i, words, count, bytes;
125ad309a88SDingqiang Lin 		union SFCFSR_DATA    fifostat;
126ad309a88SDingqiang Lin 		u32 *p_data = (u32 *)data;
127ad309a88SDingqiang Lin 
128ad309a88SDingqiang Lin 		if (cmd.b.rw == SFC_WRITE) {
12958463f4dSJon Lin 			words  = (size + 3) >> 2;
130ad309a88SDingqiang Lin 			while (words) {
131ad309a88SDingqiang Lin 				fifostat.d32 = readl(g_sfc_reg + SFC_FSR);
132ad309a88SDingqiang Lin 				if (fifostat.b.txlevel > 0) {
133ad309a88SDingqiang Lin 					count = words < fifostat.b.txlevel ?
134ad309a88SDingqiang Lin 						words : fifostat.b.txlevel;
135ad309a88SDingqiang Lin 					for (i = 0; i < count; i++) {
136ad309a88SDingqiang Lin 						writel(*p_data++,
137ad309a88SDingqiang Lin 						       g_sfc_reg + SFC_DATA);
138ad309a88SDingqiang Lin 						words--;
139ad309a88SDingqiang Lin 					}
140ad309a88SDingqiang Lin 					if (words == 0)
141ad309a88SDingqiang Lin 						break;
142ad309a88SDingqiang Lin 					timeout = 0;
143ad309a88SDingqiang Lin 				} else {
144ad309a88SDingqiang Lin 					sfc_delay(1);
145ad309a88SDingqiang Lin 					if (timeout++ > 10000) {
146ad309a88SDingqiang Lin 						ret = SFC_TX_TIMEOUT;
147ad309a88SDingqiang Lin 						break;
148ad309a88SDingqiang Lin 					}
149ad309a88SDingqiang Lin 				}
150ad309a88SDingqiang Lin 			}
151ad309a88SDingqiang Lin 		} else {
152ad309a88SDingqiang Lin 			/* SFC_READ == cmd.b.rw */
15358463f4dSJon Lin 			bytes = size & 0x3;
15458463f4dSJon Lin 			words = size >> 2;
155ad309a88SDingqiang Lin 			while (words) {
156ad309a88SDingqiang Lin 				fifostat.d32 = readl(g_sfc_reg + SFC_FSR);
157ad309a88SDingqiang Lin 				if (fifostat.b.rxlevel > 0) {
158ad309a88SDingqiang Lin 					u32 count;
159ad309a88SDingqiang Lin 
160ad309a88SDingqiang Lin 					count = words < fifostat.b.rxlevel ?
161ad309a88SDingqiang Lin 						words : fifostat.b.rxlevel;
162ad309a88SDingqiang Lin 
163ad309a88SDingqiang Lin 					for (i = 0; i < count; i++) {
164ad309a88SDingqiang Lin 						*p_data++ = readl(g_sfc_reg +
165ad309a88SDingqiang Lin 								  SFC_DATA);
166ad309a88SDingqiang Lin 						words--;
167ad309a88SDingqiang Lin 					}
168ad309a88SDingqiang Lin 					if (words == 0)
169ad309a88SDingqiang Lin 						break;
170ad309a88SDingqiang Lin 					timeout = 0;
171ad309a88SDingqiang Lin 				} else {
172ad309a88SDingqiang Lin 					sfc_delay(1);
173ad309a88SDingqiang Lin 					if (timeout++ > 10000) {
174ad309a88SDingqiang Lin 						ret = SFC_RX_TIMEOUT;
175ad309a88SDingqiang Lin 						break;
176ad309a88SDingqiang Lin 					}
177ad309a88SDingqiang Lin 				}
178ad309a88SDingqiang Lin 			}
179ad309a88SDingqiang Lin 
180ad309a88SDingqiang Lin 			timeout = 0;
181ad309a88SDingqiang Lin 			while (bytes) {
182ad309a88SDingqiang Lin 				fifostat.d32 = readl(g_sfc_reg + SFC_FSR);
183ad309a88SDingqiang Lin 				if (fifostat.b.rxlevel > 0) {
184ad309a88SDingqiang Lin 					u8 *p_data1 = (u8 *)p_data;
185ad309a88SDingqiang Lin 
186ad309a88SDingqiang Lin 					words = readl(g_sfc_reg + SFC_DATA);
187ad309a88SDingqiang Lin 					for (i = 0; i < bytes; i++)
188ad309a88SDingqiang Lin 						p_data1[i] =
189ad309a88SDingqiang Lin 						(u8)((words >> (i * 8)) & 0xFF);
190ad309a88SDingqiang Lin 					break;
191ad309a88SDingqiang Lin 				}
192ad309a88SDingqiang Lin 
193ad309a88SDingqiang Lin 				sfc_delay(1);
194ad309a88SDingqiang Lin 				if (timeout++ > 10000) {
195ad309a88SDingqiang Lin 					ret = SFC_RX_TIMEOUT;
196ad309a88SDingqiang Lin 					break;
197ad309a88SDingqiang Lin 				}
198ad309a88SDingqiang Lin 			}
199ad309a88SDingqiang Lin 		}
200ad309a88SDingqiang Lin 	}
201ad309a88SDingqiang Lin 
202ad309a88SDingqiang Lin exit_wait:
203ad309a88SDingqiang Lin 	timeout = 0;    /* wait cmd or data send complete */
20458463f4dSJon Lin 	while (readl(g_sfc_reg + SFC_SR) & SFC_BUSY) {
205ad309a88SDingqiang Lin 		sfc_delay(1);
206ad309a88SDingqiang Lin 		if (timeout++ > 100000) {         /* wait 100ms */
207ad309a88SDingqiang Lin 			ret = SFC_TX_TIMEOUT;
208ad309a88SDingqiang Lin 			break;
209ad309a88SDingqiang Lin 		}
210ad309a88SDingqiang Lin 	}
211ad309a88SDingqiang Lin 	sfc_delay(1); /* CS# High Time (read/write) >100ns */
212ad309a88SDingqiang Lin 	return ret;
213ad309a88SDingqiang Lin }
214