blob: a9a8e69bde0005e7f0e0dd0d936c89288a5f1608 [file] [log] [blame]
Dima Zavin03cf4312009-01-23 16:38:30 -08001/*
2 * Copyright (c) 2008, Google Inc.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in
12 * the documentation and/or other materials provided with the
13 * distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
16 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
17 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
18 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
19 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
21 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
22 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
23 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
25 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 */
28
29#include <debug.h>
30#include <reg.h>
31#include <stdlib.h>
32#include <string.h>
33#include <dev/flash.h>
34#include <lib/ptable.h>
35
36#include "dmov.h"
37#include "nand.h"
38
39#define VERBOSE 0
40
41typedef struct dmov_ch dmov_ch;
42struct dmov_ch
43{
44 volatile unsigned cmd;
45 volatile unsigned result;
46 volatile unsigned status;
47 volatile unsigned config;
48};
49
50static void dmov_prep_ch(dmov_ch *ch, unsigned id)
51{
52 ch->cmd = DMOV_CMD_PTR(id);
53 ch->result = DMOV_RSLT(id);
54 ch->status = DMOV_STATUS(id);
55 ch->config = DMOV_CONFIG(id);
56}
57
58#define SRC_CRCI_NAND_CMD CMD_SRC_CRCI(DMOV_NAND_CRCI_CMD)
59#define DST_CRCI_NAND_CMD CMD_DST_CRCI(DMOV_NAND_CRCI_CMD)
60#define SRC_CRCI_NAND_DATA CMD_SRC_CRCI(DMOV_NAND_CRCI_DATA)
61#define DST_CRCI_NAND_DATA CMD_DST_CRCI(DMOV_NAND_CRCI_DATA)
62
63static unsigned CFG0, CFG1;
64
65#define CFG1_WIDE_FLASH (1U << 1)
66
67#define paddr(n) ((unsigned) (n))
68
69static int dmov_exec_cmdptr(unsigned id, unsigned *ptr)
70{
71 dmov_ch ch;
72 unsigned n;
73
74 dmov_prep_ch(&ch, id);
75
76 writel(DMOV_CMD_PTR_LIST | DMOV_CMD_ADDR(paddr(ptr)), ch.cmd);
77
78 while(!(readl(ch.status) & DMOV_STATUS_RSLT_VALID)) ;
79
80 n = readl(ch.status);
81 while(DMOV_STATUS_RSLT_COUNT(n)) {
82 n = readl(ch.result);
83 if(n != 0x80000002) {
84 dprintf(CRITICAL, "ERROR: result: %x\n", n);
85 dprintf(CRITICAL, "ERROR: flush: %x %x %x %x\n",
86 readl(DMOV_FLUSH0(DMOV_NAND_CHAN)),
87 readl(DMOV_FLUSH1(DMOV_NAND_CHAN)),
88 readl(DMOV_FLUSH2(DMOV_NAND_CHAN)),
89 readl(DMOV_FLUSH3(DMOV_NAND_CHAN)));
90 }
91 n = readl(ch.status);
92 }
93
94 return 0;
95}
96
97static unsigned flash_maker = 0;
98static unsigned flash_device = 0;
99
100static void flash_read_id(dmov_s *cmdlist, unsigned *ptrlist)
101{
102 dmov_s *cmd = cmdlist;
103 unsigned *ptr = ptrlist;
104 unsigned *data = ptrlist + 4;
105
106 data[0] = 0 | 4;
107 data[1] = NAND_CMD_FETCH_ID;
108 data[2] = 1;
109 data[3] = 0;
110 data[4] = 0;
111
112 cmd[0].cmd = 0 | CMD_OCB;
113 cmd[0].src = paddr(&data[0]);
114 cmd[0].dst = NAND_FLASH_CHIP_SELECT;
115 cmd[0].len = 4;
116
117 cmd[1].cmd = DST_CRCI_NAND_CMD;
118 cmd[1].src = paddr(&data[1]);
119 cmd[1].dst = NAND_FLASH_CMD;
120 cmd[1].len = 4;
121
122 cmd[2].cmd = 0;
123 cmd[2].src = paddr(&data[2]);
124 cmd[2].dst = NAND_EXEC_CMD;
125 cmd[2].len = 4;
126
127 cmd[3].cmd = SRC_CRCI_NAND_DATA;
128 cmd[3].src = NAND_FLASH_STATUS;
129 cmd[3].dst = paddr(&data[3]);
130 cmd[3].len = 4;
131
132 cmd[4].cmd = CMD_OCU | CMD_LC;
133 cmd[4].src = NAND_READ_ID;
134 cmd[4].dst = paddr(&data[4]);
135 cmd[4].len = 4;
136
137 ptr[0] = (paddr(cmd) >> 3) | CMD_PTR_LP;
138
139 dmov_exec_cmdptr(DMOV_NAND_CHAN, ptr);
140
141#if VERBOSE
142 dprintf(INFO, "status: %x\n", data[3]);
143#endif
144 dprintf(INFO, "nandid: %x maker %02x device %02x\n",
145 data[4], data[4] & 0xff, (data[4] >> 8) & 0xff);
146
147 flash_maker = data[4] & 0xff;
148 flash_device = (data[4] >> 8) & 0xff;
149}
150
151static int flash_erase_block(dmov_s *cmdlist, unsigned *ptrlist, unsigned page)
152{
153 dmov_s *cmd = cmdlist;
154 unsigned *ptr = ptrlist;
155 unsigned *data = ptrlist + 4;
156
157 /* only allow erasing on block boundaries */
158 if(page & 63) return -1;
159
160 data[0] = NAND_CMD_BLOCK_ERASE;
161 data[1] = page;
162 data[2] = 0;
163 data[3] = 0 | 4;
164 data[4] = 1;
165 data[5] = 0xeeeeeeee;
166 data[6] = CFG0 & (~(7 << 6)); /* CW_PER_PAGE = 0 */
167 data[7] = CFG1;
168
169 cmd[0].cmd = DST_CRCI_NAND_CMD | CMD_OCB;
170 cmd[0].src = paddr(&data[0]);
171 cmd[0].dst = NAND_FLASH_CMD;
172 cmd[0].len = 16;
173
174 cmd[1].cmd = 0;
175 cmd[1].src = paddr(&data[6]);
176 cmd[1].dst = NAND_DEV0_CFG0;
177 cmd[1].len = 8;
178
179 cmd[2].cmd = 0;
180 cmd[2].src = paddr(&data[4]);
181 cmd[2].dst = NAND_EXEC_CMD;
182 cmd[2].len = 4;
183
184 cmd[3].cmd = SRC_CRCI_NAND_DATA | CMD_OCU | CMD_LC;
185 cmd[3].src = NAND_FLASH_STATUS;
186 cmd[3].dst = paddr(&data[5]);
187 cmd[3].len = 4;
188
189 ptr[0] = (paddr(cmd) >> 3) | CMD_PTR_LP;
190
191 dmov_exec_cmdptr(DMOV_NAND_CHAN, ptr);
192
193#if VERBOSE
194 dprintf(INFO, "status: %x\n", data[5]);
195#endif
196
197 /* we fail if there was an operation error, a mpu error, or the
198 ** erase success bit was not set.
199 */
200 if(data[5] & 0x110) return -1;
201 if(!(data[5] & 0x80)) return -1;
202
203 return 0;
204}
205
206struct data_flash_io {
207 unsigned cmd;
208 unsigned addr0;
209 unsigned addr1;
210 unsigned chipsel;
211 unsigned cfg0;
212 unsigned cfg1;
213 unsigned exec;
214 unsigned ecc_cfg;
215 unsigned ecc_cfg_save;
216 struct {
217 unsigned flash_status;
218 unsigned buffer_status;
219 } result[4];
220};
221
222static int _flash_read_page(dmov_s *cmdlist, unsigned *ptrlist, unsigned page, void *_addr, void *_spareaddr)
223{
224 dmov_s *cmd = cmdlist;
225 unsigned *ptr = ptrlist;
226 struct data_flash_io *data = (void*) (ptrlist + 4);
227 unsigned addr = (unsigned) _addr;
228 unsigned spareaddr = (unsigned) _spareaddr;
229 unsigned n;
230
231 data->cmd = NAND_CMD_PAGE_READ_ECC;
232 data->addr0 = page << 16;
233 data->addr1 = (page >> 16) & 0xff;
234 data->chipsel = 0 | 4; /* flash0 + undoc bit */
235
236 /* GO bit for the EXEC register */
237 data->exec = 1;
238
239 data->cfg0 = CFG0;
240 data->cfg1 = CFG1;
241
242 data->ecc_cfg = 0x203;
243
244 /* save existing ecc config */
245 cmd->cmd = CMD_OCB;
246 cmd->src = NAND_EBI2_ECC_BUF_CFG;
247 cmd->dst = paddr(&data->ecc_cfg_save);
248 cmd->len = 4;
249 cmd++;
250
251 for(n = 0; n < 4; n++) {
252 /* write CMD / ADDR0 / ADDR1 / CHIPSEL regs in a burst */
253 cmd->cmd = DST_CRCI_NAND_CMD;
254 cmd->src = paddr(&data->cmd);
255 cmd->dst = NAND_FLASH_CMD;
256 cmd->len = ((n == 0) ? 16 : 4);
257 cmd++;
258
259 if (n == 0) {
260 /* block on cmd ready, set configuration */
261 cmd->cmd = 0;
262 cmd->src = paddr(&data->cfg0);
263 cmd->dst = NAND_DEV0_CFG0;
264 cmd->len = 8;
265 cmd++;
266
267 /* set our ecc config */
268 cmd->cmd = 0;
269 cmd->src = paddr(&data->ecc_cfg);
270 cmd->dst = NAND_EBI2_ECC_BUF_CFG;
271 cmd->len = 4;
272 cmd++;
273 }
274 /* kick the execute register */
275 cmd->cmd = 0;
276 cmd->src = paddr(&data->exec);
277 cmd->dst = NAND_EXEC_CMD;
278 cmd->len = 4;
279 cmd++;
280
281 /* block on data ready, then read the status register */
282 cmd->cmd = SRC_CRCI_NAND_DATA;
283 cmd->src = NAND_FLASH_STATUS;
284 cmd->dst = paddr(&data->result[n]);
285 cmd->len = 8;
286 cmd++;
287
288 /* read data block */
289 cmd->cmd = 0;
290 cmd->src = NAND_FLASH_BUFFER;
291 cmd->dst = addr + n * 516;
292 cmd->len = ((n < 3) ? 516 : 500);
293 cmd++;
294 }
295
296 /* read extra data */
297 cmd->cmd = 0;
298 cmd->src = NAND_FLASH_BUFFER + 500;
299 cmd->dst = spareaddr;
300 cmd->len = 16;
301 cmd++;
302
303 /* restore saved ecc config */
304 cmd->cmd = CMD_OCU | CMD_LC;
305 cmd->src = paddr(&data->ecc_cfg_save);
306 cmd->dst = NAND_EBI2_ECC_BUF_CFG;
307 cmd->len = 4;
308
309 ptr[0] = (paddr(cmdlist) >> 3) | CMD_PTR_LP;
310
311 dmov_exec_cmdptr(DMOV_NAND_CHAN, ptr);
312
313#if VERBOSE
314 dprintf(INFO, "read page %d: status: %x %x %x %x\n",
315 page, data[5], data[6], data[7], data[8]);
316 for(n = 0; n < 4; n++) {
317 ptr = (unsigned*)(addr + 512 * n);
318 dprintf(INFO, "data%d: %x %x %x %x\n", n, ptr[0], ptr[1], ptr[2], ptr[3]);
319 ptr = (unsigned*)(spareaddr + 16 * n);
320 dprintf(INFO, "spare data%d %x %x %x %x\n", n, ptr[0], ptr[1], ptr[2], ptr[3]);
321 }
322#endif
323
324 /* if any of the writes failed (0x10), or there was a
325 ** protection violation (0x100), we lose
326 */
327 for(n = 0; n < 4; n++) {
328 if (data->result[n].flash_status & 0x110) {
329 return -1;
330 }
331 }
332
333 return 0;
334}
335
336static int _flash_write_page(dmov_s *cmdlist, unsigned *ptrlist, unsigned page,
337 const void *_addr, const void *_spareaddr)
338{
339 dmov_s *cmd = cmdlist;
340 unsigned *ptr = ptrlist;
341 struct data_flash_io *data = (void*) (ptrlist + 4);
342 unsigned addr = (unsigned) _addr;
343 unsigned spareaddr = (unsigned) _spareaddr;
344 unsigned n;
345
346 data->cmd = NAND_CMD_PRG_PAGE;
347 data->addr0 = page << 16;
348 data->addr1 = (page >> 16) & 0xff;
349 data->chipsel = 0 | 4; /* flash0 + undoc bit */
350
351 data->cfg0 = CFG0;
352 data->cfg1 = CFG1;
353
354 /* GO bit for the EXEC register */
355 data->exec = 1;
356
357 data->ecc_cfg = 0x203;
358
359 /* save existing ecc config */
360 cmd->cmd = CMD_OCB;
361 cmd->src = NAND_EBI2_ECC_BUF_CFG;
362 cmd->dst = paddr(&data->ecc_cfg_save);
363 cmd->len = 4;
364 cmd++;
365
366 for(n = 0; n < 4; n++) {
367 /* write CMD / ADDR0 / ADDR1 / CHIPSEL regs in a burst */
368 cmd->cmd = DST_CRCI_NAND_CMD;
369 cmd->src = paddr(&data->cmd);
370 cmd->dst = NAND_FLASH_CMD;
371 cmd->len = ((n == 0) ? 16 : 4);
372 cmd++;
373
374 if (n == 0) {
375 /* set configuration */
376 cmd->cmd = 0;
377 cmd->src = paddr(&data->cfg0);
378 cmd->dst = NAND_DEV0_CFG0;
379 cmd->len = 8;
380 cmd++;
381
382 /* set our ecc config */
383 cmd->cmd = 0;
384 cmd->src = paddr(&data->ecc_cfg);
385 cmd->dst = NAND_EBI2_ECC_BUF_CFG;
386 cmd->len = 4;
387 cmd++;
388 }
389
390 /* write data block */
391 cmd->cmd = 0;
392 cmd->src = addr + n * 516;
393 cmd->dst = NAND_FLASH_BUFFER;
394 cmd->len = ((n < 3) ? 516 : 510);
395 cmd++;
396
397 if (n == 3) {
398 /* write extra data */
399 cmd->cmd = 0;
400 cmd->src = spareaddr;
401 cmd->dst = NAND_FLASH_BUFFER + 500;
402 cmd->len = 16;
403 cmd++;
404 }
405
406 /* kick the execute register */
407 cmd->cmd = 0;
408 cmd->src = paddr(&data->exec);
409 cmd->dst = NAND_EXEC_CMD;
410 cmd->len = 4;
411 cmd++;
412
413 /* block on data ready, then read the status register */
414 cmd->cmd = SRC_CRCI_NAND_DATA;
415 cmd->src = NAND_FLASH_STATUS;
416 cmd->dst = paddr(&data->result[n]);
417 cmd->len = 8;
418 cmd++;
419 }
420
421 /* restore saved ecc config */
422 cmd->cmd = CMD_OCU | CMD_LC;
423 cmd->src = paddr(&data->ecc_cfg_save);
424 cmd->dst = NAND_EBI2_ECC_BUF_CFG;
425 cmd->len = 4;
426
427 ptr[0] = (paddr(cmdlist) >> 3) | CMD_PTR_LP;
428
429 dmov_exec_cmdptr(DMOV_NAND_CHAN, ptr);
430
431#if VERBOSE
432 dprintf(INFO, "write page %d: status: %x %x %x %x\n",
433 page, data[5], data[6], data[7], data[8]);
434#endif
435
436 /* if any of the writes failed (0x10), or there was a
437 ** protection violation (0x100), or the program success
438 ** bit (0x80) is unset, we lose
439 */
440 for(n = 0; n < 4; n++) {
441 if(data->result[n].flash_status & 0x110) return -1;
442 if(!(data->result[n].flash_status & 0x80)) return -1;
443 }
444
445 return 0;
446}
447
448static int flash_read_config(dmov_s *cmdlist, unsigned *ptrlist)
449{
450 cmdlist[0].cmd = CMD_OCB;
451 cmdlist[0].src = NAND_DEV0_CFG0;
452 cmdlist[0].dst = paddr(&CFG0);
453 cmdlist[0].len = 4;
454
455 cmdlist[1].cmd = CMD_OCU | CMD_LC;
456 cmdlist[1].src = NAND_DEV0_CFG1;
457 cmdlist[1].dst = paddr(&CFG1);
458 cmdlist[1].len = 4;
459
460 *ptrlist = (paddr(cmdlist) >> 3) | CMD_PTR_LP;
461
462 dmov_exec_cmdptr(DMOV_NAND_CHAN, ptrlist);
463
464 if((CFG0 == 0) || (CFG1 == 0)) {
465 return -1;
466 }
467
468 dprintf(INFO, "nandcfg: %x %x (initial)\n", CFG0, CFG1);
469
470 CFG0 = (3 << 6) /* 4 codeword per page for 2k nand */
471 | (516 << 9) /* 516 user data bytes */
472 | (10 << 19) /* 10 parity bytes */
473 | (5 << 27) /* 5 address cycles */
474 | (1 << 30) /* Read status before data */
475 | (1 << 31) /* Send read cmd */
476 /* 0 spare bytes for 16 bit nand or 1 spare bytes for 8 bit */
477 | ((CFG1 & CFG1_WIDE_FLASH) ? (0 << 23) : (1 << 23));
478 CFG1 = (0 << 0) /* Enable ecc */
479 | (7 << 2) /* 8 recovery cycles */
480 | (0 << 5) /* Allow CS deassertion */
481 | (465 << 6) /* Bad block marker location */
482 | (0 << 16) /* Bad block in user data area */
483 | (2 << 17) /* 6 cycle tWB/tRB */
484 | (CFG1 & CFG1_WIDE_FLASH); /* preserve wide flash flag */
485
486 dprintf(INFO, "nandcfg: %x %x (used)\n", CFG0, CFG1);
487
488 return 0;
489}
490
491static unsigned *flash_ptrlist;
492static dmov_s *flash_cmdlist;
493static void *flash_spare;
494static void *flash_data;
495
496static struct ptable *flash_ptable = NULL;
497
498void flash_init(struct ptable *new_ptable)
499{
500 ASSERT(flash_ptable == NULL && new_ptable != NULL);
501
502 flash_ptable = new_ptable;
503
504 flash_ptrlist = memalign(32, 1024);
505 flash_cmdlist = memalign(32, 1024);
506 flash_data = memalign(32, 2048);
507 flash_spare = memalign(32, 64);
508
509 if(flash_read_config(flash_cmdlist, flash_ptrlist)) {
510 dprintf(CRITICAL, "ERROR: could not read CFG0/CFG1 state\n");
511 ASSERT(0);
512 }
513
514 flash_read_id(flash_cmdlist, flash_ptrlist);
515}
516
517struct ptable *flash_get_ptable(void)
518{
519 return flash_ptable;
520}
521
522int flash_erase(struct ptentry *ptn)
523{
524 unsigned block = ptn->start;
525 unsigned count = ptn->length;
526
527 while(count-- > 0) {
528 if(flash_erase_block(flash_cmdlist, flash_ptrlist, block * 64)) {
529 dprintf(INFO, "cannot erase @ %d (bad block?)\n", block);
530 }
531 block++;
532 }
533 return 0;
534}
535
536int flash_read_ext(struct ptentry *ptn, unsigned extra_per_page,
537 unsigned offset, void *data, unsigned bytes)
538{
539 unsigned page = (ptn->start * 64) + (offset / 2048);
540 unsigned lastpage = (ptn->start + ptn->length) * 64;
541 unsigned count = (bytes + 2047 + extra_per_page) / (2048 + extra_per_page);
542 unsigned *spare = (unsigned*) flash_spare;
543 unsigned errors = 0;
544 unsigned char *image = data;
545
546 if(offset & 2047)
547 return -1;
548
549 while(page < lastpage) {
550 if(count == 0) {
551 dprintf(INFO, "flash_read_image: success (%d errors)\n", errors);
552 return 0;
553 }
554
555 if(_flash_read_page(flash_cmdlist, flash_ptrlist, page++, image, spare)) {
556 errors++;
557 continue;
558 }
559 image += 2048;
560 memcpy(image, spare, extra_per_page);
561 image += extra_per_page;
562 count -= 1;
563 }
564
565 /* could not find enough valid pages before we hit the end */
566 dprintf(INFO, "flash_read_image: failed (%d errors)\n", errors);
567 return 0xffffffff;
568}
569
570int flash_write(struct ptentry *ptn, unsigned extra_per_page, const void *data,
571 unsigned bytes)
572{
573 unsigned page = ptn->start * 64;
574 unsigned lastpage = (ptn->start + ptn->length) * 64;
575 unsigned *spare = (unsigned*) flash_spare;
576 const unsigned char *image = data;
577 unsigned wsize = 2048 + extra_per_page;
578 unsigned n;
579 int r;
580
581 for(n = 0; n < 16; n++) spare[n] = 0xffffffff;
582
583 while(bytes > 0) {
584 if(bytes < wsize) {
585 dprintf(CRITICAL, "flash_write_image: image undersized (%d < %d)\n", bytes, wsize);
586 return -1;
587 }
588 if(page >= lastpage) {
589 dprintf(CRITICAL, "flash_write_image: out of space\n");
590 return -1;
591 }
592
593 if((page & 63) == 0) {
594 if(flash_erase_block(flash_cmdlist, flash_ptrlist, page)) {
595 dprintf(INFO, "flash_write_image: bad block @ %d\n", page >> 6);
596 page += 64;
597 continue;
598 }
599 }
600
601 if(extra_per_page) {
602 r = _flash_write_page(flash_cmdlist, flash_ptrlist, page++, image, image + 2048);
603 } else {
604 r = _flash_write_page(flash_cmdlist, flash_ptrlist, page++, image, spare);
605 }
606 if(r) {
607 dprintf(INFO, "flash_write_image: write failure @ page %d (src %d)\n", page, image - (const unsigned char *)data);
608 image -= (page & 63) * wsize;
609 bytes += (page & 63) * wsize;
610 page &= ~63;
611 if(flash_erase_block(flash_cmdlist, flash_ptrlist, page)) {
612 dprintf(INFO, "flash_write_image: erase failure @ page %d\n", page);
613 }
614 dprintf(INFO, "flash_write_image: restart write @ page %d (src %d)\n", page, image - (const unsigned char *)data);
615 page += 64;
616 continue;
617 }
618
619 image += wsize;
620 bytes -= wsize;
621 }
622
623 /* erase any remaining pages in the partition */
624 page = (page + 63) & (~63);
625 while(page < lastpage){
626 if(flash_erase_block(flash_cmdlist, flash_ptrlist, page)) {
627 dprintf(INFO, "flash_write_image: bad block @ %d\n", page >> 6);
628 }
629 page += 64;
630 }
631
632 dprintf(INFO, "flash_write_image: success\n");
633 return 0;
634}
635
636#if 0
637static int flash_read_page(unsigned page, void *data, void *extra)
638{
639 return _flash_read_page(flash_cmdlist, flash_ptrlist,
640 page, data, extra);
641}
642#endif