summaryrefslogtreecommitdiffstats
path: root/drivers/mtd/nand/jz4740_nand.c
blob: 7a62cc33613c1f9e4eaac33da19e2b8c27bd74a3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
/*
 * Platform independend driver for JZ4740.
 *
 * Copyright (c) 2007 Ingenic Semiconductor Inc.
 * Author: <jlwei@ingenic.cn>
 *
 * SPDX-License-Identifier:	GPL-2.0+
 */
#include <common.h>

#include <nand.h>
#include <asm/io.h>
#include <asm/jz4740.h>

#define JZ_NAND_DATA_ADDR ((void __iomem *)0xB8000000)
#define JZ_NAND_CMD_ADDR (JZ_NAND_DATA_ADDR + 0x8000)
#define JZ_NAND_ADDR_ADDR (JZ_NAND_DATA_ADDR + 0x10000)

#define BIT(x) (1 << (x))
#define JZ_NAND_ECC_CTRL_ENCODING	BIT(3)
#define JZ_NAND_ECC_CTRL_RS		BIT(2)
#define JZ_NAND_ECC_CTRL_RESET		BIT(1)
#define JZ_NAND_ECC_CTRL_ENABLE		BIT(0)

#define EMC_SMCR1_OPT_NAND	0x094c4400
/* Optimize the timing of nand */

static struct jz4740_emc * emc = (struct jz4740_emc *)JZ4740_EMC_BASE;

static struct nand_ecclayout qi_lb60_ecclayout_2gb = {
	.eccbytes = 72,
	.eccpos = {
		12, 13, 14, 15, 16, 17, 18, 19,
		20, 21, 22, 23, 24, 25, 26, 27,
		28, 29, 30, 31, 32, 33, 34, 35,
		36, 37, 38, 39, 40, 41, 42, 43,
		44, 45, 46, 47, 48, 49, 50, 51,
		52, 53, 54, 55, 56, 57, 58, 59,
		60, 61, 62, 63, 64, 65, 66, 67,
		68, 69, 70, 71, 72, 73, 74, 75,
		76, 77, 78, 79, 80, 81, 82, 83 },
	.oobfree = {
		{.offset = 2,
		 .length = 10 },
		{.offset = 84,
		 .length = 44 } }
};

static int is_reading;

static void jz_nand_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl)
{
	struct nand_chip *this = mtd->priv;
	uint32_t reg;

	if (ctrl & NAND_CTRL_CHANGE) {
		if (ctrl & NAND_ALE)
			this->IO_ADDR_W = JZ_NAND_ADDR_ADDR;
		else if (ctrl & NAND_CLE)
			this->IO_ADDR_W = JZ_NAND_CMD_ADDR;
		else
			this->IO_ADDR_W = JZ_NAND_DATA_ADDR;

		reg = readl(&emc->nfcsr);
		if (ctrl & NAND_NCE)
			reg |= EMC_NFCSR_NFCE1;
		else
			reg &= ~EMC_NFCSR_NFCE1;
		writel(reg, &emc->nfcsr);
	}

	if (cmd != NAND_CMD_NONE)
		writeb(cmd, this->IO_ADDR_W);
}

static int jz_nand_device_ready(struct mtd_info *mtd)
{
	return (readl(GPIO_PXPIN(2)) & 0x40000000) ? 1 : 0;
}

void board_nand_select_device(struct nand_chip *nand, int chip)
{
	/*
	 * Don't use "chip" to address the NAND device,
	 * generate the cs from the address where it is encoded.
	 */
}

static int jz_nand_rs_calculate_ecc(struct mtd_info *mtd, const u_char *dat,
				u_char *ecc_code)
{
	uint32_t status;
	int i;

	if (is_reading)
		return 0;

	do {
		status = readl(&emc->nfints);
	} while (!(status & EMC_NFINTS_ENCF));

	/* disable ecc */
	writel(readl(&emc->nfecr) & ~EMC_NFECR_ECCE, &emc->nfecr);

	for (i = 0; i < 9; i++)
		ecc_code[i] = readb(&emc->nfpar[i]);

	return 0;
}

static void jz_nand_hwctl(struct mtd_info *mtd, int mode)
{
	uint32_t reg;

	writel(0, &emc->nfints);
	reg = readl(&emc->nfecr);
	reg |= JZ_NAND_ECC_CTRL_RESET;
	reg |= JZ_NAND_ECC_CTRL_ENABLE;
	reg |= JZ_NAND_ECC_CTRL_RS;

	switch (mode) {
	case NAND_ECC_READ:
		reg &= ~JZ_NAND_ECC_CTRL_ENCODING;
		is_reading = 1;
		break;
	case NAND_ECC_WRITE:
		reg |= JZ_NAND_ECC_CTRL_ENCODING;
		is_reading = 0;
		break;
	default:
		break;
	}

	writel(reg, &emc->nfecr);
}

/* Correct 1~9-bit errors in 512-bytes data */
static void jz_rs_correct(unsigned char *dat, int idx, int mask)
{
	int i;

	idx--;

	i = idx + (idx >> 3);
	if (i >= 512)
		return;

	mask <<= (idx & 0x7);

	dat[i] ^= mask & 0xff;
	if (i < 511)
		dat[i + 1] ^= (mask >> 8) & 0xff;
}

static int jz_nand_rs_correct_data(struct mtd_info *mtd, u_char *dat,
				   u_char *read_ecc, u_char *calc_ecc)
{
	int k;
	uint32_t errcnt, index, mask, status;

	/* Set PAR values */
	const uint8_t all_ff_ecc[] = {
		0xcd, 0x9d, 0x90, 0x58, 0xf4, 0x8b, 0xff, 0xb7, 0x6f };

	if (read_ecc[0] == 0xff && read_ecc[1] == 0xff &&
	    read_ecc[2] == 0xff && read_ecc[3] == 0xff &&
	    read_ecc[4] == 0xff && read_ecc[5] == 0xff &&
	    read_ecc[6] == 0xff && read_ecc[7] == 0xff &&
	    read_ecc[8] == 0xff) {
		for (k = 0; k < 9; k++)
			writeb(all_ff_ecc[k], &emc->nfpar[k]);
	} else {
		for (k = 0; k < 9; k++)
			writeb(read_ecc[k], &emc->nfpar[k]);
	}
	/* Set PRDY */
	writel(readl(&emc->nfecr) | EMC_NFECR_PRDY, &emc->nfecr);

	/* Wait for completion */
	do {
		status = readl(&emc->nfints);
	} while (!(status & EMC_NFINTS_DECF));

	/* disable ecc */
	writel(readl(&emc->nfecr) & ~EMC_NFECR_ECCE, &emc->nfecr);

	/* Check decoding */
	if (!(status & EMC_NFINTS_ERR))
		return 0;

	if (status & EMC_NFINTS_UNCOR) {
		printf("uncorrectable ecc\n");
		return -1;
	}

	errcnt = (status & EMC_NFINTS_ERRCNT_MASK) >> EMC_NFINTS_ERRCNT_BIT;

	switch (errcnt) {
	case 4:
		index = (readl(&emc->nferr[3]) & EMC_NFERR_INDEX_MASK) >>
			EMC_NFERR_INDEX_BIT;
		mask = (readl(&emc->nferr[3]) & EMC_NFERR_MASK_MASK) >>
			EMC_NFERR_MASK_BIT;
		jz_rs_correct(dat, index, mask);
	case 3:
		index = (readl(&emc->nferr[2]) & EMC_NFERR_INDEX_MASK) >>
			EMC_NFERR_INDEX_BIT;
		mask = (readl(&emc->nferr[2]) & EMC_NFERR_MASK_MASK) >>
			EMC_NFERR_MASK_BIT;
		jz_rs_correct(dat, index, mask);
	case 2:
		index = (readl(&emc->nferr[1]) & EMC_NFERR_INDEX_MASK) >>
			EMC_NFERR_INDEX_BIT;
		mask = (readl(&emc->nferr[1]) & EMC_NFERR_MASK_MASK) >>
			EMC_NFERR_MASK_BIT;
		jz_rs_correct(dat, index, mask);
	case 1:
		index = (readl(&emc->nferr[0]) & EMC_NFERR_INDEX_MASK) >>
			EMC_NFERR_INDEX_BIT;
		mask = (readl(&emc->nferr[0]) & EMC_NFERR_MASK_MASK) >>
			EMC_NFERR_MASK_BIT;
		jz_rs_correct(dat, index, mask);
	default:
		break;
	}

	return errcnt;
}

/*
 * Main initialization routine
 */
int board_nand_init(struct nand_chip *nand)
{
	uint32_t reg;

	reg = readl(&emc->nfcsr);
	reg |= EMC_NFCSR_NFE1;	/* EMC setup, Set NFE bit */
	writel(reg, &emc->nfcsr);

	writel(EMC_SMCR1_OPT_NAND, &emc->smcr[1]);

	nand->IO_ADDR_R		= JZ_NAND_DATA_ADDR;
	nand->IO_ADDR_W		= JZ_NAND_DATA_ADDR;
	nand->cmd_ctrl		= jz_nand_cmd_ctrl;
	nand->dev_ready		= jz_nand_device_ready;
	nand->ecc.hwctl		= jz_nand_hwctl;
	nand->ecc.correct	= jz_nand_rs_correct_data;
	nand->ecc.calculate	= jz_nand_rs_calculate_ecc;
	nand->ecc.mode		= NAND_ECC_HW_OOB_FIRST;
	nand->ecc.size		= CONFIG_SYS_NAND_ECCSIZE;
	nand->ecc.bytes		= CONFIG_SYS_NAND_ECCBYTES;
	nand->ecc.strength	= 4;
	nand->ecc.layout	= &qi_lb60_ecclayout_2gb;
	nand->chip_delay	= 50;
	nand->bbt_options	|= NAND_BBT_USE_FLASH;

	return 0;
}
OpenPOWER on IntegriCloud