xref: /openbmc/u-boot/drivers/mtd/nand/raw/denali_spl.c (revision 592cd5defd4f71d34ffcbd8dd3326bc10f662e20)
1*a430fa06SMiquel Raynal // SPDX-License-Identifier: GPL-2.0+
2*a430fa06SMiquel Raynal /*
3*a430fa06SMiquel Raynal  * Copyright (C) 2014       Panasonic Corporation
4*a430fa06SMiquel Raynal  * Copyright (C) 2014-2015  Masahiro Yamada <yamada.masahiro@socionext.com>
5*a430fa06SMiquel Raynal  */
6*a430fa06SMiquel Raynal 
7*a430fa06SMiquel Raynal #include <common.h>
8*a430fa06SMiquel Raynal #include <asm/io.h>
9*a430fa06SMiquel Raynal #include <asm/unaligned.h>
10*a430fa06SMiquel Raynal #include <linux/mtd/rawnand.h>
11*a430fa06SMiquel Raynal #include "denali.h"
12*a430fa06SMiquel Raynal 
13*a430fa06SMiquel Raynal #define DENALI_MAP01		(1 << 26)	/* read/write pages in PIO */
14*a430fa06SMiquel Raynal #define DENALI_MAP10		(2 << 26)	/* high-level control plane */
15*a430fa06SMiquel Raynal 
16*a430fa06SMiquel Raynal #define INDEX_CTRL_REG		0x0
17*a430fa06SMiquel Raynal #define INDEX_DATA_REG		0x10
18*a430fa06SMiquel Raynal 
19*a430fa06SMiquel Raynal #define SPARE_ACCESS		0x41
20*a430fa06SMiquel Raynal #define MAIN_ACCESS		0x42
21*a430fa06SMiquel Raynal #define PIPELINE_ACCESS		0x2000
22*a430fa06SMiquel Raynal 
23*a430fa06SMiquel Raynal #define BANK(x) ((x) << 24)
24*a430fa06SMiquel Raynal 
25*a430fa06SMiquel Raynal static void __iomem *denali_flash_mem =
26*a430fa06SMiquel Raynal 			(void __iomem *)CONFIG_SYS_NAND_DATA_BASE;
27*a430fa06SMiquel Raynal static void __iomem *denali_flash_reg =
28*a430fa06SMiquel Raynal 			(void __iomem *)CONFIG_SYS_NAND_REGS_BASE;
29*a430fa06SMiquel Raynal 
30*a430fa06SMiquel Raynal static const int flash_bank;
31*a430fa06SMiquel Raynal static int page_size, oob_size, pages_per_block;
32*a430fa06SMiquel Raynal 
index_addr(uint32_t address,uint32_t data)33*a430fa06SMiquel Raynal static void index_addr(uint32_t address, uint32_t data)
34*a430fa06SMiquel Raynal {
35*a430fa06SMiquel Raynal 	writel(address, denali_flash_mem + INDEX_CTRL_REG);
36*a430fa06SMiquel Raynal 	writel(data, denali_flash_mem + INDEX_DATA_REG);
37*a430fa06SMiquel Raynal }
38*a430fa06SMiquel Raynal 
wait_for_irq(uint32_t irq_mask)39*a430fa06SMiquel Raynal static int wait_for_irq(uint32_t irq_mask)
40*a430fa06SMiquel Raynal {
41*a430fa06SMiquel Raynal 	unsigned long timeout = 1000000;
42*a430fa06SMiquel Raynal 	uint32_t intr_status;
43*a430fa06SMiquel Raynal 
44*a430fa06SMiquel Raynal 	do {
45*a430fa06SMiquel Raynal 		intr_status = readl(denali_flash_reg + INTR_STATUS(flash_bank));
46*a430fa06SMiquel Raynal 
47*a430fa06SMiquel Raynal 		if (intr_status & INTR__ECC_UNCOR_ERR) {
48*a430fa06SMiquel Raynal 			debug("Uncorrected ECC detected\n");
49*a430fa06SMiquel Raynal 			return -EBADMSG;
50*a430fa06SMiquel Raynal 		}
51*a430fa06SMiquel Raynal 
52*a430fa06SMiquel Raynal 		if (intr_status & irq_mask)
53*a430fa06SMiquel Raynal 			break;
54*a430fa06SMiquel Raynal 
55*a430fa06SMiquel Raynal 		udelay(1);
56*a430fa06SMiquel Raynal 		timeout--;
57*a430fa06SMiquel Raynal 	} while (timeout);
58*a430fa06SMiquel Raynal 
59*a430fa06SMiquel Raynal 	if (!timeout) {
60*a430fa06SMiquel Raynal 		debug("Timeout with interrupt status %08x\n", intr_status);
61*a430fa06SMiquel Raynal 		return -EIO;
62*a430fa06SMiquel Raynal 	}
63*a430fa06SMiquel Raynal 
64*a430fa06SMiquel Raynal 	return 0;
65*a430fa06SMiquel Raynal }
66*a430fa06SMiquel Raynal 
read_data_from_flash_mem(uint8_t * buf,int len)67*a430fa06SMiquel Raynal static void read_data_from_flash_mem(uint8_t *buf, int len)
68*a430fa06SMiquel Raynal {
69*a430fa06SMiquel Raynal 	int i;
70*a430fa06SMiquel Raynal 	uint32_t *buf32;
71*a430fa06SMiquel Raynal 
72*a430fa06SMiquel Raynal 	/* transfer the data from the flash */
73*a430fa06SMiquel Raynal 	buf32 = (uint32_t *)buf;
74*a430fa06SMiquel Raynal 
75*a430fa06SMiquel Raynal 	/*
76*a430fa06SMiquel Raynal 	 * Let's take care of unaligned access although it rarely happens.
77*a430fa06SMiquel Raynal 	 * Avoid put_unaligned() for the normal use cases since it leads to
78*a430fa06SMiquel Raynal 	 * a bit performance regression.
79*a430fa06SMiquel Raynal 	 */
80*a430fa06SMiquel Raynal 	if ((unsigned long)buf32 % 4) {
81*a430fa06SMiquel Raynal 		for (i = 0; i < len / 4; i++)
82*a430fa06SMiquel Raynal 			put_unaligned(readl(denali_flash_mem + INDEX_DATA_REG),
83*a430fa06SMiquel Raynal 				      buf32++);
84*a430fa06SMiquel Raynal 	} else {
85*a430fa06SMiquel Raynal 		for (i = 0; i < len / 4; i++)
86*a430fa06SMiquel Raynal 			*buf32++ = readl(denali_flash_mem + INDEX_DATA_REG);
87*a430fa06SMiquel Raynal 	}
88*a430fa06SMiquel Raynal 
89*a430fa06SMiquel Raynal 	if (len % 4) {
90*a430fa06SMiquel Raynal 		u32 tmp;
91*a430fa06SMiquel Raynal 
92*a430fa06SMiquel Raynal 		tmp = cpu_to_le32(readl(denali_flash_mem + INDEX_DATA_REG));
93*a430fa06SMiquel Raynal 		buf = (uint8_t *)buf32;
94*a430fa06SMiquel Raynal 		for (i = 0; i < len % 4; i++) {
95*a430fa06SMiquel Raynal 			*buf++ = tmp;
96*a430fa06SMiquel Raynal 			tmp >>= 8;
97*a430fa06SMiquel Raynal 		}
98*a430fa06SMiquel Raynal 	}
99*a430fa06SMiquel Raynal }
100*a430fa06SMiquel Raynal 
denali_send_pipeline_cmd(int page,int ecc_en,int access_type)101*a430fa06SMiquel Raynal int denali_send_pipeline_cmd(int page, int ecc_en, int access_type)
102*a430fa06SMiquel Raynal {
103*a430fa06SMiquel Raynal 	uint32_t addr, cmd;
104*a430fa06SMiquel Raynal 	static uint32_t page_count = 1;
105*a430fa06SMiquel Raynal 
106*a430fa06SMiquel Raynal 	writel(ecc_en, denali_flash_reg + ECC_ENABLE);
107*a430fa06SMiquel Raynal 
108*a430fa06SMiquel Raynal 	/* clear all bits of intr_status. */
109*a430fa06SMiquel Raynal 	writel(0xffff, denali_flash_reg + INTR_STATUS(flash_bank));
110*a430fa06SMiquel Raynal 
111*a430fa06SMiquel Raynal 	addr = BANK(flash_bank) | page;
112*a430fa06SMiquel Raynal 
113*a430fa06SMiquel Raynal 	/* setup the acccess type */
114*a430fa06SMiquel Raynal 	cmd = DENALI_MAP10 | addr;
115*a430fa06SMiquel Raynal 	index_addr(cmd, access_type);
116*a430fa06SMiquel Raynal 
117*a430fa06SMiquel Raynal 	/* setup the pipeline command */
118*a430fa06SMiquel Raynal 	index_addr(cmd, PIPELINE_ACCESS | page_count);
119*a430fa06SMiquel Raynal 
120*a430fa06SMiquel Raynal 	cmd = DENALI_MAP01 | addr;
121*a430fa06SMiquel Raynal 	writel(cmd, denali_flash_mem + INDEX_CTRL_REG);
122*a430fa06SMiquel Raynal 
123*a430fa06SMiquel Raynal 	return wait_for_irq(INTR__LOAD_COMP);
124*a430fa06SMiquel Raynal }
125*a430fa06SMiquel Raynal 
nand_read_oob(void * buf,int page)126*a430fa06SMiquel Raynal static int nand_read_oob(void *buf, int page)
127*a430fa06SMiquel Raynal {
128*a430fa06SMiquel Raynal 	int ret;
129*a430fa06SMiquel Raynal 
130*a430fa06SMiquel Raynal 	ret = denali_send_pipeline_cmd(page, 0, SPARE_ACCESS);
131*a430fa06SMiquel Raynal 	if (ret < 0)
132*a430fa06SMiquel Raynal 		return ret;
133*a430fa06SMiquel Raynal 
134*a430fa06SMiquel Raynal 	read_data_from_flash_mem(buf, oob_size);
135*a430fa06SMiquel Raynal 
136*a430fa06SMiquel Raynal 	return 0;
137*a430fa06SMiquel Raynal }
138*a430fa06SMiquel Raynal 
nand_read_page(void * buf,int page)139*a430fa06SMiquel Raynal static int nand_read_page(void *buf, int page)
140*a430fa06SMiquel Raynal {
141*a430fa06SMiquel Raynal 	int ret;
142*a430fa06SMiquel Raynal 
143*a430fa06SMiquel Raynal 	ret = denali_send_pipeline_cmd(page, 1, MAIN_ACCESS);
144*a430fa06SMiquel Raynal 	if (ret < 0)
145*a430fa06SMiquel Raynal 		return ret;
146*a430fa06SMiquel Raynal 
147*a430fa06SMiquel Raynal 	read_data_from_flash_mem(buf, page_size);
148*a430fa06SMiquel Raynal 
149*a430fa06SMiquel Raynal 	return 0;
150*a430fa06SMiquel Raynal }
151*a430fa06SMiquel Raynal 
nand_block_isbad(void * buf,int block)152*a430fa06SMiquel Raynal static int nand_block_isbad(void *buf, int block)
153*a430fa06SMiquel Raynal {
154*a430fa06SMiquel Raynal 	int ret;
155*a430fa06SMiquel Raynal 
156*a430fa06SMiquel Raynal 	ret = nand_read_oob(buf, block * pages_per_block);
157*a430fa06SMiquel Raynal 	if (ret < 0)
158*a430fa06SMiquel Raynal 		return ret;
159*a430fa06SMiquel Raynal 
160*a430fa06SMiquel Raynal 	return *((uint8_t *)buf + CONFIG_SYS_NAND_BAD_BLOCK_POS) != 0xff;
161*a430fa06SMiquel Raynal }
162*a430fa06SMiquel Raynal 
163*a430fa06SMiquel Raynal /* nand_init() - initialize data to make nand usable by SPL */
nand_init(void)164*a430fa06SMiquel Raynal void nand_init(void)
165*a430fa06SMiquel Raynal {
166*a430fa06SMiquel Raynal 	/* access to main area */
167*a430fa06SMiquel Raynal 	writel(0, denali_flash_reg + TRANSFER_SPARE_REG);
168*a430fa06SMiquel Raynal 
169*a430fa06SMiquel Raynal 	/*
170*a430fa06SMiquel Raynal 	 * These registers are expected to be already set by the hardware
171*a430fa06SMiquel Raynal 	 * or earlier boot code.  So we read these values out.
172*a430fa06SMiquel Raynal 	 */
173*a430fa06SMiquel Raynal 	page_size = readl(denali_flash_reg + DEVICE_MAIN_AREA_SIZE);
174*a430fa06SMiquel Raynal 	oob_size = readl(denali_flash_reg + DEVICE_SPARE_AREA_SIZE);
175*a430fa06SMiquel Raynal 	pages_per_block = readl(denali_flash_reg + PAGES_PER_BLOCK);
176*a430fa06SMiquel Raynal }
177*a430fa06SMiquel Raynal 
nand_spl_load_image(uint32_t offs,unsigned int size,void * dst)178*a430fa06SMiquel Raynal int nand_spl_load_image(uint32_t offs, unsigned int size, void *dst)
179*a430fa06SMiquel Raynal {
180*a430fa06SMiquel Raynal 	int block, page, column, readlen;
181*a430fa06SMiquel Raynal 	int ret;
182*a430fa06SMiquel Raynal 	int force_bad_block_check = 1;
183*a430fa06SMiquel Raynal 
184*a430fa06SMiquel Raynal 	page = offs / page_size;
185*a430fa06SMiquel Raynal 	column = offs % page_size;
186*a430fa06SMiquel Raynal 
187*a430fa06SMiquel Raynal 	block = page / pages_per_block;
188*a430fa06SMiquel Raynal 	page = page % pages_per_block;
189*a430fa06SMiquel Raynal 
190*a430fa06SMiquel Raynal 	while (size) {
191*a430fa06SMiquel Raynal 		if (force_bad_block_check || page == 0) {
192*a430fa06SMiquel Raynal 			ret = nand_block_isbad(dst, block);
193*a430fa06SMiquel Raynal 			if (ret < 0)
194*a430fa06SMiquel Raynal 				return ret;
195*a430fa06SMiquel Raynal 
196*a430fa06SMiquel Raynal 			if (ret) {
197*a430fa06SMiquel Raynal 				block++;
198*a430fa06SMiquel Raynal 				continue;
199*a430fa06SMiquel Raynal 			}
200*a430fa06SMiquel Raynal 		}
201*a430fa06SMiquel Raynal 
202*a430fa06SMiquel Raynal 		force_bad_block_check = 0;
203*a430fa06SMiquel Raynal 
204*a430fa06SMiquel Raynal 		ret = nand_read_page(dst, block * pages_per_block + page);
205*a430fa06SMiquel Raynal 		if (ret < 0)
206*a430fa06SMiquel Raynal 			return ret;
207*a430fa06SMiquel Raynal 
208*a430fa06SMiquel Raynal 		readlen = min(page_size - column, (int)size);
209*a430fa06SMiquel Raynal 
210*a430fa06SMiquel Raynal 		if (unlikely(column)) {
211*a430fa06SMiquel Raynal 			/* Partial page read */
212*a430fa06SMiquel Raynal 			memmove(dst, dst + column, readlen);
213*a430fa06SMiquel Raynal 			column = 0;
214*a430fa06SMiquel Raynal 		}
215*a430fa06SMiquel Raynal 
216*a430fa06SMiquel Raynal 		size -= readlen;
217*a430fa06SMiquel Raynal 		dst += readlen;
218*a430fa06SMiquel Raynal 		page++;
219*a430fa06SMiquel Raynal 		if (page == pages_per_block) {
220*a430fa06SMiquel Raynal 			block++;
221*a430fa06SMiquel Raynal 			page = 0;
222*a430fa06SMiquel Raynal 		}
223*a430fa06SMiquel Raynal 	}
224*a430fa06SMiquel Raynal 
225*a430fa06SMiquel Raynal 	return 0;
226*a430fa06SMiquel Raynal }
227*a430fa06SMiquel Raynal 
nand_deselect(void)228*a430fa06SMiquel Raynal void nand_deselect(void) {}
229