xref: /rk3399_rockchip-uboot/drivers/rkflash/sfc.c (revision 1bb49bc4e2f612da166d09b71ad250f00f6f643c)
1ad309a88SDingqiang Lin /*
2ad309a88SDingqiang Lin  * Copyright (c) 2018 Fuzhou Rockchip Electronics Co., Ltd
3ad309a88SDingqiang Lin  *
4ad309a88SDingqiang Lin  * SPDX-License-Identifier: (GPL-2.0+ OR MIT)
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 
14ad309a88SDingqiang Lin static void __iomem *g_sfc_reg;
15ad309a88SDingqiang Lin 
16ad309a88SDingqiang Lin static void sfc_reset(void)
17ad309a88SDingqiang Lin {
18ad309a88SDingqiang Lin 	int timeout = 10000;
19ad309a88SDingqiang Lin 
20ad309a88SDingqiang Lin 	writel(SFC_RESET, g_sfc_reg + SFC_RCVR);
21ad309a88SDingqiang Lin 	while ((readl(g_sfc_reg + SFC_RCVR) == SFC_RESET) && (timeout > 0)) {
22ad309a88SDingqiang Lin 		sfc_delay(1);
23ad309a88SDingqiang Lin 		timeout--;
24ad309a88SDingqiang Lin 	}
25ad309a88SDingqiang Lin 	writel(0xFFFFFFFF, g_sfc_reg + SFC_ICLR);
26ad309a88SDingqiang Lin }
27ad309a88SDingqiang Lin 
28ad309a88SDingqiang Lin u16 sfc_get_version(void)
29ad309a88SDingqiang Lin {
30ad309a88SDingqiang Lin 	return  (u32)(readl(g_sfc_reg + SFC_VER) & 0xffff);
31ad309a88SDingqiang Lin }
32ad309a88SDingqiang Lin 
33ad309a88SDingqiang Lin int sfc_init(void __iomem *reg_addr)
34ad309a88SDingqiang Lin {
35ad309a88SDingqiang Lin 	g_sfc_reg = reg_addr;
36ad309a88SDingqiang Lin 	sfc_reset();
37ad309a88SDingqiang Lin 	writel(0, g_sfc_reg + SFC_CTRL);
38ad309a88SDingqiang Lin 
39ad309a88SDingqiang Lin 	return SFC_OK;
40ad309a88SDingqiang Lin }
41ad309a88SDingqiang Lin 
42ad309a88SDingqiang Lin void sfc_clean_irq(void)
43ad309a88SDingqiang Lin {
44ad309a88SDingqiang Lin 	writel(0xFFFFFFFF, g_sfc_reg + SFC_ICLR);
45ad309a88SDingqiang Lin 	writel(0xFFFFFFFF, g_sfc_reg + SFC_IMR);
46ad309a88SDingqiang Lin }
47ad309a88SDingqiang Lin 
48ad309a88SDingqiang Lin int sfc_request(u32 sfcmd, u32 sfctrl, u32 addr, void *data)
49ad309a88SDingqiang Lin {
50ad309a88SDingqiang Lin 	int ret = SFC_OK;
51ad309a88SDingqiang Lin 	union SFCCMD_DATA cmd;
52ad309a88SDingqiang Lin 	int reg;
53ad309a88SDingqiang Lin 	int timeout = 0;
54ad309a88SDingqiang Lin 
55ad309a88SDingqiang Lin 	reg = readl(g_sfc_reg + SFC_FSR);
56ad309a88SDingqiang Lin 	if (!(reg & SFC_TXEMPTY) || !(reg & SFC_RXEMPTY) ||
57ad309a88SDingqiang Lin 	    (readl(g_sfc_reg + SFC_SR) & SFC_BUSY))
58ad309a88SDingqiang Lin 		sfc_reset();
59ad309a88SDingqiang Lin 
60ad309a88SDingqiang Lin 	cmd.d32 = sfcmd;
61ad309a88SDingqiang Lin 	if (cmd.b.addrbits == SFC_ADDR_XBITS) {
62ad309a88SDingqiang Lin 		union SFCCTRL_DATA ctrl;
63ad309a88SDingqiang Lin 
64ad309a88SDingqiang Lin 		ctrl.d32 = sfctrl;
65ad309a88SDingqiang Lin 		if (!ctrl.b.addrbits)
66ad309a88SDingqiang Lin 			return SFC_PARAM_ERR;
67ad309a88SDingqiang Lin 		/* Controller plus 1 automatically */
68ad309a88SDingqiang Lin 		writel(ctrl.b.addrbits - 1, g_sfc_reg + SFC_ABIT);
69ad309a88SDingqiang Lin 	}
70ad309a88SDingqiang Lin 	/* shift in the data at negedge sclk_out */
71ad309a88SDingqiang Lin 	sfctrl |= 0x2;
72ad309a88SDingqiang Lin 
73ad309a88SDingqiang Lin 	writel(sfctrl, g_sfc_reg + SFC_CTRL);
74ad309a88SDingqiang Lin 	writel(sfcmd, g_sfc_reg + SFC_CMD);
75ad309a88SDingqiang Lin 	if (cmd.b.addrbits)
76ad309a88SDingqiang Lin 		writel(addr, g_sfc_reg + SFC_ADDR);
77ad309a88SDingqiang Lin 	if (!cmd.b.datasize)
78ad309a88SDingqiang Lin 		goto exit_wait;
79ad309a88SDingqiang Lin 	if (SFC_ENABLE_DMA & sfctrl) {
80ad309a88SDingqiang Lin 		struct bounce_buffer bb;
81ad309a88SDingqiang Lin 		unsigned int bb_flags;
82ad309a88SDingqiang Lin 
83ad309a88SDingqiang Lin 		if (cmd.b.rw == SFC_WRITE)
84ad309a88SDingqiang Lin 			bb_flags = GEN_BB_READ;
85ad309a88SDingqiang Lin 		else
86ad309a88SDingqiang Lin 			bb_flags = GEN_BB_WRITE;
87ad309a88SDingqiang Lin 
88ad309a88SDingqiang Lin 		ret = bounce_buffer_start(&bb, data, cmd.b.datasize, bb_flags);
89ad309a88SDingqiang Lin 		if (ret)
90ad309a88SDingqiang Lin 			return ret;
91ad309a88SDingqiang Lin 
92ad309a88SDingqiang Lin 		writel(0xFFFFFFFF, g_sfc_reg + SFC_ICLR);
93ad309a88SDingqiang Lin 		writel(~((u32)FINISH_INT), g_sfc_reg + SFC_IMR);
94*1bb49bc4SDingqiang Lin 		writel((unsigned long)bb.bounce_buffer, g_sfc_reg + SFC_DMA_ADDR);
95ad309a88SDingqiang Lin 		writel(SFC_DMA_START, g_sfc_reg + SFC_DMA_TRIGGER);
96ad309a88SDingqiang Lin 
97ad309a88SDingqiang Lin 		timeout = cmd.b.datasize * 10;
98ad309a88SDingqiang Lin 		while ((readl(g_sfc_reg + SFC_SR) & SFC_BUSY) &&
99ad309a88SDingqiang Lin 		       (timeout-- > 0))
100ad309a88SDingqiang Lin 			sfc_delay(1);
101ad309a88SDingqiang Lin 		writel(0xFFFFFFFF, g_sfc_reg + SFC_ICLR);
102ad309a88SDingqiang Lin 		if (timeout <= 0)
103ad309a88SDingqiang Lin 			ret = SFC_WAIT_TIMEOUT;
104ad309a88SDingqiang Lin 		bounce_buffer_stop(&bb);
105ad309a88SDingqiang Lin 	} else {
106ad309a88SDingqiang Lin 		u32 i, words, count, bytes;
107ad309a88SDingqiang Lin 		union SFCFSR_DATA    fifostat;
108ad309a88SDingqiang Lin 		u32 *p_data = (u32 *)data;
109ad309a88SDingqiang Lin 
110ad309a88SDingqiang Lin 		if (cmd.b.rw == SFC_WRITE) {
111ad309a88SDingqiang Lin 			words  = (cmd.b.datasize + 3) >> 2;
112ad309a88SDingqiang Lin 			while (words) {
113ad309a88SDingqiang Lin 				fifostat.d32 = readl(g_sfc_reg + SFC_FSR);
114ad309a88SDingqiang Lin 				if (fifostat.b.txlevel > 0) {
115ad309a88SDingqiang Lin 					count = words < fifostat.b.txlevel ?
116ad309a88SDingqiang Lin 						words : fifostat.b.txlevel;
117ad309a88SDingqiang Lin 					for (i = 0; i < count; i++) {
118ad309a88SDingqiang Lin 						writel(*p_data++,
119ad309a88SDingqiang Lin 						       g_sfc_reg + SFC_DATA);
120ad309a88SDingqiang Lin 						words--;
121ad309a88SDingqiang Lin 					}
122ad309a88SDingqiang Lin 					if (words == 0)
123ad309a88SDingqiang Lin 						break;
124ad309a88SDingqiang Lin 					timeout = 0;
125ad309a88SDingqiang Lin 				} else {
126ad309a88SDingqiang Lin 					sfc_delay(1);
127ad309a88SDingqiang Lin 					if (timeout++ > 10000) {
128ad309a88SDingqiang Lin 						ret = SFC_TX_TIMEOUT;
129ad309a88SDingqiang Lin 						break;
130ad309a88SDingqiang Lin 					}
131ad309a88SDingqiang Lin 				}
132ad309a88SDingqiang Lin 			}
133ad309a88SDingqiang Lin 		} else {
134ad309a88SDingqiang Lin 			/* SFC_READ == cmd.b.rw */
135ad309a88SDingqiang Lin 			bytes = cmd.b.datasize & 0x3;
136ad309a88SDingqiang Lin 			words = cmd.b.datasize >> 2;
137ad309a88SDingqiang Lin 			while (words) {
138ad309a88SDingqiang Lin 				fifostat.d32 = readl(g_sfc_reg + SFC_FSR);
139ad309a88SDingqiang Lin 				if (fifostat.b.rxlevel > 0) {
140ad309a88SDingqiang Lin 					u32 count;
141ad309a88SDingqiang Lin 
142ad309a88SDingqiang Lin 					count = words < fifostat.b.rxlevel ?
143ad309a88SDingqiang Lin 						words : fifostat.b.rxlevel;
144ad309a88SDingqiang Lin 
145ad309a88SDingqiang Lin 					for (i = 0; i < count; i++) {
146ad309a88SDingqiang Lin 						*p_data++ = readl(g_sfc_reg +
147ad309a88SDingqiang Lin 								  SFC_DATA);
148ad309a88SDingqiang Lin 						words--;
149ad309a88SDingqiang Lin 					}
150ad309a88SDingqiang Lin 					if (words == 0)
151ad309a88SDingqiang Lin 						break;
152ad309a88SDingqiang Lin 					timeout = 0;
153ad309a88SDingqiang Lin 				} else {
154ad309a88SDingqiang Lin 					sfc_delay(1);
155ad309a88SDingqiang Lin 					if (timeout++ > 10000) {
156ad309a88SDingqiang Lin 						ret = SFC_RX_TIMEOUT;
157ad309a88SDingqiang Lin 						break;
158ad309a88SDingqiang Lin 					}
159ad309a88SDingqiang Lin 				}
160ad309a88SDingqiang Lin 			}
161ad309a88SDingqiang Lin 
162ad309a88SDingqiang Lin 			timeout = 0;
163ad309a88SDingqiang Lin 			while (bytes) {
164ad309a88SDingqiang Lin 				fifostat.d32 = readl(g_sfc_reg + SFC_FSR);
165ad309a88SDingqiang Lin 				if (fifostat.b.rxlevel > 0) {
166ad309a88SDingqiang Lin 					u8 *p_data1 = (u8 *)p_data;
167ad309a88SDingqiang Lin 
168ad309a88SDingqiang Lin 					words = readl(g_sfc_reg + SFC_DATA);
169ad309a88SDingqiang Lin 					for (i = 0; i < bytes; i++)
170ad309a88SDingqiang Lin 						p_data1[i] =
171ad309a88SDingqiang Lin 						(u8)((words >> (i * 8)) & 0xFF);
172ad309a88SDingqiang Lin 					break;
173ad309a88SDingqiang Lin 				}
174ad309a88SDingqiang Lin 
175ad309a88SDingqiang Lin 				sfc_delay(1);
176ad309a88SDingqiang Lin 				if (timeout++ > 10000) {
177ad309a88SDingqiang Lin 					ret = SFC_RX_TIMEOUT;
178ad309a88SDingqiang Lin 					break;
179ad309a88SDingqiang Lin 				}
180ad309a88SDingqiang Lin 			}
181ad309a88SDingqiang Lin 		}
182ad309a88SDingqiang Lin 	}
183ad309a88SDingqiang Lin 
184ad309a88SDingqiang Lin exit_wait:
185ad309a88SDingqiang Lin 	timeout = 0;    /* wait cmd or data send complete */
186ad309a88SDingqiang Lin 	while (!(readl(g_sfc_reg + SFC_FSR) & SFC_TXEMPTY)) {
187ad309a88SDingqiang Lin 		sfc_delay(1);
188ad309a88SDingqiang Lin 		if (timeout++ > 100000) {         /* wait 100ms */
189ad309a88SDingqiang Lin 			ret = SFC_TX_TIMEOUT;
190ad309a88SDingqiang Lin 			break;
191ad309a88SDingqiang Lin 		}
192ad309a88SDingqiang Lin 	}
193ad309a88SDingqiang Lin 	sfc_delay(1); /* CS# High Time (read/write) >100ns */
194ad309a88SDingqiang Lin 	return ret;
195ad309a88SDingqiang Lin }
196