1*10465441SEvalZero /*
2*10465441SEvalZero * Copyright (c) 2006-2018, RT-Thread Development Team
3*10465441SEvalZero *
4*10465441SEvalZero * SPDX-License-Identifier: Apache-2.0
5*10465441SEvalZero *
6*10465441SEvalZero * Change Logs:
7*10465441SEvalZero * Date Author Notes
8*10465441SEvalZero * 2018-09-10 heyuanjie87 first version
9*10465441SEvalZero */
10*10465441SEvalZero
11*10465441SEvalZero #include <rtdevice.h>
12*10465441SEvalZero
13*10465441SEvalZero #define MTDTONAND(x) ((rt_nand_t*)(x))
14*10465441SEvalZero #define NOTALIGNED(x) ((x & (chip->page_size - 1)) != 0)
15*10465441SEvalZero #ifndef min
16*10465441SEvalZero #define min(a,b) (a>b? b:a)
17*10465441SEvalZero #endif
18*10465441SEvalZero
nand_fill_oob(rt_nand_t * chip,uint8_t * oob,size_t len,struct mtd_io_desc * desc)19*10465441SEvalZero static uint8_t *nand_fill_oob(rt_nand_t *chip, uint8_t *oob, size_t len, struct mtd_io_desc *desc)
20*10465441SEvalZero {
21*10465441SEvalZero rt_memset(chip->oob_poi, 0xff, chip->oobsize);
22*10465441SEvalZero
23*10465441SEvalZero switch (desc->mode)
24*10465441SEvalZero {
25*10465441SEvalZero case MTD_OPM_PLACE_OOB:
26*10465441SEvalZero case MTD_OPM_RAW:
27*10465441SEvalZero rt_memcpy(chip->oob_poi + desc->ooboffs, oob, len);
28*10465441SEvalZero return oob + len;
29*10465441SEvalZero
30*10465441SEvalZero case MTD_OPM_AUTO_OOB:
31*10465441SEvalZero {
32*10465441SEvalZero const struct mtd_oob_region *free = chip->freelayout;
33*10465441SEvalZero uint32_t boffs;
34*10465441SEvalZero size_t bytes;
35*10465441SEvalZero
36*10465441SEvalZero bytes = min(len, free->length);
37*10465441SEvalZero boffs = free->offset;
38*10465441SEvalZero
39*10465441SEvalZero rt_memcpy(chip->oob_poi + boffs, oob, bytes);
40*10465441SEvalZero oob += bytes;
41*10465441SEvalZero
42*10465441SEvalZero return oob;
43*10465441SEvalZero }
44*10465441SEvalZero }
45*10465441SEvalZero
46*10465441SEvalZero return NULL;
47*10465441SEvalZero }
48*10465441SEvalZero
nand_transfer_oob(rt_nand_t * chip,uint8_t * oob,struct mtd_io_desc * desc,size_t len)49*10465441SEvalZero static uint8_t *nand_transfer_oob(rt_nand_t *chip, uint8_t *oob, struct mtd_io_desc *desc, size_t len)
50*10465441SEvalZero {
51*10465441SEvalZero switch (desc->mode)
52*10465441SEvalZero {
53*10465441SEvalZero case MTD_OPM_PLACE_OOB:
54*10465441SEvalZero case MTD_OPM_RAW:
55*10465441SEvalZero rt_memcpy(oob, chip->oob_poi + desc->ooboffs, len);
56*10465441SEvalZero return oob + len;
57*10465441SEvalZero
58*10465441SEvalZero case MTD_OPM_AUTO_OOB:
59*10465441SEvalZero {
60*10465441SEvalZero struct mtd_oob_region *free = (struct mtd_oob_region *)chip->freelayout;
61*10465441SEvalZero uint32_t boffs = 0, roffs = desc->ooboffs;
62*10465441SEvalZero size_t bytes = 0;
63*10465441SEvalZero
64*10465441SEvalZero for (; free->length && len; free++, len -= bytes)
65*10465441SEvalZero {
66*10465441SEvalZero /* Read request not from offset 0? */
67*10465441SEvalZero if (roffs)
68*10465441SEvalZero {
69*10465441SEvalZero if (roffs >= free->length)
70*10465441SEvalZero {
71*10465441SEvalZero roffs -= free->length;
72*10465441SEvalZero continue;
73*10465441SEvalZero }
74*10465441SEvalZero boffs = free->offset + roffs;
75*10465441SEvalZero bytes = min(len, (free->length - roffs));
76*10465441SEvalZero roffs = 0;
77*10465441SEvalZero }
78*10465441SEvalZero else
79*10465441SEvalZero {
80*10465441SEvalZero bytes = min(len, free->length);
81*10465441SEvalZero boffs = free->offset;
82*10465441SEvalZero }
83*10465441SEvalZero
84*10465441SEvalZero rt_memcpy(oob, chip->oob_poi + boffs, bytes);
85*10465441SEvalZero oob += bytes;
86*10465441SEvalZero }
87*10465441SEvalZero
88*10465441SEvalZero return oob;
89*10465441SEvalZero }
90*10465441SEvalZero }
91*10465441SEvalZero
92*10465441SEvalZero return NULL;
93*10465441SEvalZero }
94*10465441SEvalZero
nand_read_page_raw(rt_nand_t * chip,uint8_t * buf,int oob_required,int page)95*10465441SEvalZero static int nand_read_page_raw(rt_nand_t *chip, uint8_t *buf, int oob_required, int page)
96*10465441SEvalZero {
97*10465441SEvalZero chip->ops->read_buf(chip, buf, chip->page_size);
98*10465441SEvalZero
99*10465441SEvalZero if (oob_required)
100*10465441SEvalZero chip->ops->read_buf(chip, chip->oob_poi, chip->oobsize);
101*10465441SEvalZero
102*10465441SEvalZero return 0;
103*10465441SEvalZero }
104*10465441SEvalZero
nand_write_page_raw(rt_nand_t * chip,const uint8_t * buf,int oob_required,int page)105*10465441SEvalZero static int nand_write_page_raw(rt_nand_t *chip, const uint8_t *buf, int oob_required, int page)
106*10465441SEvalZero {
107*10465441SEvalZero chip->ops->write_buf(chip, buf, chip->page_size);
108*10465441SEvalZero
109*10465441SEvalZero if (oob_required)
110*10465441SEvalZero chip->ops->write_buf(chip, chip->oob_poi, chip->oobsize);
111*10465441SEvalZero
112*10465441SEvalZero return 0;
113*10465441SEvalZero }
114*10465441SEvalZero
nand_write_page_hwecc(rt_nand_t * chip,const uint8_t * buf,int oob_required,int page)115*10465441SEvalZero static int nand_write_page_hwecc(rt_nand_t *chip, const uint8_t *buf, int oob_required, int page)
116*10465441SEvalZero {
117*10465441SEvalZero uint16_t i;
118*10465441SEvalZero uint16_t stepsize = chip->ecc.stepsize;
119*10465441SEvalZero uint16_t eccbytes = chip->ecc.bytes;
120*10465441SEvalZero uint16_t eccsteps = chip->ecc._step;
121*10465441SEvalZero uint16_t eccpos = chip->ecc.layout->offset;
122*10465441SEvalZero uint8_t *ecc_calc = chip->buffers.ecccalc;
123*10465441SEvalZero const uint8_t *p = buf;
124*10465441SEvalZero
125*10465441SEvalZero for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += stepsize)
126*10465441SEvalZero {
127*10465441SEvalZero chip->ops->cmdfunc(chip, NAND_CMD_ECC_EN, 0, 0);
128*10465441SEvalZero chip->ops->write_buf(chip, p, stepsize);
129*10465441SEvalZero chip->ecc.calculate(chip, p, &ecc_calc[i]);
130*10465441SEvalZero chip->ops->cmdfunc(chip, NAND_CMD_ECC_DIS, 0, 0);
131*10465441SEvalZero }
132*10465441SEvalZero
133*10465441SEvalZero rt_memcpy(&chip->oob_poi[eccpos], ecc_calc, chip->ecc.layout->length);
134*10465441SEvalZero
135*10465441SEvalZero chip->ops->write_buf(chip, chip->oob_poi, chip->oobsize);
136*10465441SEvalZero
137*10465441SEvalZero return 0;
138*10465441SEvalZero }
139*10465441SEvalZero
nand_read_page_hwecc(rt_nand_t * chip,uint8_t * buf,int oob_required,int page)140*10465441SEvalZero static int nand_read_page_hwecc(rt_nand_t *chip, uint8_t *buf, int oob_required, int page)
141*10465441SEvalZero {
142*10465441SEvalZero uint16_t i;
143*10465441SEvalZero uint16_t eccsize = chip->ecc.stepsize;
144*10465441SEvalZero uint16_t eccbytes = chip->ecc.bytes;
145*10465441SEvalZero uint16_t eccsteps = chip->ecc._step;
146*10465441SEvalZero uint16_t eccpos = chip->ecc.layout->offset;
147*10465441SEvalZero uint8_t *p = buf;
148*10465441SEvalZero uint8_t *ecc_calc = chip->buffers.ecccalc;
149*10465441SEvalZero uint8_t *ecc_code = chip->buffers.ecccode;
150*10465441SEvalZero int ret = 0;
151*10465441SEvalZero
152*10465441SEvalZero for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize)
153*10465441SEvalZero {
154*10465441SEvalZero chip->ops->cmdfunc(chip, NAND_CMD_ECC_EN, 0, 0);
155*10465441SEvalZero chip->ops->read_buf(chip, p, eccsize);
156*10465441SEvalZero chip->ecc.calculate(chip, p, &ecc_calc[i]);
157*10465441SEvalZero chip->ops->cmdfunc(chip, NAND_CMD_ECC_DIS, 0, 0);
158*10465441SEvalZero }
159*10465441SEvalZero
160*10465441SEvalZero chip->ops->read_buf(chip, chip->oob_poi, chip->oobsize);
161*10465441SEvalZero rt_memcpy(ecc_code, &chip->oob_poi[eccpos], chip->ecc.layout->length);
162*10465441SEvalZero
163*10465441SEvalZero eccsteps = chip->ecc._step;
164*10465441SEvalZero p = buf;
165*10465441SEvalZero
166*10465441SEvalZero for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize)
167*10465441SEvalZero {
168*10465441SEvalZero int stat;
169*10465441SEvalZero
170*10465441SEvalZero stat = chip->ecc.correct(chip, p, &ecc_code[i], &ecc_calc[i]);
171*10465441SEvalZero if (stat != 0)
172*10465441SEvalZero ret = -1;
173*10465441SEvalZero }
174*10465441SEvalZero
175*10465441SEvalZero return ret;
176*10465441SEvalZero }
177*10465441SEvalZero
nand_write_page(rt_nand_t * chip,const uint8_t * buf,int oob_required,int page,int raw)178*10465441SEvalZero static int nand_write_page(rt_nand_t *chip, const uint8_t *buf,
179*10465441SEvalZero int oob_required, int page, int raw)
180*10465441SEvalZero {
181*10465441SEvalZero int status;
182*10465441SEvalZero
183*10465441SEvalZero chip->ops->cmdfunc(chip, NAND_CMD_PAGE_WR0, page, 0x00);
184*10465441SEvalZero
185*10465441SEvalZero if (raw)
186*10465441SEvalZero {
187*10465441SEvalZero nand_write_page_raw(chip, buf, oob_required, page);
188*10465441SEvalZero }
189*10465441SEvalZero else
190*10465441SEvalZero {
191*10465441SEvalZero chip->write_page(chip, buf, oob_required, page);
192*10465441SEvalZero }
193*10465441SEvalZero
194*10465441SEvalZero status = chip->ops->cmdfunc(chip, NAND_CMD_PAGE_WR1, -1, -1);
195*10465441SEvalZero
196*10465441SEvalZero return status;
197*10465441SEvalZero }
198*10465441SEvalZero
nand_do_read_desc(rt_nand_t * chip,loff_t from,struct mtd_io_desc * desc)199*10465441SEvalZero static int nand_do_read_desc(rt_nand_t *chip, loff_t from, struct mtd_io_desc *desc)
200*10465441SEvalZero {
201*10465441SEvalZero int page, bytes;
202*10465441SEvalZero char oob_required;
203*10465441SEvalZero char ecc_fail = 0;
204*10465441SEvalZero int ret = 0;
205*10465441SEvalZero uint32_t readlen = desc->datlen;
206*10465441SEvalZero uint16_t oobreadlen = desc->ooblen;
207*10465441SEvalZero uint16_t max_oobsize = desc->mode == MTD_OPM_AUTO_OOB ?
208*10465441SEvalZero chip->freelayout->length : chip->oobsize;
209*10465441SEvalZero
210*10465441SEvalZero uint8_t *oob, *buf, *notalign = 0;
211*10465441SEvalZero
212*10465441SEvalZero /* Reject reads, which are not page aligned */
213*10465441SEvalZero if (NOTALIGNED(from))
214*10465441SEvalZero {
215*10465441SEvalZero return -EINVAL;
216*10465441SEvalZero }
217*10465441SEvalZero
218*10465441SEvalZero buf = desc->datbuf;
219*10465441SEvalZero if (NOTALIGNED(desc->datlen) && !chip->pagebuf)
220*10465441SEvalZero {
221*10465441SEvalZero chip->pagebuf = rt_malloc(chip->page_size);
222*10465441SEvalZero if (!chip->pagebuf)
223*10465441SEvalZero return -ENOMEM;
224*10465441SEvalZero }
225*10465441SEvalZero
226*10465441SEvalZero page = (int)(from / chip->page_size);
227*10465441SEvalZero
228*10465441SEvalZero oob = desc->oobbuf;
229*10465441SEvalZero oob_required = oob ? 1 : 0;
230*10465441SEvalZero
231*10465441SEvalZero while (1)
232*10465441SEvalZero {
233*10465441SEvalZero bytes = min(chip->page_size, readlen);
234*10465441SEvalZero
235*10465441SEvalZero chip->ops->cmdfunc(chip, NAND_CMD_PAGE_RD, page, 0x00);
236*10465441SEvalZero if (NOTALIGNED(bytes))
237*10465441SEvalZero {
238*10465441SEvalZero notalign = buf;
239*10465441SEvalZero buf = chip->pagebuf;
240*10465441SEvalZero }
241*10465441SEvalZero /*
242*10465441SEvalZero * Now read the page into the buffer. Absent an error,
243*10465441SEvalZero * the read methods return max bitflips per ecc step.
244*10465441SEvalZero */
245*10465441SEvalZero if (desc->mode == MTD_OPM_RAW)
246*10465441SEvalZero {
247*10465441SEvalZero ret = nand_read_page_raw(chip, buf, oob_required, page);
248*10465441SEvalZero }
249*10465441SEvalZero else
250*10465441SEvalZero {
251*10465441SEvalZero ret = chip->read_page(chip, buf, oob_required, page);
252*10465441SEvalZero }
253*10465441SEvalZero
254*10465441SEvalZero if (ret != 0)
255*10465441SEvalZero {
256*10465441SEvalZero ret = -EBADMSG;
257*10465441SEvalZero break;
258*10465441SEvalZero }
259*10465441SEvalZero
260*10465441SEvalZero if (oob)
261*10465441SEvalZero {
262*10465441SEvalZero int toread = min(oobreadlen, max_oobsize);
263*10465441SEvalZero
264*10465441SEvalZero if (toread)
265*10465441SEvalZero {
266*10465441SEvalZero oob = nand_transfer_oob(chip, oob, desc, toread);
267*10465441SEvalZero oobreadlen -= toread;
268*10465441SEvalZero }
269*10465441SEvalZero }
270*10465441SEvalZero
271*10465441SEvalZero if (notalign)
272*10465441SEvalZero {
273*10465441SEvalZero rt_memcpy(notalign, buf, bytes);
274*10465441SEvalZero }
275*10465441SEvalZero
276*10465441SEvalZero buf += bytes;
277*10465441SEvalZero readlen -= bytes;
278*10465441SEvalZero
279*10465441SEvalZero if (!readlen)
280*10465441SEvalZero break;
281*10465441SEvalZero
282*10465441SEvalZero page++;
283*10465441SEvalZero }
284*10465441SEvalZero
285*10465441SEvalZero desc->datretlen = desc->datlen - (size_t)readlen;
286*10465441SEvalZero if (oob)
287*10465441SEvalZero desc->oobretlen = desc->ooblen - oobreadlen;
288*10465441SEvalZero
289*10465441SEvalZero return ret;
290*10465441SEvalZero }
291*10465441SEvalZero
292*10465441SEvalZero /*
293*10465441SEvalZero * write with ECC
294*10465441SEvalZero *
295*10465441SEvalZero */
nand_do_write_desc(rt_nand_t * chip,loff_t to,struct mtd_io_desc * desc)296*10465441SEvalZero static int nand_do_write_desc(rt_nand_t *chip, loff_t to, struct mtd_io_desc *desc)
297*10465441SEvalZero {
298*10465441SEvalZero int page;
299*10465441SEvalZero uint16_t writelen = desc->datlen;
300*10465441SEvalZero uint16_t oob_required = desc->oobbuf ? 1 : 0;
301*10465441SEvalZero uint16_t oobwritelen = desc->ooblen;
302*10465441SEvalZero uint16_t oobmaxlen = desc->mode == MTD_OPM_AUTO_OOB ?
303*10465441SEvalZero chip->freelayout->length : chip->oobsize;
304*10465441SEvalZero
305*10465441SEvalZero uint8_t *oob = desc->oobbuf;
306*10465441SEvalZero uint8_t *buf = desc->datbuf;
307*10465441SEvalZero int ret;
308*10465441SEvalZero
309*10465441SEvalZero if (!writelen)
310*10465441SEvalZero return 0;
311*10465441SEvalZero
312*10465441SEvalZero /* Reject writes, which are not page aligned */
313*10465441SEvalZero if (NOTALIGNED(to))
314*10465441SEvalZero {
315*10465441SEvalZero return -EINVAL;
316*10465441SEvalZero }
317*10465441SEvalZero
318*10465441SEvalZero page = (int)(to / chip->page_size);
319*10465441SEvalZero
320*10465441SEvalZero /* Don't allow multipage oob writes with offset */
321*10465441SEvalZero if (oob && desc->ooboffs && (desc->ooboffs + desc->ooblen > oobmaxlen))
322*10465441SEvalZero {
323*10465441SEvalZero ret = -EINVAL;
324*10465441SEvalZero goto err_out;
325*10465441SEvalZero }
326*10465441SEvalZero
327*10465441SEvalZero if (NOTALIGNED(desc->datlen) && !chip->pagebuf)
328*10465441SEvalZero {
329*10465441SEvalZero chip->pagebuf = rt_malloc(chip->page_size);
330*10465441SEvalZero if (!chip->pagebuf)
331*10465441SEvalZero return -ENOMEM;
332*10465441SEvalZero }
333*10465441SEvalZero
334*10465441SEvalZero while (1)
335*10465441SEvalZero {
336*10465441SEvalZero uint16_t bytes = min(chip->page_size, writelen);
337*10465441SEvalZero
338*10465441SEvalZero if (oob)
339*10465441SEvalZero {
340*10465441SEvalZero size_t len = min(oobwritelen, oobmaxlen);
341*10465441SEvalZero oob = nand_fill_oob(chip, oob, len, desc);
342*10465441SEvalZero oobwritelen -= len;
343*10465441SEvalZero }
344*10465441SEvalZero else
345*10465441SEvalZero {
346*10465441SEvalZero /* We still need to erase leftover OOB data */
347*10465441SEvalZero rt_memset(chip->oob_poi, 0xff, chip->oobsize);
348*10465441SEvalZero }
349*10465441SEvalZero
350*10465441SEvalZero if (NOTALIGNED(bytes))
351*10465441SEvalZero {
352*10465441SEvalZero uint8_t *dbtmp = buf;
353*10465441SEvalZero buf = chip->pagebuf;
354*10465441SEvalZero rt_memset(&buf[bytes], 0xff, chip->page_size - bytes);
355*10465441SEvalZero rt_memcpy(buf, dbtmp, bytes);
356*10465441SEvalZero }
357*10465441SEvalZero ret = nand_write_page(chip, buf, oob_required, page, (desc->mode == MTD_OPM_RAW));
358*10465441SEvalZero if (ret)
359*10465441SEvalZero break;
360*10465441SEvalZero
361*10465441SEvalZero writelen -= bytes;
362*10465441SEvalZero if (!writelen)
363*10465441SEvalZero break;
364*10465441SEvalZero
365*10465441SEvalZero buf += bytes;
366*10465441SEvalZero page++;
367*10465441SEvalZero }
368*10465441SEvalZero
369*10465441SEvalZero desc->datretlen = desc->datlen - writelen;
370*10465441SEvalZero if (oob)
371*10465441SEvalZero desc->oobretlen = desc->ooblen;
372*10465441SEvalZero
373*10465441SEvalZero err_out:
374*10465441SEvalZero
375*10465441SEvalZero return ret;
376*10465441SEvalZero }
377*10465441SEvalZero
nand_read_oob_std(rt_nand_t * chip,int page)378*10465441SEvalZero static int nand_read_oob_std(rt_nand_t *chip, int page)
379*10465441SEvalZero {
380*10465441SEvalZero chip->ops->cmdfunc(chip, NAND_CMD_PAGE_RD, page, chip->page_size);
381*10465441SEvalZero chip->ops->read_buf(chip, chip->oob_poi, chip->oobsize);
382*10465441SEvalZero
383*10465441SEvalZero return 0;
384*10465441SEvalZero }
385*10465441SEvalZero
386*10465441SEvalZero /*
387*10465441SEvalZero * read one page of OOB
388*10465441SEvalZero */
nand_only_read_oob(rt_nand_t * chip,loff_t from,struct mtd_io_desc * desc)389*10465441SEvalZero static int nand_only_read_oob(rt_nand_t *chip, loff_t from, struct mtd_io_desc *desc)
390*10465441SEvalZero {
391*10465441SEvalZero int page;
392*10465441SEvalZero int readlen = desc->ooblen;
393*10465441SEvalZero int len;
394*10465441SEvalZero uint8_t *buf = desc->oobbuf;
395*10465441SEvalZero int ret = 0;
396*10465441SEvalZero
397*10465441SEvalZero if (desc->mode == MTD_OPM_AUTO_OOB)
398*10465441SEvalZero len = chip->freelayout->length;
399*10465441SEvalZero else
400*10465441SEvalZero len = chip->oobsize;
401*10465441SEvalZero
402*10465441SEvalZero if (desc->ooboffs >= len) //attempt to start read outside oob
403*10465441SEvalZero {
404*10465441SEvalZero return -EINVAL;
405*10465441SEvalZero }
406*10465441SEvalZero
407*10465441SEvalZero page = (int)(from / chip->page_size);
408*10465441SEvalZero
409*10465441SEvalZero ret = nand_read_oob_std(chip, page);
410*10465441SEvalZero if (ret == 0)
411*10465441SEvalZero {
412*10465441SEvalZero len = min(len, readlen);
413*10465441SEvalZero buf = nand_transfer_oob(chip, buf, desc, len);
414*10465441SEvalZero desc->oobretlen = len;
415*10465441SEvalZero }
416*10465441SEvalZero
417*10465441SEvalZero return ret;
418*10465441SEvalZero }
419*10465441SEvalZero
nand_write_oob_std(rt_nand_t * chip,int page)420*10465441SEvalZero static int nand_write_oob_std(rt_nand_t *chip, int page)
421*10465441SEvalZero {
422*10465441SEvalZero int status;
423*10465441SEvalZero
424*10465441SEvalZero chip->ops->cmdfunc(chip, NAND_CMD_PAGE_WR0, page, chip->page_size);
425*10465441SEvalZero chip->ops->write_buf(chip, chip->oob_poi, chip->oobsize);
426*10465441SEvalZero /* Send command to program the OOB data */
427*10465441SEvalZero status = chip->ops->cmdfunc(chip, NAND_CMD_PAGE_WR1, -1, -1);
428*10465441SEvalZero
429*10465441SEvalZero return status & NAND_STATUS_FAIL ? -EIO : 0;
430*10465441SEvalZero }
431*10465441SEvalZero
nand_only_write_oob(rt_nand_t * chip,loff_t to,struct mtd_io_desc * desc)432*10465441SEvalZero static int nand_only_write_oob(rt_nand_t *chip, loff_t to, struct mtd_io_desc *desc)
433*10465441SEvalZero {
434*10465441SEvalZero int page, ret, len;
435*10465441SEvalZero
436*10465441SEvalZero if (desc->mode == MTD_OPM_AUTO_OOB)
437*10465441SEvalZero len = chip->freelayout->length;
438*10465441SEvalZero else
439*10465441SEvalZero len = chip->oobsize;
440*10465441SEvalZero
441*10465441SEvalZero /* Do not allow write past end of page */
442*10465441SEvalZero if ((desc->ooboffs + desc->ooblen) > len)
443*10465441SEvalZero {
444*10465441SEvalZero return -EINVAL;
445*10465441SEvalZero }
446*10465441SEvalZero
447*10465441SEvalZero if (desc->ooblen == 0)
448*10465441SEvalZero {
449*10465441SEvalZero return -EINVAL;
450*10465441SEvalZero }
451*10465441SEvalZero
452*10465441SEvalZero /* get page */
453*10465441SEvalZero page = (int)(to / chip->page_size);
454*10465441SEvalZero
455*10465441SEvalZero nand_fill_oob(chip, desc->oobbuf, desc->ooblen, desc);
456*10465441SEvalZero
457*10465441SEvalZero ret = nand_write_oob_std(chip, page);
458*10465441SEvalZero if (ret == 0)
459*10465441SEvalZero desc->oobretlen = len;
460*10465441SEvalZero
461*10465441SEvalZero return ret;
462*10465441SEvalZero }
463*10465441SEvalZero
nand_erase(rt_mtd_t * mtd,loff_t addr,size_t size)464*10465441SEvalZero static int nand_erase(rt_mtd_t *mtd, loff_t addr, size_t size)
465*10465441SEvalZero {
466*10465441SEvalZero rt_nand_t *chip;
467*10465441SEvalZero int status;
468*10465441SEvalZero int page;
469*10465441SEvalZero uint32_t blksize;
470*10465441SEvalZero
471*10465441SEvalZero chip = MTDTONAND(mtd);
472*10465441SEvalZero blksize = mtd->block_size;
473*10465441SEvalZero page = addr / chip->page_size;
474*10465441SEvalZero
475*10465441SEvalZero while (size >= blksize)
476*10465441SEvalZero {
477*10465441SEvalZero status = chip->ops->cmdfunc(chip, NAND_CMD_BLK_ERASE, page, 0);
478*10465441SEvalZero if (status & NAND_STATUS_FAIL)
479*10465441SEvalZero {
480*10465441SEvalZero break;
481*10465441SEvalZero }
482*10465441SEvalZero size -= blksize;
483*10465441SEvalZero page += chip->pages_pb;
484*10465441SEvalZero }
485*10465441SEvalZero
486*10465441SEvalZero return size;
487*10465441SEvalZero }
488*10465441SEvalZero
nand_read(rt_mtd_t * mtd,loff_t from,struct mtd_io_desc * desc)489*10465441SEvalZero static int nand_read(rt_mtd_t *mtd, loff_t from, struct mtd_io_desc *desc)
490*10465441SEvalZero {
491*10465441SEvalZero int ret = -ENOTSUP;
492*10465441SEvalZero rt_nand_t *chip;
493*10465441SEvalZero
494*10465441SEvalZero chip = MTDTONAND(mtd);
495*10465441SEvalZero
496*10465441SEvalZero switch (desc->mode)
497*10465441SEvalZero {
498*10465441SEvalZero case MTD_OPM_PLACE_OOB:
499*10465441SEvalZero case MTD_OPM_AUTO_OOB:
500*10465441SEvalZero case MTD_OPM_RAW:
501*10465441SEvalZero break;
502*10465441SEvalZero
503*10465441SEvalZero default:
504*10465441SEvalZero goto out;
505*10465441SEvalZero }
506*10465441SEvalZero
507*10465441SEvalZero if (!desc->datbuf || !desc->datlen)
508*10465441SEvalZero ret = nand_only_read_oob(chip, from, desc);
509*10465441SEvalZero else
510*10465441SEvalZero ret = nand_do_read_desc(chip, from, desc);
511*10465441SEvalZero
512*10465441SEvalZero out:
513*10465441SEvalZero
514*10465441SEvalZero return ret;
515*10465441SEvalZero }
516*10465441SEvalZero
nand_write(rt_mtd_t * mtd,loff_t to,struct mtd_io_desc * desc)517*10465441SEvalZero static int nand_write(rt_mtd_t *mtd, loff_t to, struct mtd_io_desc *desc)
518*10465441SEvalZero {
519*10465441SEvalZero int ret = -ENOTSUP;
520*10465441SEvalZero rt_nand_t *chip;
521*10465441SEvalZero
522*10465441SEvalZero chip = MTDTONAND(mtd);
523*10465441SEvalZero
524*10465441SEvalZero switch (desc->mode)
525*10465441SEvalZero {
526*10465441SEvalZero case MTD_OPM_PLACE_OOB:
527*10465441SEvalZero case MTD_OPM_AUTO_OOB:
528*10465441SEvalZero case MTD_OPM_RAW:
529*10465441SEvalZero break;
530*10465441SEvalZero
531*10465441SEvalZero default:
532*10465441SEvalZero goto out;
533*10465441SEvalZero }
534*10465441SEvalZero
535*10465441SEvalZero if (!desc->datbuf || !desc->datlen)
536*10465441SEvalZero ret = nand_only_write_oob(chip, to, desc);
537*10465441SEvalZero else
538*10465441SEvalZero ret = nand_do_write_desc(chip, to, desc);
539*10465441SEvalZero
540*10465441SEvalZero out:
541*10465441SEvalZero
542*10465441SEvalZero return ret;
543*10465441SEvalZero }
544*10465441SEvalZero
nand_block_isbad(rt_mtd_t * mtd,uint32_t blk)545*10465441SEvalZero static int nand_block_isbad(rt_mtd_t *mtd, uint32_t blk)
546*10465441SEvalZero {
547*10465441SEvalZero int ret;
548*10465441SEvalZero rt_nand_t *chip = MTDTONAND(mtd);
549*10465441SEvalZero
550*10465441SEvalZero if (chip->ops->isbad)
551*10465441SEvalZero {
552*10465441SEvalZero ret = chip->ops->isbad(chip, blk);
553*10465441SEvalZero }
554*10465441SEvalZero else
555*10465441SEvalZero {
556*10465441SEvalZero int page;
557*10465441SEvalZero
558*10465441SEvalZero page = blk * chip->pages_pb;
559*10465441SEvalZero nand_read_oob_std(chip, page);
560*10465441SEvalZero ret = chip->oob_poi[0] != 0xFF;
561*10465441SEvalZero }
562*10465441SEvalZero
563*10465441SEvalZero return ret;
564*10465441SEvalZero }
565*10465441SEvalZero
nand_block_markbad(rt_mtd_t * mtd,uint32_t blk)566*10465441SEvalZero static int nand_block_markbad(rt_mtd_t *mtd, uint32_t blk)
567*10465441SEvalZero {
568*10465441SEvalZero int ret;
569*10465441SEvalZero rt_nand_t *chip;
570*10465441SEvalZero
571*10465441SEvalZero chip = MTDTONAND(mtd);
572*10465441SEvalZero
573*10465441SEvalZero if (chip->ops->markbad)
574*10465441SEvalZero {
575*10465441SEvalZero ret = chip->ops->markbad(chip, blk);
576*10465441SEvalZero }
577*10465441SEvalZero else
578*10465441SEvalZero {
579*10465441SEvalZero int page;
580*10465441SEvalZero
581*10465441SEvalZero page = blk * chip->pages_pb;
582*10465441SEvalZero rt_memset(chip->oob_poi, 0xff, chip->oobsize);
583*10465441SEvalZero chip->oob_poi[0] = 0;
584*10465441SEvalZero ret = nand_write_oob_std(chip, page);
585*10465441SEvalZero }
586*10465441SEvalZero
587*10465441SEvalZero return ret;
588*10465441SEvalZero }
589*10465441SEvalZero
590*10465441SEvalZero static const struct mtd_ops _ops =
591*10465441SEvalZero {
592*10465441SEvalZero nand_erase,
593*10465441SEvalZero nand_read,
594*10465441SEvalZero nand_write,
595*10465441SEvalZero nand_block_isbad,
596*10465441SEvalZero nand_block_markbad,
597*10465441SEvalZero };
598*10465441SEvalZero
rt_mtd_nand_init(rt_nand_t * nand,int blk_size,int page_size,int oob_size)599*10465441SEvalZero int rt_mtd_nand_init(rt_nand_t *nand, int blk_size, int page_size, int oob_size)
600*10465441SEvalZero {
601*10465441SEvalZero uint8_t *buf;
602*10465441SEvalZero
603*10465441SEvalZero buf = rt_malloc(oob_size * 3);
604*10465441SEvalZero if (buf == RT_NULL)
605*10465441SEvalZero return -ENOMEM;
606*10465441SEvalZero
607*10465441SEvalZero nand->oob_poi = buf;
608*10465441SEvalZero buf += oob_size;
609*10465441SEvalZero nand->buffers.ecccalc = buf;
610*10465441SEvalZero buf += oob_size;
611*10465441SEvalZero nand->buffers.ecccode = buf;
612*10465441SEvalZero nand->pagebuf = 0; /* alloc when unaligen access */
613*10465441SEvalZero
614*10465441SEvalZero nand->pages_pb = blk_size / page_size;
615*10465441SEvalZero nand->ecc._step = page_size / nand->ecc.stepsize;
616*10465441SEvalZero nand->page_size = page_size;
617*10465441SEvalZero nand->oobsize = oob_size;
618*10465441SEvalZero
619*10465441SEvalZero nand->parent.type = MTD_TYPE_NAND;
620*10465441SEvalZero nand->parent.ops = &_ops;
621*10465441SEvalZero nand->parent.sector_size = page_size;
622*10465441SEvalZero nand->parent.block_size = blk_size;
623*10465441SEvalZero nand->parent.oob_size = oob_size;
624*10465441SEvalZero
625*10465441SEvalZero switch (nand->ecc.mode)
626*10465441SEvalZero {
627*10465441SEvalZero case NAND_ECCM_NONE:
628*10465441SEvalZero {
629*10465441SEvalZero nand->read_page = nand_read_page_raw;
630*10465441SEvalZero nand->write_page = nand_write_page_raw;
631*10465441SEvalZero }break;
632*10465441SEvalZero case NAND_ECCM_HW:
633*10465441SEvalZero {
634*10465441SEvalZero nand->read_page = nand_read_page_hwecc;
635*10465441SEvalZero nand->write_page = nand_write_page_hwecc;
636*10465441SEvalZero }break;
637*10465441SEvalZero default:
638*10465441SEvalZero {
639*10465441SEvalZero rt_free(buf);
640*10465441SEvalZero return -1;
641*10465441SEvalZero }
642*10465441SEvalZero }
643*10465441SEvalZero
644*10465441SEvalZero return 0;
645*10465441SEvalZero }
646