blob: b56707d232a20aa38fec9cb669b767683e24e6ac [file] [log] [blame]
wdenk4a5c8a72003-03-06 00:02:04 +00001/*
2 * (C) Copyright 2002
3 * Kyle Harris, Nexus Technologies, Inc. kharris@nexus-tech.net
4 *
5 * (C) Copyright 2002
6 * Sysgo Real-Time Solutions, GmbH <www.elinos.com>
7 * Marius Groeger <mgroeger@sysgo.de>
8 *
9 * (C) Copyright 2002
10 * Robert Schwebel, Pengutronix, <r.schwebel@pengutronix.de>
11 *
12 * See file CREDITS for list of people who contributed to this
13 * project.
14 *
15 * This program is free software; you can redistribute it and/or
16 * modify it under the terms of the GNU General Public License as
17 * published by the Free Software Foundation; either version 2 of
18 * the License, or (at your option) any later version.
19 *
20 * This program is distributed in the hope that it will be useful,
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 * GNU General Public License for more details.
24 *
25 * You should have received a copy of the GNU General Public License
26 * along with this program; if not, write to the Free Software
27 * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
28 * MA 02111-1307 USA
29 */
30
31#include <common.h>
32#include <asm/arch/pxa-regs.h>
33
wdenkcc1e2562003-03-06 13:39:27 +000034#if defined CFG_JFFS_CUSTOM_PART
35#include <jffs2/jffs2.h>
36#endif
37
38/* Debugging macros ------------------------------------------------------ */
39
40#undef FLASH_DEBUG
41//#define FLASH_DEBUG 1
42
43/* Some debug macros */
44#if (FLASH_DEBUG > 2 )
45#define PRINTK3(args...) printf(args)
46#else
47#define PRINTK3(args...)
48#endif
49
50#if FLASH_DEBUG > 1
51#define PRINTK2(args...) printf(args)
52#else
53#define PRINTK2(args...)
54#endif
55
56#ifdef FLASH_DEBUG
57#define PRINTK(args...) printf(args)
58#else
59#define PRINTK(args...)
60#endif
61
62/* ------------------------------------------------------------------------ */
63
64/* Development system: we have only 16 MB Flash */
65#ifdef CONFIG_MTD_INNOKOM_16MB
66#define FLASH_BANK_SIZE 0x01000000 /* 16 MB (during development) */
67#define MAIN_SECT_SIZE 0x00020000 /* 128k per sector */
68#endif
69
70/* Production system: we have 64 MB Flash */
71#ifdef CONFIG_MTD_INNOKOM_64MB
72#define FLASH_BANK_SIZE 0x04000000 /* 64 MB */
73#define MAIN_SECT_SIZE 0x00020000 /* 128k per sector */
74#endif
wdenk4a5c8a72003-03-06 00:02:04 +000075
76flash_info_t flash_info[CFG_MAX_FLASH_BANKS];
77
78
wdenkcc1e2562003-03-06 13:39:27 +000079#if defined CFG_JFFS_CUSTOM_PART
80
81/**
82 * jffs2_part_info - get information about a JFFS2 partition
83 *
84 * @part_num: number of the partition you want to get info about
85 * @return: struct part_info* in case of success, 0 if failure
86 */
87
88static struct part_info part;
wdenkef5fe752003-03-12 10:41:04 +000089static int current_part = -1;
wdenkcc1e2562003-03-06 13:39:27 +000090
91#ifdef CONFIG_MTD_INNOKOM_16MB
92#ifdef CONFIG_MTD_INNOKOM_64MB
93#error Please define only one CONFIG_MTD_INNOKOM_XXMB option.
94#endif
95struct part_info* jffs2_part_info(int part_num) {
wdenkef5fe752003-03-12 10:41:04 +000096 void *jffs2_priv_saved = part.jffs2_priv;
wdenkcc1e2562003-03-06 13:39:27 +000097
98 PRINTK2("jffs2_part_info: part_num=%i\n",part_num);
99
wdenkef5fe752003-03-12 10:41:04 +0000100 if (current_part == part_num)
101 return &part;
102
wdenkcc1e2562003-03-06 13:39:27 +0000103 /* u-boot partition */
104 if(part_num==0){
wdenkcc1e2562003-03-06 13:39:27 +0000105 memset(&part, 0, sizeof(part));
wdenkef5fe752003-03-12 10:41:04 +0000106
wdenkcc1e2562003-03-06 13:39:27 +0000107 part.offset=(char*)0x00000000;
108 part.size=256*1024;
wdenkef5fe752003-03-12 10:41:04 +0000109
wdenkcc1e2562003-03-06 13:39:27 +0000110 /* Mark the struct as ready */
wdenkef5fe752003-03-12 10:41:04 +0000111 current_part = part_num;
wdenkcc1e2562003-03-06 13:39:27 +0000112
113 PRINTK("part.offset = 0x%08x\n",(unsigned int)part.offset);
114 PRINTK("part.size = 0x%08x\n",(unsigned int)part.size);
wdenkcc1e2562003-03-06 13:39:27 +0000115 }
116
117 /* primary OS+firmware partition */
118 if(part_num==1){
wdenkcc1e2562003-03-06 13:39:27 +0000119 memset(&part, 0, sizeof(part));
wdenkef5fe752003-03-12 10:41:04 +0000120
wdenkcc1e2562003-03-06 13:39:27 +0000121 part.offset=(char*)0x00040000;
122 part.size=768*1024;
wdenkef5fe752003-03-12 10:41:04 +0000123
wdenkcc1e2562003-03-06 13:39:27 +0000124 /* Mark the struct as ready */
wdenkef5fe752003-03-12 10:41:04 +0000125 current_part = part_num;
wdenkcc1e2562003-03-06 13:39:27 +0000126
127 PRINTK("part.offset = 0x%08x\n",(unsigned int)part.offset);
128 PRINTK("part.size = 0x%08x\n",(unsigned int)part.size);
wdenkcc1e2562003-03-06 13:39:27 +0000129 }
wdenkef5fe752003-03-12 10:41:04 +0000130
wdenkcc1e2562003-03-06 13:39:27 +0000131 /* secondary OS+firmware partition */
132 if(part_num==2){
wdenkcc1e2562003-03-06 13:39:27 +0000133 memset(&part, 0, sizeof(part));
wdenkef5fe752003-03-12 10:41:04 +0000134
wdenkcc1e2562003-03-06 13:39:27 +0000135 part.offset=(char*)0x00100000;
136 part.size=8*1024*1024;
wdenkef5fe752003-03-12 10:41:04 +0000137
wdenkcc1e2562003-03-06 13:39:27 +0000138 /* Mark the struct as ready */
wdenkef5fe752003-03-12 10:41:04 +0000139 current_part = part_num;
wdenkcc1e2562003-03-06 13:39:27 +0000140
141 PRINTK("part.offset = 0x%08x\n",(unsigned int)part.offset);
142 PRINTK("part.size = 0x%08x\n",(unsigned int)part.size);
wdenkcc1e2562003-03-06 13:39:27 +0000143 }
144
145 /* data partition */
146 if(part_num==3){
wdenkcc1e2562003-03-06 13:39:27 +0000147 memset(&part, 0, sizeof(part));
wdenkef5fe752003-03-12 10:41:04 +0000148
wdenkcc1e2562003-03-06 13:39:27 +0000149 part.offset=(char*)0x00900000;
150 part.size=7*1024*1024;
wdenkef5fe752003-03-12 10:41:04 +0000151
wdenkcc1e2562003-03-06 13:39:27 +0000152 /* Mark the struct as ready */
wdenkef5fe752003-03-12 10:41:04 +0000153 current_part = part_num;
wdenkcc1e2562003-03-06 13:39:27 +0000154
155 PRINTK("part.offset = 0x%08x\n",(unsigned int)part.offset);
156 PRINTK("part.size = 0x%08x\n",(unsigned int)part.size);
wdenkef5fe752003-03-12 10:41:04 +0000157 }
158
159 if (current_part == part_num) {
160 part.usr_priv = &current_part;
161 part.jffs2_priv = jffs2_priv_saved;
wdenkcc1e2562003-03-06 13:39:27 +0000162 return &part;
163 }
164
165 PRINTK("jffs2_part_info: end of partition table\n");
166 return 0;
167}
168#endif /* CONFIG_MTD_INNOKOM_16MB */
169
170#ifdef CONFIG_MTD_INNOKOM_64MB
171#ifdef CONFIG_MTD_INNOKOM_16MB
172#error Please define only one CONFIG_MTD_INNOKOM_XXMB option.
173#endif
174struct part_info* jffs2_part_info(int part_num) {
wdenkef5fe752003-03-12 10:41:04 +0000175 void *jffs2_priv_saved = part.jffs2_priv;
wdenkcc1e2562003-03-06 13:39:27 +0000176
177 PRINTK2("jffs2_part_info: part_num=%i\n",part_num);
178
wdenkef5fe752003-03-12 10:41:04 +0000179 if (current_part == part_num)
180 return &part;
181
wdenkcc1e2562003-03-06 13:39:27 +0000182 /* u-boot partition */
183 if(part_num==0){
wdenkcc1e2562003-03-06 13:39:27 +0000184 memset(&part, 0, sizeof(part));
wdenkef5fe752003-03-12 10:41:04 +0000185
wdenkcc1e2562003-03-06 13:39:27 +0000186 part.offset=(char*)0x00000000;
187 part.size=256*1024;
wdenkef5fe752003-03-12 10:41:04 +0000188
wdenkcc1e2562003-03-06 13:39:27 +0000189 /* Mark the struct as ready */
wdenkef5fe752003-03-12 10:41:04 +0000190 current_part = part_num;
wdenkcc1e2562003-03-06 13:39:27 +0000191
192 PRINTK("part.offset = 0x%08x\n",(unsigned int)part.offset);
193 PRINTK("part.size = 0x%08x\n",(unsigned int)part.size);
wdenkcc1e2562003-03-06 13:39:27 +0000194 }
195
196 /* primary OS+firmware partition */
197 if(part_num==1){
wdenkcc1e2562003-03-06 13:39:27 +0000198 memset(&part, 0, sizeof(part));
wdenkef5fe752003-03-12 10:41:04 +0000199
wdenkcc1e2562003-03-06 13:39:27 +0000200 part.offset=(char*)0x00040000;
201 part.size=16*1024*1024-128*1024;
wdenkef5fe752003-03-12 10:41:04 +0000202
wdenkcc1e2562003-03-06 13:39:27 +0000203 /* Mark the struct as ready */
wdenkef5fe752003-03-12 10:41:04 +0000204 current_part = part_num;
wdenkcc1e2562003-03-06 13:39:27 +0000205
206 PRINTK("part.offset = 0x%08x\n",(unsigned int)part.offset);
207 PRINTK("part.size = 0x%08x\n",(unsigned int)part.size);
wdenkcc1e2562003-03-06 13:39:27 +0000208 }
wdenkef5fe752003-03-12 10:41:04 +0000209
wdenkcc1e2562003-03-06 13:39:27 +0000210 /* secondary OS+firmware partition */
211 if(part_num==2){
wdenkcc1e2562003-03-06 13:39:27 +0000212 memset(&part, 0, sizeof(part));
wdenkef5fe752003-03-12 10:41:04 +0000213
wdenkcc1e2562003-03-06 13:39:27 +0000214 part.offset=(char*)0x01020000;
215 part.size=16*1024*1024-128*1024;
wdenkef5fe752003-03-12 10:41:04 +0000216
wdenkcc1e2562003-03-06 13:39:27 +0000217 /* Mark the struct as ready */
wdenkef5fe752003-03-12 10:41:04 +0000218 current_part = part_num;
wdenkcc1e2562003-03-06 13:39:27 +0000219
220 PRINTK("part.offset = 0x%08x\n",(unsigned int)part.offset);
221 PRINTK("part.size = 0x%08x\n",(unsigned int)part.size);
wdenkcc1e2562003-03-06 13:39:27 +0000222 }
223
224 /* data partition */
225 if(part_num==3){
wdenkcc1e2562003-03-06 13:39:27 +0000226 memset(&part, 0, sizeof(part));
wdenkef5fe752003-03-12 10:41:04 +0000227
wdenkcc1e2562003-03-06 13:39:27 +0000228 part.offset=(char*)0x02000000;
229 part.size=32*1024*1024;
wdenkef5fe752003-03-12 10:41:04 +0000230
wdenkcc1e2562003-03-06 13:39:27 +0000231 /* Mark the struct as ready */
wdenkef5fe752003-03-12 10:41:04 +0000232 current_part = part_num;
wdenkcc1e2562003-03-06 13:39:27 +0000233
234 PRINTK("part.offset = 0x%08x\n",(unsigned int)part.offset);
235 PRINTK("part.size = 0x%08x\n",(unsigned int)part.size);
wdenkef5fe752003-03-12 10:41:04 +0000236 }
237
238 if (current_part == part_num) {
239 part.usr_priv = &current_part;
240 part.jffs2_priv = jffs2_priv_saved;
wdenkcc1e2562003-03-06 13:39:27 +0000241 return &part;
242 }
243
244 PRINTK("jffs2_part_info: end of partition table\n");
245 return 0;
246}
247#endif /* CONFIG_MTD_INNOKOM_64MB */
248#endif /* defined CFG_JFFS_CUSTOM_PART */
249
250
wdenk4a5c8a72003-03-06 00:02:04 +0000251/**
252 * flash_init: - initialize data structures for flash chips
253 *
254 * @return: size of the flash
255 */
256
257ulong flash_init(void)
258{
259 int i, j;
260 ulong size = 0;
261
262 for (i = 0; i < CFG_MAX_FLASH_BANKS; i++) {
263 ulong flashbase = 0;
264 flash_info[i].flash_id =
265 (INTEL_MANUFACT & FLASH_VENDMASK) |
266 (INTEL_ID_28F128J3 & FLASH_TYPEMASK);
267 flash_info[i].size = FLASH_BANK_SIZE;
268 flash_info[i].sector_count = CFG_MAX_FLASH_SECT;
269 memset(flash_info[i].protect, 0, CFG_MAX_FLASH_SECT);
270
271 switch (i) {
272 case 0:
273 flashbase = PHYS_FLASH_1;
274 break;
275 default:
276 panic("configured to many flash banks!\n");
277 break;
278 }
279 for (j = 0; j < flash_info[i].sector_count; j++) {
280 flash_info[i].start[j] = flashbase + j*MAIN_SECT_SIZE;
281 }
282 size += flash_info[i].size;
283 }
284
wdenkcc1e2562003-03-06 13:39:27 +0000285 /* Protect u-boot sectors */
wdenk4a5c8a72003-03-06 00:02:04 +0000286 flash_protect(FLAG_PROTECT_SET,
287 CFG_FLASH_BASE,
wdenkcc1e2562003-03-06 13:39:27 +0000288 CFG_FLASH_BASE + (256*1024) - 1,
wdenk4a5c8a72003-03-06 00:02:04 +0000289 &flash_info[0]);
290
291#ifdef CFG_ENV_IS_IN_FLASH
292 flash_protect(FLAG_PROTECT_SET,
293 CFG_ENV_ADDR,
294 CFG_ENV_ADDR + CFG_ENV_SIZE - 1,
295 &flash_info[0]);
296#endif
297
298 return size;
299}
300
301
302/**
303 * flash_print_info: - print information about the flash situation
304 *
305 * @param info:
306 */
307
308void flash_print_info (flash_info_t *info)
309{
310 int i, j;
311
312 for (j=0; j<CFG_MAX_FLASH_BANKS; j++) {
313
314 switch (info->flash_id & FLASH_VENDMASK) {
315
316 case (INTEL_MANUFACT & FLASH_VENDMASK):
317 printf("Intel: ");
318 break;
319 default:
320 printf("Unknown Vendor ");
321 break;
322 }
323
324 switch (info->flash_id & FLASH_TYPEMASK) {
325
326 case (INTEL_ID_28F128J3 & FLASH_TYPEMASK):
327 printf("28F128J3 (128Mbit)\n");
328 break;
329 default:
330 printf("Unknown Chip Type\n");
331 return;
332 }
333
wdenkef5fe752003-03-12 10:41:04 +0000334 printf(" Size: %ld MB in %d Sectors\n",
wdenk4a5c8a72003-03-06 00:02:04 +0000335 info->size >> 20, info->sector_count);
336
337 printf(" Sector Start Addresses:");
338 for (i = 0; i < info->sector_count; i++) {
339 if ((i % 5) == 0) printf ("\n ");
wdenkef5fe752003-03-12 10:41:04 +0000340
wdenk4a5c8a72003-03-06 00:02:04 +0000341 printf (" %08lX%s", info->start[i],
342 info->protect[i] ? " (RO)" : " ");
343 }
344 printf ("\n");
345 info++;
346 }
347}
348
349
350/**
351 * flash_erase: - erase flash sectors
352 *
353 */
354
355int flash_erase(flash_info_t *info, int s_first, int s_last)
356{
357 int flag, prot, sect;
358 int rc = ERR_OK;
359
360 if (info->flash_id == FLASH_UNKNOWN)
361 return ERR_UNKNOWN_FLASH_TYPE;
362
363 if ((s_first < 0) || (s_first > s_last)) {
364 return ERR_INVAL;
365 }
366
367 if ((info->flash_id & FLASH_VENDMASK) != (INTEL_MANUFACT & FLASH_VENDMASK))
368 return ERR_UNKNOWN_FLASH_VENDOR;
wdenkef5fe752003-03-12 10:41:04 +0000369
wdenk4a5c8a72003-03-06 00:02:04 +0000370 prot = 0;
371 for (sect=s_first; sect<=s_last; ++sect) {
372 if (info->protect[sect]) prot++;
373 }
374
375 if (prot) return ERR_PROTECTED;
376
377 /*
378 * Disable interrupts which might cause a timeout
379 * here. Remember that our exception vectors are
380 * at address 0 in the flash, and we don't want a
381 * (ticker) exception to happen while the flash
382 * chip is in programming mode.
383 */
384
385 flag = disable_interrupts();
386
387 /* Start erase on unprotected sectors */
388 for (sect = s_first; sect<=s_last && !ctrlc(); sect++) {
389
390 printf("Erasing sector %2d ... ", sect);
391
wdenkcc1e2562003-03-06 13:39:27 +0000392 PRINTK("\n");
393
wdenk4a5c8a72003-03-06 00:02:04 +0000394 /* arm simple, non interrupt dependent timer */
395 reset_timer_masked();
396
397 if (info->protect[sect] == 0) { /* not protected */
wdenkcc1e2562003-03-06 13:39:27 +0000398 u16 * volatile addr = (u16 * volatile)(info->start[sect]);
wdenk4a5c8a72003-03-06 00:02:04 +0000399
wdenkcc1e2562003-03-06 13:39:27 +0000400 PRINTK("unlocking sector\n");
401 *addr = 0x0060;
402 *addr = 0x00d0;
403 *addr = 0x00ff;
wdenk4a5c8a72003-03-06 00:02:04 +0000404
wdenkcc1e2562003-03-06 13:39:27 +0000405 PRINTK("erasing sector\n");
406 *addr = 0x0020;
407 PRINTK("confirming erase\n");
408 *addr = 0x00D0;
wdenk4a5c8a72003-03-06 00:02:04 +0000409
wdenkcc1e2562003-03-06 13:39:27 +0000410 while ((*addr & 0x0080) != 0x0080) {
411 PRINTK(".");
wdenk4a5c8a72003-03-06 00:02:04 +0000412 if (get_timer_masked() > CFG_FLASH_ERASE_TOUT) {
wdenkcc1e2562003-03-06 13:39:27 +0000413 *addr = 0x00B0; /* suspend erase*/
414 *addr = 0x00FF; /* read mode */
wdenk4a5c8a72003-03-06 00:02:04 +0000415 rc = ERR_TIMOUT;
416 goto outahere;
417 }
418 }
wdenkef5fe752003-03-12 10:41:04 +0000419
wdenkcc1e2562003-03-06 13:39:27 +0000420 PRINTK("clearing status register\n");
wdenkef5fe752003-03-12 10:41:04 +0000421 *addr = 0x0050;
wdenkcc1e2562003-03-06 13:39:27 +0000422 PRINTK("resetting to read mode");
wdenkef5fe752003-03-12 10:41:04 +0000423 *addr = 0x00FF;
wdenk4a5c8a72003-03-06 00:02:04 +0000424 }
wdenkef5fe752003-03-12 10:41:04 +0000425
wdenk4a5c8a72003-03-06 00:02:04 +0000426 printf("ok.\n");
427 }
428
429 if (ctrlc()) printf("User Interrupt!\n");
430
431 outahere:
432
433 /* allow flash to settle - wait 10 ms */
434 udelay_masked(10000);
435
436 if (flag) enable_interrupts();
437
438 return rc;
439}
440
441
442/**
443 * write_word: - copy memory to flash
444 *
445 * @param info:
446 * @param dest:
447 * @param data:
448 * @return:
449 */
450
451static int write_word (flash_info_t *info, ulong dest, ushort data)
452{
wdenkcc1e2562003-03-06 13:39:27 +0000453 volatile u16 *addr = (u16 *)dest, val;
wdenk4a5c8a72003-03-06 00:02:04 +0000454 int rc = ERR_OK;
455 int flag;
456
457 /* Check if Flash is (sufficiently) erased */
458 if ((*addr & data) != data) return ERR_NOT_ERASED;
459
460 /*
461 * Disable interrupts which might cause a timeout
462 * here. Remember that our exception vectors are
463 * at address 0 in the flash, and we don't want a
464 * (ticker) exception to happen while the flash
465 * chip is in programming mode.
466 */
467 flag = disable_interrupts();
468
469 /* clear status register command */
470 *addr = 0x50;
471
472 /* program set-up command */
473 *addr = 0x40;
474
475 /* latch address/data */
476 *addr = data;
477
478 /* arm simple, non interrupt dependent timer */
479 reset_timer_masked();
480
481 /* wait while polling the status register */
482 while(((val = *addr) & 0x80) != 0x80) {
483 if (get_timer_masked() > CFG_FLASH_WRITE_TOUT) {
484 rc = ERR_TIMOUT;
485 *addr = 0xB0; /* suspend program command */
486 goto outahere;
487 }
488 }
489
490 if(val & 0x1A) { /* check for error */
491 printf("\nFlash write error %02x at address %08lx\n",
492 (int)val, (unsigned long)dest);
493 if(val & (1<<3)) {
494 printf("Voltage range error.\n");
495 rc = ERR_PROG_ERROR;
496 goto outahere;
497 }
498 if(val & (1<<1)) {
499 printf("Device protect error.\n");
500 rc = ERR_PROTECTED;
501 goto outahere;
502 }
503 if(val & (1<<4)) {
504 printf("Programming error.\n");
505 rc = ERR_PROG_ERROR;
506 goto outahere;
507 }
508 rc = ERR_PROG_ERROR;
509 goto outahere;
510 }
511
512 outahere:
513
514 *addr = 0xFF; /* read array command */
515 if (flag) enable_interrupts();
516
517 return rc;
518}
519
520
521/**
522 * write_buf: - Copy memory to flash.
523 *
524 * @param info:
525 * @param src: source of copy transaction
526 * @param addr: where to copy to
527 * @param cnt: number of bytes to copy
528 *
529 * @return error code
530 */
531
532int write_buff (flash_info_t *info, uchar *src, ulong addr, ulong cnt)
533{
534 ulong cp, wp;
535 ushort data;
536 int l;
537 int i, rc;
538
539 wp = (addr & ~1); /* get lower word aligned address */
540
541 /*
542 * handle unaligned start bytes
543 */
544 if ((l = addr - wp) != 0) {
545 data = 0;
546 for (i=0, cp=wp; i<l; ++i, ++cp) {
547 data = (data >> 8) | (*(uchar *)cp << 8);
548 }
549 for (; i<2 && cnt>0; ++i) {
550 data = (data >> 8) | (*src++ << 8);
551 --cnt;
552 ++cp;
553 }
554 for (; cnt==0 && i<2; ++i, ++cp) {
555 data = (data >> 8) | (*(uchar *)cp << 8);
556 }
557
558 if ((rc = write_word(info, wp, data)) != 0) {
559 return (rc);
560 }
561 wp += 2;
562 }
563
564 /*
565 * handle word aligned part
566 */
567 while (cnt >= 2) {
568 /* data = *((vushort*)src); */
569 data = *((ushort*)src);
570 if ((rc = write_word(info, wp, data)) != 0) {
571 return (rc);
572 }
573 src += 2;
574 wp += 2;
575 cnt -= 2;
576 }
577
578 if (cnt == 0) return ERR_OK;
579
580 /*
581 * handle unaligned tail bytes
582 */
583 data = 0;
584 for (i=0, cp=wp; i<2 && cnt>0; ++i, ++cp) {
585 data = (data >> 8) | (*src++ << 8);
586 --cnt;
587 }
588 for (; i<2; ++i, ++cp) {
589 data = (data >> 8) | (*(uchar *)cp << 8);
590 }
591
592 return write_word(info, wp, data);
593}
594