]> rtime.felk.cvut.cz Git - l4.git/blob - l4/pkg/drivers-frst/nand/src/nand.cc
update
[l4.git] / l4 / pkg / drivers-frst / nand / src / nand.cc
1 #include <assert.h>
2 #include <stdlib.h>
3 #include <stdio.h>
4 #include <errno.h>
5
6 #include "common.h"
7 #include "nand.h"
8
9 Nand_chip::Nand_chip(Nand_ctrl *ctrl, Dev_desc *dev, Mfr_desc *mfr, int ext_id)
10   : _state(Ready), _ongoing_op(0), _ctrl(ctrl), _dev(dev), _mfr(mfr)
11 {
12   /* New devices have all the information in additional id bytes */
13   if (!dev->sz_page)
14     {
15       /* The 3rd id byte holds MLC / multichip data */
16       _sz_write = 1024 << (ext_id & 0x3);
17       ext_id >>= 2;
18       _sz_spare = (8 << (ext_id & 0x01)) * (_sz_write >> 9);
19       ext_id >>= 2;
20       _sz_erase = (64 * 1024) << (ext_id & 0x03);
21       ext_id >>= 2;
22       _bus_width = (ext_id & 0x01) ? Opt_buswidth_16 : 0;
23     }
24   /* Old devices have chip data hardcoded in the device id table */
25   else
26     {
27       _sz_write = dev->sz_page;
28       _sz_spare = dev->sz_spare ? dev->sz_spare : _sz_write / 32;
29       _sz_erase = dev->sz_erase;
30       _bus_width = dev->options & Opt_buswidth_16;
31     }
32   _sz_chip = (long long unsigned)dev->sz_chip << 20;
33
34   if (_bus_width != (_options & Opt_buswidth_16))
35     {
36       printf("NAND bus width %d instead %d bit\n",
37              (_options & Opt_buswidth_16) ? 16 : 8, _bus_width ? 16 : 8);
38     }
39
40   _options |= dev->options;
41   _options |= Opt_no_autoincr;
42
43   printf("NAND chip: Mfr ID: 0x%02x(%s), Device ID: 0x%02x(%s)\n",
44          mfr->id, mfr->name, dev->id, dev->name);
45 }
46
47 int Nand_chip::handle_irq()
48 {
49   switch (_state)
50     {
51     case Reading:
52       return _ctrl->done_read(static_cast<Read_op *>(_ongoing_op));
53       break;
54
55     case Writing:
56       return _ctrl->done_write(static_cast<Write_op *>(_ongoing_op));
57       break;
58
59     case Erasing:
60       return _ctrl->done_erase(static_cast<Erase_op *>(_ongoing_op));
61       break;
62
63     default:
64       _state = Ready;
65       break;
66     }
67   return 0;
68 }
69
70 bool Nand_ctrl::is_wp()
71 {
72   wr_cmd(Cmd_status);
73   return (rd_dat() & Status_wp) ? false : true;
74 }
75
76 int Nand_ctrl::get_status()
77 {
78   wr_cmd(Cmd_status);
79   return rd_dat();
80 }
81
82 int Nand_ctrl::read(Read_op *op)
83 {
84   Nand_chip *chip = select(op->addr);
85
86   if (!op->transfer->len)
87     return -EINVAL;
88
89   u16 col = op->addr & (chip->sz_write() - 1);
90   u32 row = (op->addr >> chip->page_shift()) & chip->page_mask();
91
92   assert(!col);
93   assert(op->transfer->len + col <= (chip->sz_write() + chip->sz_spare()));
94
95   wr_cmd(Cmd_read0);
96   wr_adr(col);
97   wr_adr(col >> 8);
98   wr_adr(row);
99   wr_adr(row >> 8);
100   wr_adr(row >> 16);
101   wr_cmd(Cmd_readstart);
102
103   chip->set_state(Nand_chip::Reading, op);
104
105   return 0;
106 }
107
108 int Nand_ctrl::done_read(Read_op *op)
109 {
110   Nand_chip *chip = select(op->addr);
111   assert(chip->state() == Nand_chip::Reading);
112
113   for (unsigned i = 0; i < op->transfer->num; ++i)
114     rd_dat((u8 *)(*op->transfer)[i].addr, (*op->transfer)[i].size);
115
116   chip->set_state(Nand_chip::Ready);
117   return 0;
118 }
119
120 int Nand_ctrl::write(Write_op *op)
121 {
122   Nand_chip *chip = select(op->addr);
123
124   if (!op->transfer->len)
125     return -EINVAL;
126
127   // check end of device
128   if ((op->addr + op->transfer->len) > size)
129     return -EINVAL;
130   
131   // check page aligning
132   if (!aligned(op->addr))
133     {
134       printf("NAND: Attempt to write not page aligned data\n");
135       return -EINVAL;
136     }
137
138   if (is_wp())
139     {
140       printf("NAND: Device is write protected\n");
141       return -EIO;
142     }
143
144   u32 page = (op->addr >> chip->page_shift()) & chip->page_mask();
145
146   wr_cmd(Cmd_seqin);
147   wr_adr(0x00);
148   wr_adr(0x00);
149   wr_adr(page);
150   wr_adr(page >> 8);
151   wr_adr(page >> 16);
152
153   for (unsigned i = 0; i < op->transfer->num; ++i)
154     wr_dat((u8 *)(*op->transfer)[i].addr, (*op->transfer)[i].size);
155
156   wr_cmd(Cmd_pageprog);
157   chip->set_state(Nand_chip::Writing, op);
158   
159   return 0;
160 }
161
162 int Nand_ctrl::done_write(Write_op *op)
163 {
164   Nand_chip *chip = select(op->addr);
165   assert(chip->state() == Nand_chip::Writing);
166   chip->set_state(Nand_chip::Ready);
167   return (get_status() & Status_fail) ? -EIO : 0;
168 }
169
170 int Nand_ctrl::erase(Erase_op *op)
171 {
172   Nand_chip *chip = select(op->addr);
173
174 #if 0
175   printf("nand: erase: start = 0x%08x, len = %u\n",
176       (unsigned int) op->addr, op->len);
177 #endif
178
179   /* address must align on block boundary */
180   if (op->addr & chip->erase_mask())
181     {
182       printf("nand: erase: Unaligned address\n");
183       return -EINVAL;
184     }
185
186   /* length must align on block boundary */
187   if (op->len & chip->erase_mask())
188     {
189       printf("nand: erase: Length not block aligned\n");
190       return -EINVAL;
191     }
192
193   /* Do not allow erase past end of device */
194   if ((op->len + op->addr) > size)
195     {
196       printf("nand: erase: Erase past end of device\n");
197       return -EINVAL;
198   }
199
200   /* Check, if it is write protected */
201   if (is_wp())
202     {
203       printf("nand_erase: Device is write protected!!!\n");
204       return -EIO;
205     }
206
207   int page = op->addr >> chip->page_shift();
208
209   wr_cmd(Cmd_erase1);
210   wr_adr(page);
211   wr_adr(page >> 8);
212   wr_adr(page >> 16);
213   wr_cmd(Cmd_erase2);
214
215   chip->set_state(Nand_chip::Erasing, op);
216   
217   return 0;
218 }
219
220 int Nand_ctrl::done_erase(Erase_op *op)
221 {
222   Nand_chip *chip = select(op->addr);
223   assert(chip->state() == Nand_chip::Erasing);
224   chip->set_state(Nand_chip::Ready);
225   return (get_status() & Status_fail) ? -EIO : 0;
226 }
227
228 int Nand_ctrl::get_id(char id[4])
229 {
230   wr_cmd(Cmd_readid);
231   wr_adr(0x00);
232   wr_dat(0xff);
233       
234   id[0] = rd_dat();
235   id[1] = rd_dat();
236   id[2] = rd_dat();
237   id[3] = rd_dat();
238
239   return 0;
240 }
241
242 int Nand_ctrl::scan(int maxchips)
243 {
244   for (int i = 0; i < maxchips; ++i)
245     {
246       wr_cmd(Cmd_reset);
247       udelay(100);
248       wr_cmd(Cmd_status);
249       while (!(rd_dat() & Status_ready));
250      
251       char id1[4], id2[4];
252       get_id(id1);
253       get_id(id2);
254
255       if (id1[0] != id2[0] || id1[1] != id2[1])
256         {
257           printf("manufacturer or device id corrupt:\n");
258           printf("mfr-id1:%02x mfr-id2:%02x\n", id1[0], id2[0]);
259           printf("dev-id1:%02x dev-id2:%02x\n", id1[1], id2[1]);
260           return -1;
261         }
262
263       /* identify manufacturer */
264       Mfr_desc *mfr = 0;
265       for (int j = 0; _mfr_ids[j].id != 0; j++)
266         {
267           if (id1[0] == _mfr_ids[j].id)
268             {
269               mfr = &_mfr_ids[j];
270               break;
271             }
272         }
273
274       /* identify device */
275       Dev_desc *dev = 0;
276       for (int j = 0; _dev_ids[j].name != 0; j++)
277         {
278           if (id1[1] == _dev_ids[j].id)
279             {
280               dev = &_dev_ids[j];
281               break;
282             }
283         }
284       
285       if (!dev)
286         {
287           printf("no device device found\n");
288           continue;
289         }
290       if (!mfr)
291         {
292           printf("no manufacturer found\n");
293           continue;
294         }
295
296       Nand_chip *chip = new Nand_chip(this, dev, mfr, id1[3]);
297       add(chip);
298
299       // XXX all chips should have the same features
300       sz_write = chip->sz_write();
301       sz_spare = chip->sz_spare();
302       sz_erase = chip->sz_erase();
303
304       size +=  chip->sz_chip();
305       numchips++;
306     }
307
308   //printf("%d NAND chips detected\n", numchips);
309   
310   return 0;
311 }
312
313 Nand_ctrl::Nand_ctrl()
314   : size(0)
315 {}
316