xref: /nrf52832-nimble/rt-thread/components/drivers/mtd/mtdnand.c (revision 104654410c56c573564690304ae786df310c91fc)
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