xref: /OK3568_Linux_fs/u-boot/arch/arm/mach-sunxi/spl_spi_sunxi.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun /*
2*4882a593Smuzhiyun  * Copyright (C) 2016 Siarhei Siamashka <siarhei.siamashka@gmail.com>
3*4882a593Smuzhiyun  *
4*4882a593Smuzhiyun  * SPDX-License-Identifier:	GPL-2.0+
5*4882a593Smuzhiyun  */
6*4882a593Smuzhiyun 
7*4882a593Smuzhiyun #include <common.h>
8*4882a593Smuzhiyun #include <spl.h>
9*4882a593Smuzhiyun #include <asm/gpio.h>
10*4882a593Smuzhiyun #include <asm/io.h>
11*4882a593Smuzhiyun #include <linux/libfdt.h>
12*4882a593Smuzhiyun 
13*4882a593Smuzhiyun #ifdef CONFIG_SPL_OS_BOOT
14*4882a593Smuzhiyun #error CONFIG_SPL_OS_BOOT is not supported yet
15*4882a593Smuzhiyun #endif
16*4882a593Smuzhiyun 
17*4882a593Smuzhiyun /*
18*4882a593Smuzhiyun  * This is a very simple U-Boot image loading implementation, trying to
19*4882a593Smuzhiyun  * replicate what the boot ROM is doing when loading the SPL. Because we
20*4882a593Smuzhiyun  * know the exact pins where the SPI Flash is connected and also know
21*4882a593Smuzhiyun  * that the Read Data Bytes (03h) command is supported, the hardware
22*4882a593Smuzhiyun  * configuration is very simple and we don't need the extra flexibility
23*4882a593Smuzhiyun  * of the SPI framework. Moreover, we rely on the default settings of
24*4882a593Smuzhiyun  * the SPI controler hardware registers and only adjust what needs to
25*4882a593Smuzhiyun  * be changed. This is good for the code size and this implementation
26*4882a593Smuzhiyun  * adds less than 400 bytes to the SPL.
27*4882a593Smuzhiyun  *
28*4882a593Smuzhiyun  * There are two variants of the SPI controller in Allwinner SoCs:
29*4882a593Smuzhiyun  * A10/A13/A20 (sun4i variant) and everything else (sun6i variant).
30*4882a593Smuzhiyun  * Both of them are supported.
31*4882a593Smuzhiyun  *
32*4882a593Smuzhiyun  * The pin mixing part is SoC specific and only A10/A13/A20/H3/A64 are
33*4882a593Smuzhiyun  * supported at the moment.
34*4882a593Smuzhiyun  */
35*4882a593Smuzhiyun 
36*4882a593Smuzhiyun /*****************************************************************************/
37*4882a593Smuzhiyun /* SUN4I variant of the SPI controller                                       */
38*4882a593Smuzhiyun /*****************************************************************************/
39*4882a593Smuzhiyun 
40*4882a593Smuzhiyun #define SUN4I_SPI0_CCTL             (0x01C05000 + 0x1C)
41*4882a593Smuzhiyun #define SUN4I_SPI0_CTL              (0x01C05000 + 0x08)
42*4882a593Smuzhiyun #define SUN4I_SPI0_RX               (0x01C05000 + 0x00)
43*4882a593Smuzhiyun #define SUN4I_SPI0_TX               (0x01C05000 + 0x04)
44*4882a593Smuzhiyun #define SUN4I_SPI0_FIFO_STA         (0x01C05000 + 0x28)
45*4882a593Smuzhiyun #define SUN4I_SPI0_BC               (0x01C05000 + 0x20)
46*4882a593Smuzhiyun #define SUN4I_SPI0_TC               (0x01C05000 + 0x24)
47*4882a593Smuzhiyun 
48*4882a593Smuzhiyun #define SUN4I_CTL_ENABLE            BIT(0)
49*4882a593Smuzhiyun #define SUN4I_CTL_MASTER            BIT(1)
50*4882a593Smuzhiyun #define SUN4I_CTL_TF_RST            BIT(8)
51*4882a593Smuzhiyun #define SUN4I_CTL_RF_RST            BIT(9)
52*4882a593Smuzhiyun #define SUN4I_CTL_XCH               BIT(10)
53*4882a593Smuzhiyun 
54*4882a593Smuzhiyun /*****************************************************************************/
55*4882a593Smuzhiyun /* SUN6I variant of the SPI controller                                       */
56*4882a593Smuzhiyun /*****************************************************************************/
57*4882a593Smuzhiyun 
58*4882a593Smuzhiyun #define SUN6I_SPI0_CCTL             (0x01C68000 + 0x24)
59*4882a593Smuzhiyun #define SUN6I_SPI0_GCR              (0x01C68000 + 0x04)
60*4882a593Smuzhiyun #define SUN6I_SPI0_TCR              (0x01C68000 + 0x08)
61*4882a593Smuzhiyun #define SUN6I_SPI0_FIFO_STA         (0x01C68000 + 0x1C)
62*4882a593Smuzhiyun #define SUN6I_SPI0_MBC              (0x01C68000 + 0x30)
63*4882a593Smuzhiyun #define SUN6I_SPI0_MTC              (0x01C68000 + 0x34)
64*4882a593Smuzhiyun #define SUN6I_SPI0_BCC              (0x01C68000 + 0x38)
65*4882a593Smuzhiyun #define SUN6I_SPI0_TXD              (0x01C68000 + 0x200)
66*4882a593Smuzhiyun #define SUN6I_SPI0_RXD              (0x01C68000 + 0x300)
67*4882a593Smuzhiyun 
68*4882a593Smuzhiyun #define SUN6I_CTL_ENABLE            BIT(0)
69*4882a593Smuzhiyun #define SUN6I_CTL_MASTER            BIT(1)
70*4882a593Smuzhiyun #define SUN6I_CTL_SRST              BIT(31)
71*4882a593Smuzhiyun #define SUN6I_TCR_XCH               BIT(31)
72*4882a593Smuzhiyun 
73*4882a593Smuzhiyun /*****************************************************************************/
74*4882a593Smuzhiyun 
75*4882a593Smuzhiyun #define CCM_AHB_GATING0             (0x01C20000 + 0x60)
76*4882a593Smuzhiyun #define CCM_SPI0_CLK                (0x01C20000 + 0xA0)
77*4882a593Smuzhiyun #define SUN6I_BUS_SOFT_RST_REG0     (0x01C20000 + 0x2C0)
78*4882a593Smuzhiyun 
79*4882a593Smuzhiyun #define AHB_RESET_SPI0_SHIFT        20
80*4882a593Smuzhiyun #define AHB_GATE_OFFSET_SPI0        20
81*4882a593Smuzhiyun 
82*4882a593Smuzhiyun #define SPI0_CLK_DIV_BY_2           0x1000
83*4882a593Smuzhiyun #define SPI0_CLK_DIV_BY_4           0x1001
84*4882a593Smuzhiyun 
85*4882a593Smuzhiyun /*****************************************************************************/
86*4882a593Smuzhiyun 
87*4882a593Smuzhiyun /*
88*4882a593Smuzhiyun  * Allwinner A10/A20 SoCs were using pins PC0,PC1,PC2,PC23 for booting
89*4882a593Smuzhiyun  * from SPI Flash, everything else is using pins PC0,PC1,PC2,PC3.
90*4882a593Smuzhiyun  */
spi0_pinmux_setup(unsigned int pin_function)91*4882a593Smuzhiyun static void spi0_pinmux_setup(unsigned int pin_function)
92*4882a593Smuzhiyun {
93*4882a593Smuzhiyun 	unsigned int pin;
94*4882a593Smuzhiyun 
95*4882a593Smuzhiyun 	for (pin = SUNXI_GPC(0); pin <= SUNXI_GPC(2); pin++)
96*4882a593Smuzhiyun 		sunxi_gpio_set_cfgpin(pin, pin_function);
97*4882a593Smuzhiyun 
98*4882a593Smuzhiyun 	if (IS_ENABLED(CONFIG_MACH_SUN4I) || IS_ENABLED(CONFIG_MACH_SUN7I))
99*4882a593Smuzhiyun 		sunxi_gpio_set_cfgpin(SUNXI_GPC(23), pin_function);
100*4882a593Smuzhiyun 	else
101*4882a593Smuzhiyun 		sunxi_gpio_set_cfgpin(SUNXI_GPC(3), pin_function);
102*4882a593Smuzhiyun }
103*4882a593Smuzhiyun 
104*4882a593Smuzhiyun /*
105*4882a593Smuzhiyun  * Setup 6 MHz from OSC24M (because the BROM is doing the same).
106*4882a593Smuzhiyun  */
spi0_enable_clock(void)107*4882a593Smuzhiyun static void spi0_enable_clock(void)
108*4882a593Smuzhiyun {
109*4882a593Smuzhiyun 	/* Deassert SPI0 reset on SUN6I */
110*4882a593Smuzhiyun 	if (IS_ENABLED(CONFIG_SUNXI_GEN_SUN6I))
111*4882a593Smuzhiyun 		setbits_le32(SUN6I_BUS_SOFT_RST_REG0,
112*4882a593Smuzhiyun 			     (1 << AHB_RESET_SPI0_SHIFT));
113*4882a593Smuzhiyun 
114*4882a593Smuzhiyun 	/* Open the SPI0 gate */
115*4882a593Smuzhiyun 	setbits_le32(CCM_AHB_GATING0, (1 << AHB_GATE_OFFSET_SPI0));
116*4882a593Smuzhiyun 
117*4882a593Smuzhiyun 	/* Divide by 4 */
118*4882a593Smuzhiyun 	writel(SPI0_CLK_DIV_BY_4, IS_ENABLED(CONFIG_SUNXI_GEN_SUN6I) ?
119*4882a593Smuzhiyun 				  SUN6I_SPI0_CCTL : SUN4I_SPI0_CCTL);
120*4882a593Smuzhiyun 	/* 24MHz from OSC24M */
121*4882a593Smuzhiyun 	writel((1 << 31), CCM_SPI0_CLK);
122*4882a593Smuzhiyun 
123*4882a593Smuzhiyun 	if (IS_ENABLED(CONFIG_SUNXI_GEN_SUN6I)) {
124*4882a593Smuzhiyun 		/* Enable SPI in the master mode and do a soft reset */
125*4882a593Smuzhiyun 		setbits_le32(SUN6I_SPI0_GCR, SUN6I_CTL_MASTER |
126*4882a593Smuzhiyun 					     SUN6I_CTL_ENABLE |
127*4882a593Smuzhiyun 					     SUN6I_CTL_SRST);
128*4882a593Smuzhiyun 		/* Wait for completion */
129*4882a593Smuzhiyun 		while (readl(SUN6I_SPI0_GCR) & SUN6I_CTL_SRST)
130*4882a593Smuzhiyun 			;
131*4882a593Smuzhiyun 	} else {
132*4882a593Smuzhiyun 		/* Enable SPI in the master mode and reset FIFO */
133*4882a593Smuzhiyun 		setbits_le32(SUN4I_SPI0_CTL, SUN4I_CTL_MASTER |
134*4882a593Smuzhiyun 					     SUN4I_CTL_ENABLE |
135*4882a593Smuzhiyun 					     SUN4I_CTL_TF_RST |
136*4882a593Smuzhiyun 					     SUN4I_CTL_RF_RST);
137*4882a593Smuzhiyun 	}
138*4882a593Smuzhiyun }
139*4882a593Smuzhiyun 
spi0_disable_clock(void)140*4882a593Smuzhiyun static void spi0_disable_clock(void)
141*4882a593Smuzhiyun {
142*4882a593Smuzhiyun 	/* Disable the SPI0 controller */
143*4882a593Smuzhiyun 	if (IS_ENABLED(CONFIG_SUNXI_GEN_SUN6I))
144*4882a593Smuzhiyun 		clrbits_le32(SUN6I_SPI0_GCR, SUN6I_CTL_MASTER |
145*4882a593Smuzhiyun 					     SUN6I_CTL_ENABLE);
146*4882a593Smuzhiyun 	else
147*4882a593Smuzhiyun 		clrbits_le32(SUN4I_SPI0_CTL, SUN4I_CTL_MASTER |
148*4882a593Smuzhiyun 					     SUN4I_CTL_ENABLE);
149*4882a593Smuzhiyun 
150*4882a593Smuzhiyun 	/* Disable the SPI0 clock */
151*4882a593Smuzhiyun 	writel(0, CCM_SPI0_CLK);
152*4882a593Smuzhiyun 
153*4882a593Smuzhiyun 	/* Close the SPI0 gate */
154*4882a593Smuzhiyun 	clrbits_le32(CCM_AHB_GATING0, (1 << AHB_GATE_OFFSET_SPI0));
155*4882a593Smuzhiyun 
156*4882a593Smuzhiyun 	/* Assert SPI0 reset on SUN6I */
157*4882a593Smuzhiyun 	if (IS_ENABLED(CONFIG_SUNXI_GEN_SUN6I))
158*4882a593Smuzhiyun 		clrbits_le32(SUN6I_BUS_SOFT_RST_REG0,
159*4882a593Smuzhiyun 			     (1 << AHB_RESET_SPI0_SHIFT));
160*4882a593Smuzhiyun }
161*4882a593Smuzhiyun 
spi0_init(void)162*4882a593Smuzhiyun static void spi0_init(void)
163*4882a593Smuzhiyun {
164*4882a593Smuzhiyun 	unsigned int pin_function = SUNXI_GPC_SPI0;
165*4882a593Smuzhiyun 
166*4882a593Smuzhiyun 	if (IS_ENABLED(CONFIG_MACH_SUN50I))
167*4882a593Smuzhiyun 		pin_function = SUN50I_GPC_SPI0;
168*4882a593Smuzhiyun 
169*4882a593Smuzhiyun 	spi0_pinmux_setup(pin_function);
170*4882a593Smuzhiyun 	spi0_enable_clock();
171*4882a593Smuzhiyun }
172*4882a593Smuzhiyun 
spi0_deinit(void)173*4882a593Smuzhiyun static void spi0_deinit(void)
174*4882a593Smuzhiyun {
175*4882a593Smuzhiyun 	/* New SoCs can disable pins, older could only set them as input */
176*4882a593Smuzhiyun 	unsigned int pin_function = SUNXI_GPIO_INPUT;
177*4882a593Smuzhiyun 	if (IS_ENABLED(CONFIG_SUNXI_GEN_SUN6I))
178*4882a593Smuzhiyun 		pin_function = SUNXI_GPIO_DISABLE;
179*4882a593Smuzhiyun 
180*4882a593Smuzhiyun 	spi0_disable_clock();
181*4882a593Smuzhiyun 	spi0_pinmux_setup(pin_function);
182*4882a593Smuzhiyun }
183*4882a593Smuzhiyun 
184*4882a593Smuzhiyun /*****************************************************************************/
185*4882a593Smuzhiyun 
186*4882a593Smuzhiyun #define SPI_READ_MAX_SIZE 60 /* FIFO size, minus 4 bytes of the header */
187*4882a593Smuzhiyun 
sunxi_spi0_read_data(u8 * buf,u32 addr,u32 bufsize,ulong spi_ctl_reg,ulong spi_ctl_xch_bitmask,ulong spi_fifo_reg,ulong spi_tx_reg,ulong spi_rx_reg,ulong spi_bc_reg,ulong spi_tc_reg,ulong spi_bcc_reg)188*4882a593Smuzhiyun static void sunxi_spi0_read_data(u8 *buf, u32 addr, u32 bufsize,
189*4882a593Smuzhiyun 				 ulong spi_ctl_reg,
190*4882a593Smuzhiyun 				 ulong spi_ctl_xch_bitmask,
191*4882a593Smuzhiyun 				 ulong spi_fifo_reg,
192*4882a593Smuzhiyun 				 ulong spi_tx_reg,
193*4882a593Smuzhiyun 				 ulong spi_rx_reg,
194*4882a593Smuzhiyun 				 ulong spi_bc_reg,
195*4882a593Smuzhiyun 				 ulong spi_tc_reg,
196*4882a593Smuzhiyun 				 ulong spi_bcc_reg)
197*4882a593Smuzhiyun {
198*4882a593Smuzhiyun 	writel(4 + bufsize, spi_bc_reg); /* Burst counter (total bytes) */
199*4882a593Smuzhiyun 	writel(4, spi_tc_reg);           /* Transfer counter (bytes to send) */
200*4882a593Smuzhiyun 	if (spi_bcc_reg)
201*4882a593Smuzhiyun 		writel(4, spi_bcc_reg);  /* SUN6I also needs this */
202*4882a593Smuzhiyun 
203*4882a593Smuzhiyun 	/* Send the Read Data Bytes (03h) command header */
204*4882a593Smuzhiyun 	writeb(0x03, spi_tx_reg);
205*4882a593Smuzhiyun 	writeb((u8)(addr >> 16), spi_tx_reg);
206*4882a593Smuzhiyun 	writeb((u8)(addr >> 8), spi_tx_reg);
207*4882a593Smuzhiyun 	writeb((u8)(addr), spi_tx_reg);
208*4882a593Smuzhiyun 
209*4882a593Smuzhiyun 	/* Start the data transfer */
210*4882a593Smuzhiyun 	setbits_le32(spi_ctl_reg, spi_ctl_xch_bitmask);
211*4882a593Smuzhiyun 
212*4882a593Smuzhiyun 	/* Wait until everything is received in the RX FIFO */
213*4882a593Smuzhiyun 	while ((readl(spi_fifo_reg) & 0x7F) < 4 + bufsize)
214*4882a593Smuzhiyun 		;
215*4882a593Smuzhiyun 
216*4882a593Smuzhiyun 	/* Skip 4 bytes */
217*4882a593Smuzhiyun 	readl(spi_rx_reg);
218*4882a593Smuzhiyun 
219*4882a593Smuzhiyun 	/* Read the data */
220*4882a593Smuzhiyun 	while (bufsize-- > 0)
221*4882a593Smuzhiyun 		*buf++ = readb(spi_rx_reg);
222*4882a593Smuzhiyun 
223*4882a593Smuzhiyun 	/* tSHSL time is up to 100 ns in various SPI flash datasheets */
224*4882a593Smuzhiyun 	udelay(1);
225*4882a593Smuzhiyun }
226*4882a593Smuzhiyun 
spi0_read_data(void * buf,u32 addr,u32 len)227*4882a593Smuzhiyun static void spi0_read_data(void *buf, u32 addr, u32 len)
228*4882a593Smuzhiyun {
229*4882a593Smuzhiyun 	u8 *buf8 = buf;
230*4882a593Smuzhiyun 	u32 chunk_len;
231*4882a593Smuzhiyun 
232*4882a593Smuzhiyun 	while (len > 0) {
233*4882a593Smuzhiyun 		chunk_len = len;
234*4882a593Smuzhiyun 		if (chunk_len > SPI_READ_MAX_SIZE)
235*4882a593Smuzhiyun 			chunk_len = SPI_READ_MAX_SIZE;
236*4882a593Smuzhiyun 
237*4882a593Smuzhiyun 		if (IS_ENABLED(CONFIG_SUNXI_GEN_SUN6I)) {
238*4882a593Smuzhiyun 			sunxi_spi0_read_data(buf8, addr, chunk_len,
239*4882a593Smuzhiyun 					     SUN6I_SPI0_TCR,
240*4882a593Smuzhiyun 					     SUN6I_TCR_XCH,
241*4882a593Smuzhiyun 					     SUN6I_SPI0_FIFO_STA,
242*4882a593Smuzhiyun 					     SUN6I_SPI0_TXD,
243*4882a593Smuzhiyun 					     SUN6I_SPI0_RXD,
244*4882a593Smuzhiyun 					     SUN6I_SPI0_MBC,
245*4882a593Smuzhiyun 					     SUN6I_SPI0_MTC,
246*4882a593Smuzhiyun 					     SUN6I_SPI0_BCC);
247*4882a593Smuzhiyun 		} else {
248*4882a593Smuzhiyun 			sunxi_spi0_read_data(buf8, addr, chunk_len,
249*4882a593Smuzhiyun 					     SUN4I_SPI0_CTL,
250*4882a593Smuzhiyun 					     SUN4I_CTL_XCH,
251*4882a593Smuzhiyun 					     SUN4I_SPI0_FIFO_STA,
252*4882a593Smuzhiyun 					     SUN4I_SPI0_TX,
253*4882a593Smuzhiyun 					     SUN4I_SPI0_RX,
254*4882a593Smuzhiyun 					     SUN4I_SPI0_BC,
255*4882a593Smuzhiyun 					     SUN4I_SPI0_TC,
256*4882a593Smuzhiyun 					     0);
257*4882a593Smuzhiyun 		}
258*4882a593Smuzhiyun 
259*4882a593Smuzhiyun 		len  -= chunk_len;
260*4882a593Smuzhiyun 		buf8 += chunk_len;
261*4882a593Smuzhiyun 		addr += chunk_len;
262*4882a593Smuzhiyun 	}
263*4882a593Smuzhiyun }
264*4882a593Smuzhiyun 
spi_load_read(struct spl_load_info * load,ulong sector,ulong count,void * buf)265*4882a593Smuzhiyun static ulong spi_load_read(struct spl_load_info *load, ulong sector,
266*4882a593Smuzhiyun 			   ulong count, void *buf)
267*4882a593Smuzhiyun {
268*4882a593Smuzhiyun 	spi0_read_data(buf, sector, count);
269*4882a593Smuzhiyun 
270*4882a593Smuzhiyun 	return count;
271*4882a593Smuzhiyun }
272*4882a593Smuzhiyun 
273*4882a593Smuzhiyun /*****************************************************************************/
274*4882a593Smuzhiyun 
spl_spi_load_image(struct spl_image_info * spl_image,struct spl_boot_device * bootdev)275*4882a593Smuzhiyun static int spl_spi_load_image(struct spl_image_info *spl_image,
276*4882a593Smuzhiyun 			      struct spl_boot_device *bootdev)
277*4882a593Smuzhiyun {
278*4882a593Smuzhiyun 	int ret = 0;
279*4882a593Smuzhiyun 	struct image_header *header;
280*4882a593Smuzhiyun 	header = (struct image_header *)(CONFIG_SYS_TEXT_BASE);
281*4882a593Smuzhiyun 
282*4882a593Smuzhiyun 	spi0_init();
283*4882a593Smuzhiyun 
284*4882a593Smuzhiyun 	spi0_read_data((void *)header, CONFIG_SYS_SPI_U_BOOT_OFFS, 0x40);
285*4882a593Smuzhiyun 
286*4882a593Smuzhiyun         if (IS_ENABLED(CONFIG_SPL_LOAD_FIT) &&
287*4882a593Smuzhiyun 		image_get_magic(header) == FDT_MAGIC) {
288*4882a593Smuzhiyun 		struct spl_load_info load;
289*4882a593Smuzhiyun 
290*4882a593Smuzhiyun 		debug("Found FIT image\n");
291*4882a593Smuzhiyun 		load.dev = NULL;
292*4882a593Smuzhiyun 		load.priv = NULL;
293*4882a593Smuzhiyun 		load.filename = NULL;
294*4882a593Smuzhiyun 		load.bl_len = 1;
295*4882a593Smuzhiyun 		load.read = spi_load_read;
296*4882a593Smuzhiyun 		ret = spl_load_simple_fit(spl_image, &load,
297*4882a593Smuzhiyun 					  CONFIG_SYS_SPI_U_BOOT_OFFS, header);
298*4882a593Smuzhiyun 	} else {
299*4882a593Smuzhiyun 		ret = spl_parse_image_header(spl_image, header);
300*4882a593Smuzhiyun 		if (ret)
301*4882a593Smuzhiyun 			return ret;
302*4882a593Smuzhiyun 
303*4882a593Smuzhiyun 		spi0_read_data((void *)spl_image->load_addr,
304*4882a593Smuzhiyun 			       CONFIG_SYS_SPI_U_BOOT_OFFS, spl_image->size);
305*4882a593Smuzhiyun 	}
306*4882a593Smuzhiyun 
307*4882a593Smuzhiyun 	spi0_deinit();
308*4882a593Smuzhiyun 
309*4882a593Smuzhiyun 	return ret;
310*4882a593Smuzhiyun }
311*4882a593Smuzhiyun /* Use priorty 0 to override the default if it happens to be linked in */
312*4882a593Smuzhiyun SPL_LOAD_IMAGE_METHOD("sunxi SPI", 0, BOOT_DEVICE_SPI, spl_spi_load_image);
313