xref: /inferno-os/os/port/flashnand.c (revision 4eb166cf184c1f102fb79e31b1465ea3e2021c39)
1 #include	"u.h"
2 #include	"../port/lib.h"
3 #include	"mem.h"
4 #include	"dat.h"
5 #include	"fns.h"
6 #include	"io.h"
7 #include	"../port/error.h"
8 
9 #include	"flashif.h"
10 
11 typedef struct Nandtab Nandtab;
12 
13 struct Nandtab {
14 	short manufacturer;
15 	uchar id;
16 	uchar l2bytesperpage;
17 	ushort pagesperblock;
18 	ushort blocks;
19 	uchar tPROGms;
20 	ushort tBERASEms;
21 	uchar tRus;
22 };
23 
24 static Nandtab nandtab[] = {
25 	{ 0xec, 	0xe6,	9,		16,		1024,	1,		4,		7 },	/* Samsung KM29U64000T */
26 
27 	{ 0x98,	0xe6,	9,		16,		1024,	1,		4,		25 }, /* Toshiba TC58V64AFT */
28 	{ 0x98,	0x73,	9,		32,		1024,	1,		10,		25},	/* Toshiba TC56V128AFT */
29 	/* Generic entries which take timings from Toshiba SMIL example code */
30 	{ -1,		0xea,	8,		16,		512,		20,		400,		100	 },
31 	{ -1,		0xe3,	9,		16,		512,		20,		400,		100	 },
32 	{ -1,		0xe5,	9,		16,		512,		20,		400,		100	 },
33 	{ -1,		0x73,	9,		32,		1024,	20,		400,		100	 },
34 	{ -1,		0x75,	9,		32,		2048,	20,		400,		100	 },
35 	{ -1,		0x76,	9,		32,		4096,	20,		400,		100	 },
36 };
37 
38 enum {
39 	ReadMode1 = 0x00,
40 	ReadMode2 = 0x01,
41 	Program = 0x10,
42 	ReadMode3 = 0x50,
43 	Erase1 = 0x60,
44 	ReadStatus = 0x70,
45 	Write = 0x80,
46 	Identify = 0x90,
47 	Erase2 = 0xd0,
48 
49 	StatusReady = 0x40,
50 	StatusFail = 0x01,
51 };
52 
53 /*
54  * NAND flash driver
55  */
56 
57 #define	DPRINT	if(0)print
58 #define	EPRINT	if(1)print
59 
60 static int idchip(Flash *f);
61 
62 static void
63 nand_writebyte(Flash *f, uchar b)
64 {
65 	archnand_write(f, &b, 1);
66 }
67 
68 static uchar
69 nand_readbyte(Flash *f)
70 {
71 	uchar b;
72 	archnand_read(f, &b, 1);
73 	return b;
74 }
75 
76 static int
77 idchip(Flash *f)
78 {
79 	int x;
80 	uchar maker, device;
81 
82 	f->id = 0;
83 	f->devid = 0;
84 	f->width = 1;
85 	archnand_claim(f, 1);
86 	archnand_setCLEandALE(f, 1, 0);
87 	nand_writebyte(f, Identify);
88 	archnand_setCLEandALE(f, 0, 1);
89 	nand_writebyte(f, 0);
90 	archnand_setCLEandALE(f, 0, 0);
91 	maker = nand_readbyte(f);
92 	device = nand_readbyte(f);
93 	archnand_claim(f, 0);
94 	iprint("man=%#ux device=%#ux\n", maker, device);
95 	for(x = 0; x < sizeof(nandtab) / sizeof(nandtab[0]); x++){
96 		if(nandtab[x].id == (device & 0xff)
97 			&& (nandtab[x].manufacturer == maker || nandtab[x].manufacturer == -1)){
98 			ulong bpp;
99 			f->id = maker;
100 			f->devid = device;
101 			f->nr = 1;
102 			bpp = 1 << nandtab[x].l2bytesperpage;
103 			bpp |= bpp >> 5;
104 			f->regions[0].erasesize = bpp * nandtab[x].pagesperblock;
105 			f->size = f->regions[0].erasesize * nandtab[x].blocks;
106 			f->regions[0].n = nandtab[x].blocks;
107 			f->regions[0].start = 0;
108 			f->regions[0].end = f->size;
109 			f->regions[0].pagesize = bpp;
110 			f->data = &nandtab[x];
111 			return 0;
112 		}
113 	}
114 	print("nand: device %#.2ux/%#.2ux not recognised\n", maker, device);
115 	return -1;
116 }
117 
118 static int
119 erasezone(Flash *f, Flashregion *r, ulong byteaddr)
120 {
121 	Nandtab *nt = f->data;
122 	int paddress;
123 	uchar val;
124 	int rv;
125 	uchar addr[2];
126 
127 	if(byteaddr%r->erasesize || byteaddr >= f->size)
128 		return -1;	/* bad zone */
129 	paddress = byteaddr/r->erasesize * nt->pagesperblock;	/* can simplify ... */
130 //print("erasezone(%.8lux) page %d %.8lux\n", byteaddr, paddress, r->erasesize);
131 	archnand_claim(f, 1);
132 	archnand_setCLEandALE(f, 1, 0);		// command mode
133 	nand_writebyte(f, Erase1);
134 	archnand_setCLEandALE(f, 0, 1);		// address mode
135 	addr[0] = paddress;
136 	addr[1] = paddress >> 8;
137 	archnand_write(f, addr, 2);
138 	archnand_setCLEandALE(f, 1, 0);		// command mode
139 	nand_writebyte(f, Erase2);
140 	nand_writebyte(f, ReadStatus);
141 	archnand_setCLEandALE(f, 0, 0);		// data mode
142 
143 	do {
144 		val = nand_readbyte(f);
145 	} while((val & StatusReady) != StatusReady);
146 
147 	if((val & StatusFail) != 0){
148 		print("erasezone failed: %.2ux\n", val);
149 		rv = -1;
150 	}
151 	else
152 		rv = 0;
153 	archnand_claim(f, 0);				// turn off chip
154 	return rv;
155 }
156 
157 static int
158 writepage(Flash *f, ulong page, ushort addr, void *buf, long n)
159 {
160 	uchar cmd;
161 	uchar val;
162 	int rv;
163 	uchar cmdbuf[3];
164 
165 //print("writepage(%ld, %d, %ld)\n", page, addr, n);
166 	// Fake a read to set the pointer
167 	if(addr < 256)
168 		cmd = ReadMode1;
169 	else if(addr < 512){
170 		cmd = ReadMode2;
171 		addr -= 256;
172 	}else{
173 		cmd = ReadMode3;
174 		addr -= 512;
175 	}
176 	archnand_claim(f, 1);
177 	archnand_setCLEandALE(f, 1, 0);		// command mode
178 	nand_writebyte(f, cmd);
179 	nand_writebyte(f, Write);
180 	archnand_setCLEandALE(f, 0, 1);		// address mode
181 	cmdbuf[0] = addr;
182 	cmdbuf[1] = page;
183 	cmdbuf[2] = page >> 8;
184 	archnand_write(f, cmdbuf, 3);
185 	archnand_setCLEandALE(f, 0, 0);		// data mode
186 	archnand_write(f, buf, n);
187 	archnand_setCLEandALE(f, 1, 0);		// command mode
188 	nand_writebyte(f, Program);
189 	nand_writebyte(f, ReadStatus);
190 	archnand_setCLEandALE(f, 0, 0);		// data mode
191 
192 	do {
193 		val = nand_readbyte(f);
194 	}while((val & StatusReady) != StatusReady);
195 
196 	if((val & StatusFail) != 0){
197 		print("writepage failed: %.2ux\n", val);
198 		rv = -1;
199 	}else
200 		rv = 0;
201 
202 	archnand_claim(f, 0);
203 	return rv;
204 }
205 
206 static int
207 write(Flash *f, ulong offset, void *buf, long n)
208 {
209 	Nandtab *nt = f->data;
210 	ulong page;
211 	ulong addr;
212 	ulong xbpp;
213 
214 //print("write(%ld, %ld)\n", offset, n);
215 
216 	xbpp = (1 << nt->l2bytesperpage);
217 	xbpp |= (xbpp >> 5);
218 	page = offset / xbpp;
219 	addr = offset % xbpp;
220 
221 	while(n > 0){
222 		int count;
223 		count = xbpp - addr;
224 		if(count > n)
225 			count = n;
226 		if(writepage(f, page, addr, buf, count) < 0)
227 			return -1;
228 		offset += count;
229 		n -= count;
230 		buf = (uchar *)buf + count;
231 		addr = 0;
232 	}
233 //print("write done\n");
234 	return 0;
235 }
236 
237 static int
238 read(Flash *f, ulong offset, void *buf, long n)
239 {
240 	Nandtab *nt = f->data;
241 	uchar cmd;
242 	ulong page;
243 	ulong addr;
244 	ushort bytesperpage, xbytesperpage, skip, partialaddr;
245 	uchar cmdbuf[3];
246 	int toread;
247 
248 //print("read(%ld, %.8lux, %ld)\n", offset, buf, n);
249 
250 	bytesperpage = (1 << nt->l2bytesperpage);
251 	xbytesperpage = bytesperpage;
252 	xbytesperpage += bytesperpage >> 5;	// 512 => 16, 256 => 8
253 	page = offset / xbytesperpage;
254 	partialaddr = offset % xbytesperpage;
255 	skip = 0;
256 	if(partialaddr >= bytesperpage && xbytesperpage - partialaddr < n){
257 		// cannot start read in extended area, and then chain into main area,
258 		// so start on last byte of main area, and skip the extra bytes
259 		// stupid chip design this one
260 		skip = partialaddr - bytesperpage + 1;
261 		n += skip;
262 		partialaddr = bytesperpage - 1;
263 	}
264 	addr = partialaddr;
265 	if(addr >= bytesperpage){
266 		cmd = ReadMode3;
267 		addr -= bytesperpage;
268 	}else if(addr >= 256){
269 		cmd = ReadMode2;
270 		addr -= 256;
271 	}else
272 		cmd = ReadMode1;
273 
274 //print("cmd %.2x page %.4lux addr %.8lux partialaddr %d skip %d\n", cmd, page, addr, partialaddr, skip);
275 	// Read first page
276 	archnand_claim(f, 1);
277 	archnand_setCLEandALE(f, 1, 0);
278 	nand_writebyte(f, cmd);
279 	archnand_setCLEandALE(f, 0, 1);
280 	cmdbuf[0] = addr;
281 	cmdbuf[1] = page;
282 	cmdbuf[2] = page >> 8;
283 	archnand_write(f, cmdbuf, 3);
284 	archnand_setCLEandALE(f, 0, 0);
285 	if(partialaddr){
286 		// partial first page
287 		microdelay(nt->tRus);
288 		toread = partialaddr < xbytesperpage ? xbytesperpage - partialaddr : 0;
289 		if(toread > n)
290 			toread = n;
291 		if(skip){
292 			archnand_read(f, 0, skip);
293 			toread -= skip;
294 			n -= skip;
295 //			partialaddr += skip;
296 		}
297 		archnand_read(f, buf, toread);
298 		n -= toread;
299 //		partialaddr += toread;
300 		buf = (uchar *)buf + toread;
301 	}
302 	while(n){
303 		microdelay(nt->tRus);
304 		toread = xbytesperpage;
305 		if(n < toread)
306 			toread = n;
307 		archnand_read(f, buf, toread);
308 		n -= toread;
309 		buf = (uchar *)buf + toread;
310 	}
311 	archnand_claim(f, 0);
312 //print("readn done\n");
313 	return 0;
314 }
315 
316 static int
317 reset(Flash *f)
318 {
319 //iprint("nandreset\n");
320 	if(f->data != nil)
321 		return 1;
322 	f->write = write;
323  	f->read = read;
324 	f->eraseall = nil;
325 	f->erasezone = erasezone;
326 	f->suspend = nil;
327 	f->resume = nil;
328 	f->sort = "nand";
329 	archnand_init(f);
330 	return idchip(f);
331 }
332 
333 void
334 flashnandlink(void)
335 {
336 	addflashcard("nand", reset);
337 }
338