blob: b7c2072e707f47374681df71157861b3e93eed8c [file] [log] [blame]
wdenk43d96162003-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 *
wdenkdc7c9a12003-03-26 06:55:25 +000012 * (C) Copyright 2002
wdenk3e386912003-04-05 00:53:31 +000013 * Auerswald GmbH & Co KG, Germany
14 * Kai-Uwe Bloem <kai-uwe.bloem@auerswald.de>
wdenkdc7c9a12003-03-26 06:55:25 +000015 *
wdenk43d96162003-03-06 00:02:04 +000016 * See file CREDITS for list of people who contributed to this
17 * project.
18 *
19 * This program is free software; you can redistribute it and/or
20 * modify it under the terms of the GNU General Public License as
21 * published by the Free Software Foundation; either version 2 of
22 * the License, or (at your option) any later version.
23 *
24 * This program is distributed in the hope that it will be useful,
25 * but WITHOUT ANY WARRANTY; without even the implied warranty of
26 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
27 * GNU General Public License for more details.
28 *
29 * You should have received a copy of the GNU General Public License
30 * along with this program; if not, write to the Free Software
31 * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
32 * MA 02111-1307 USA
33 */
34
35#include <common.h>
36#include <asm/arch/pxa-regs.h>
37
wdenk47cd00f2003-03-06 13:39:27 +000038#if defined CFG_JFFS_CUSTOM_PART
39#include <jffs2/jffs2.h>
40#endif
41
42/* Debugging macros ------------------------------------------------------ */
43
44#undef FLASH_DEBUG
45//#define FLASH_DEBUG 1
46
47/* Some debug macros */
48#if (FLASH_DEBUG > 2 )
49#define PRINTK3(args...) printf(args)
50#else
51#define PRINTK3(args...)
52#endif
53
54#if FLASH_DEBUG > 1
55#define PRINTK2(args...) printf(args)
56#else
57#define PRINTK2(args...)
58#endif
59
60#ifdef FLASH_DEBUG
61#define PRINTK(args...) printf(args)
62#else
63#define PRINTK(args...)
64#endif
65
66/* ------------------------------------------------------------------------ */
67
68/* Development system: we have only 16 MB Flash */
69#ifdef CONFIG_MTD_INNOKOM_16MB
70#define FLASH_BANK_SIZE 0x01000000 /* 16 MB (during development) */
71#define MAIN_SECT_SIZE 0x00020000 /* 128k per sector */
72#endif
73
74/* Production system: we have 64 MB Flash */
75#ifdef CONFIG_MTD_INNOKOM_64MB
76#define FLASH_BANK_SIZE 0x04000000 /* 64 MB */
77#define MAIN_SECT_SIZE 0x00020000 /* 128k per sector */
78#endif
wdenk43d96162003-03-06 00:02:04 +000079
80flash_info_t flash_info[CFG_MAX_FLASH_BANKS];
81
82
wdenk47cd00f2003-03-06 13:39:27 +000083#if defined CFG_JFFS_CUSTOM_PART
84
85/**
86 * jffs2_part_info - get information about a JFFS2 partition
87 *
88 * @part_num: number of the partition you want to get info about
89 * @return: struct part_info* in case of success, 0 if failure
90 */
91
92static struct part_info part;
wdenk3bac3512003-03-12 10:41:04 +000093static int current_part = -1;
wdenk47cd00f2003-03-06 13:39:27 +000094
95#ifdef CONFIG_MTD_INNOKOM_16MB
96#ifdef CONFIG_MTD_INNOKOM_64MB
97#error Please define only one CONFIG_MTD_INNOKOM_XXMB option.
98#endif
99struct part_info* jffs2_part_info(int part_num) {
wdenk3bac3512003-03-12 10:41:04 +0000100 void *jffs2_priv_saved = part.jffs2_priv;
wdenk47cd00f2003-03-06 13:39:27 +0000101
102 PRINTK2("jffs2_part_info: part_num=%i\n",part_num);
103
wdenk3bac3512003-03-12 10:41:04 +0000104 if (current_part == part_num)
105 return &part;
106
wdenk47cd00f2003-03-06 13:39:27 +0000107 /* u-boot partition */
108 if(part_num==0){
wdenk47cd00f2003-03-06 13:39:27 +0000109 memset(&part, 0, sizeof(part));
wdenk3bac3512003-03-12 10:41:04 +0000110
wdenk47cd00f2003-03-06 13:39:27 +0000111 part.offset=(char*)0x00000000;
112 part.size=256*1024;
wdenk3bac3512003-03-12 10:41:04 +0000113
wdenk47cd00f2003-03-06 13:39:27 +0000114 /* Mark the struct as ready */
wdenk3bac3512003-03-12 10:41:04 +0000115 current_part = part_num;
wdenk47cd00f2003-03-06 13:39:27 +0000116
117 PRINTK("part.offset = 0x%08x\n",(unsigned int)part.offset);
118 PRINTK("part.size = 0x%08x\n",(unsigned int)part.size);
wdenk47cd00f2003-03-06 13:39:27 +0000119 }
120
121 /* primary OS+firmware partition */
122 if(part_num==1){
wdenk47cd00f2003-03-06 13:39:27 +0000123 memset(&part, 0, sizeof(part));
wdenk3bac3512003-03-12 10:41:04 +0000124
wdenk47cd00f2003-03-06 13:39:27 +0000125 part.offset=(char*)0x00040000;
126 part.size=768*1024;
wdenk3bac3512003-03-12 10:41:04 +0000127
wdenk47cd00f2003-03-06 13:39:27 +0000128 /* Mark the struct as ready */
wdenk3bac3512003-03-12 10:41:04 +0000129 current_part = part_num;
wdenk47cd00f2003-03-06 13:39:27 +0000130
131 PRINTK("part.offset = 0x%08x\n",(unsigned int)part.offset);
132 PRINTK("part.size = 0x%08x\n",(unsigned int)part.size);
wdenk47cd00f2003-03-06 13:39:27 +0000133 }
wdenk3bac3512003-03-12 10:41:04 +0000134
wdenk47cd00f2003-03-06 13:39:27 +0000135 /* secondary OS+firmware partition */
136 if(part_num==2){
wdenk47cd00f2003-03-06 13:39:27 +0000137 memset(&part, 0, sizeof(part));
wdenk3bac3512003-03-12 10:41:04 +0000138
wdenk47cd00f2003-03-06 13:39:27 +0000139 part.offset=(char*)0x00100000;
140 part.size=8*1024*1024;
wdenk3bac3512003-03-12 10:41:04 +0000141
wdenk47cd00f2003-03-06 13:39:27 +0000142 /* Mark the struct as ready */
wdenk3bac3512003-03-12 10:41:04 +0000143 current_part = part_num;
wdenk47cd00f2003-03-06 13:39:27 +0000144
145 PRINTK("part.offset = 0x%08x\n",(unsigned int)part.offset);
146 PRINTK("part.size = 0x%08x\n",(unsigned int)part.size);
wdenk47cd00f2003-03-06 13:39:27 +0000147 }
148
149 /* data partition */
150 if(part_num==3){
wdenk47cd00f2003-03-06 13:39:27 +0000151 memset(&part, 0, sizeof(part));
wdenk3bac3512003-03-12 10:41:04 +0000152
wdenk47cd00f2003-03-06 13:39:27 +0000153 part.offset=(char*)0x00900000;
154 part.size=7*1024*1024;
wdenk3bac3512003-03-12 10:41:04 +0000155
wdenk47cd00f2003-03-06 13:39:27 +0000156 /* Mark the struct as ready */
wdenk3bac3512003-03-12 10:41:04 +0000157 current_part = part_num;
wdenk47cd00f2003-03-06 13:39:27 +0000158
159 PRINTK("part.offset = 0x%08x\n",(unsigned int)part.offset);
160 PRINTK("part.size = 0x%08x\n",(unsigned int)part.size);
wdenk3bac3512003-03-12 10:41:04 +0000161 }
162
163 if (current_part == part_num) {
164 part.usr_priv = &current_part;
165 part.jffs2_priv = jffs2_priv_saved;
wdenk47cd00f2003-03-06 13:39:27 +0000166 return &part;
167 }
168
169 PRINTK("jffs2_part_info: end of partition table\n");
170 return 0;
171}
172#endif /* CONFIG_MTD_INNOKOM_16MB */
173
174#ifdef CONFIG_MTD_INNOKOM_64MB
175#ifdef CONFIG_MTD_INNOKOM_16MB
176#error Please define only one CONFIG_MTD_INNOKOM_XXMB option.
177#endif
178struct part_info* jffs2_part_info(int part_num) {
wdenk3bac3512003-03-12 10:41:04 +0000179 void *jffs2_priv_saved = part.jffs2_priv;
wdenk47cd00f2003-03-06 13:39:27 +0000180
181 PRINTK2("jffs2_part_info: part_num=%i\n",part_num);
182
wdenk3bac3512003-03-12 10:41:04 +0000183 if (current_part == part_num)
184 return &part;
185
wdenk47cd00f2003-03-06 13:39:27 +0000186 /* u-boot partition */
187 if(part_num==0){
wdenk47cd00f2003-03-06 13:39:27 +0000188 memset(&part, 0, sizeof(part));
wdenk3bac3512003-03-12 10:41:04 +0000189
wdenk47cd00f2003-03-06 13:39:27 +0000190 part.offset=(char*)0x00000000;
191 part.size=256*1024;
wdenk3bac3512003-03-12 10:41:04 +0000192
wdenk47cd00f2003-03-06 13:39:27 +0000193 /* Mark the struct as ready */
wdenk3bac3512003-03-12 10:41:04 +0000194 current_part = part_num;
wdenk47cd00f2003-03-06 13:39:27 +0000195
196 PRINTK("part.offset = 0x%08x\n",(unsigned int)part.offset);
197 PRINTK("part.size = 0x%08x\n",(unsigned int)part.size);
wdenk47cd00f2003-03-06 13:39:27 +0000198 }
199
200 /* primary OS+firmware partition */
201 if(part_num==1){
wdenk47cd00f2003-03-06 13:39:27 +0000202 memset(&part, 0, sizeof(part));
wdenk3bac3512003-03-12 10:41:04 +0000203
wdenk47cd00f2003-03-06 13:39:27 +0000204 part.offset=(char*)0x00040000;
205 part.size=16*1024*1024-128*1024;
wdenk3bac3512003-03-12 10:41:04 +0000206
wdenk47cd00f2003-03-06 13:39:27 +0000207 /* Mark the struct as ready */
wdenk3bac3512003-03-12 10:41:04 +0000208 current_part = part_num;
wdenk47cd00f2003-03-06 13:39:27 +0000209
210 PRINTK("part.offset = 0x%08x\n",(unsigned int)part.offset);
211 PRINTK("part.size = 0x%08x\n",(unsigned int)part.size);
wdenk47cd00f2003-03-06 13:39:27 +0000212 }
wdenk3bac3512003-03-12 10:41:04 +0000213
wdenk47cd00f2003-03-06 13:39:27 +0000214 /* secondary OS+firmware partition */
215 if(part_num==2){
wdenk47cd00f2003-03-06 13:39:27 +0000216 memset(&part, 0, sizeof(part));
wdenk3bac3512003-03-12 10:41:04 +0000217
wdenk47cd00f2003-03-06 13:39:27 +0000218 part.offset=(char*)0x01020000;
219 part.size=16*1024*1024-128*1024;
wdenk3bac3512003-03-12 10:41:04 +0000220
wdenk47cd00f2003-03-06 13:39:27 +0000221 /* Mark the struct as ready */
wdenk3bac3512003-03-12 10:41:04 +0000222 current_part = part_num;
wdenk47cd00f2003-03-06 13:39:27 +0000223
224 PRINTK("part.offset = 0x%08x\n",(unsigned int)part.offset);
225 PRINTK("part.size = 0x%08x\n",(unsigned int)part.size);
wdenk47cd00f2003-03-06 13:39:27 +0000226 }
227
228 /* data partition */
229 if(part_num==3){
wdenk47cd00f2003-03-06 13:39:27 +0000230 memset(&part, 0, sizeof(part));
wdenk3bac3512003-03-12 10:41:04 +0000231
wdenk47cd00f2003-03-06 13:39:27 +0000232 part.offset=(char*)0x02000000;
233 part.size=32*1024*1024;
wdenk3bac3512003-03-12 10:41:04 +0000234
wdenk47cd00f2003-03-06 13:39:27 +0000235 /* Mark the struct as ready */
wdenk3bac3512003-03-12 10:41:04 +0000236 current_part = part_num;
wdenk47cd00f2003-03-06 13:39:27 +0000237
238 PRINTK("part.offset = 0x%08x\n",(unsigned int)part.offset);
239 PRINTK("part.size = 0x%08x\n",(unsigned int)part.size);
wdenk3bac3512003-03-12 10:41:04 +0000240 }
241
242 if (current_part == part_num) {
243 part.usr_priv = &current_part;
244 part.jffs2_priv = jffs2_priv_saved;
wdenk47cd00f2003-03-06 13:39:27 +0000245 return &part;
246 }
247
248 PRINTK("jffs2_part_info: end of partition table\n");
249 return 0;
250}
251#endif /* CONFIG_MTD_INNOKOM_64MB */
252#endif /* defined CFG_JFFS_CUSTOM_PART */
253
254
wdenk43d96162003-03-06 00:02:04 +0000255/**
256 * flash_init: - initialize data structures for flash chips
257 *
258 * @return: size of the flash
259 */
260
261ulong flash_init(void)
262{
263 int i, j;
264 ulong size = 0;
265
266 for (i = 0; i < CFG_MAX_FLASH_BANKS; i++) {
267 ulong flashbase = 0;
268 flash_info[i].flash_id =
269 (INTEL_MANUFACT & FLASH_VENDMASK) |
270 (INTEL_ID_28F128J3 & FLASH_TYPEMASK);
271 flash_info[i].size = FLASH_BANK_SIZE;
272 flash_info[i].sector_count = CFG_MAX_FLASH_SECT;
273 memset(flash_info[i].protect, 0, CFG_MAX_FLASH_SECT);
274
275 switch (i) {
276 case 0:
277 flashbase = PHYS_FLASH_1;
278 break;
279 default:
280 panic("configured to many flash banks!\n");
281 break;
282 }
283 for (j = 0; j < flash_info[i].sector_count; j++) {
284 flash_info[i].start[j] = flashbase + j*MAIN_SECT_SIZE;
285 }
286 size += flash_info[i].size;
287 }
288
wdenk47cd00f2003-03-06 13:39:27 +0000289 /* Protect u-boot sectors */
wdenk43d96162003-03-06 00:02:04 +0000290 flash_protect(FLAG_PROTECT_SET,
291 CFG_FLASH_BASE,
wdenk47cd00f2003-03-06 13:39:27 +0000292 CFG_FLASH_BASE + (256*1024) - 1,
wdenk43d96162003-03-06 00:02:04 +0000293 &flash_info[0]);
294
295#ifdef CFG_ENV_IS_IN_FLASH
296 flash_protect(FLAG_PROTECT_SET,
297 CFG_ENV_ADDR,
298 CFG_ENV_ADDR + CFG_ENV_SIZE - 1,
299 &flash_info[0]);
300#endif
301
302 return size;
303}
304
305
306/**
307 * flash_print_info: - print information about the flash situation
308 *
309 * @param info:
310 */
311
312void flash_print_info (flash_info_t *info)
313{
314 int i, j;
315
316 for (j=0; j<CFG_MAX_FLASH_BANKS; j++) {
317
318 switch (info->flash_id & FLASH_VENDMASK) {
319
320 case (INTEL_MANUFACT & FLASH_VENDMASK):
321 printf("Intel: ");
322 break;
323 default:
324 printf("Unknown Vendor ");
325 break;
326 }
327
328 switch (info->flash_id & FLASH_TYPEMASK) {
329
330 case (INTEL_ID_28F128J3 & FLASH_TYPEMASK):
331 printf("28F128J3 (128Mbit)\n");
332 break;
333 default:
334 printf("Unknown Chip Type\n");
335 return;
336 }
337
wdenk3bac3512003-03-12 10:41:04 +0000338 printf(" Size: %ld MB in %d Sectors\n",
wdenk43d96162003-03-06 00:02:04 +0000339 info->size >> 20, info->sector_count);
340
341 printf(" Sector Start Addresses:");
342 for (i = 0; i < info->sector_count; i++) {
343 if ((i % 5) == 0) printf ("\n ");
wdenk3bac3512003-03-12 10:41:04 +0000344
wdenk43d96162003-03-06 00:02:04 +0000345 printf (" %08lX%s", info->start[i],
346 info->protect[i] ? " (RO)" : " ");
347 }
348 printf ("\n");
349 info++;
350 }
351}
352
353
354/**
355 * flash_erase: - erase flash sectors
356 *
357 */
358
359int flash_erase(flash_info_t *info, int s_first, int s_last)
360{
361 int flag, prot, sect;
362 int rc = ERR_OK;
363
364 if (info->flash_id == FLASH_UNKNOWN)
365 return ERR_UNKNOWN_FLASH_TYPE;
366
367 if ((s_first < 0) || (s_first > s_last)) {
368 return ERR_INVAL;
369 }
370
371 if ((info->flash_id & FLASH_VENDMASK) != (INTEL_MANUFACT & FLASH_VENDMASK))
372 return ERR_UNKNOWN_FLASH_VENDOR;
wdenk3bac3512003-03-12 10:41:04 +0000373
wdenk43d96162003-03-06 00:02:04 +0000374 prot = 0;
375 for (sect=s_first; sect<=s_last; ++sect) {
376 if (info->protect[sect]) prot++;
377 }
378
379 if (prot) return ERR_PROTECTED;
380
381 /*
382 * Disable interrupts which might cause a timeout
383 * here. Remember that our exception vectors are
384 * at address 0 in the flash, and we don't want a
385 * (ticker) exception to happen while the flash
386 * chip is in programming mode.
387 */
388
389 flag = disable_interrupts();
390
391 /* Start erase on unprotected sectors */
392 for (sect = s_first; sect<=s_last && !ctrlc(); sect++) {
393
394 printf("Erasing sector %2d ... ", sect);
395
wdenk47cd00f2003-03-06 13:39:27 +0000396 PRINTK("\n");
397
wdenk43d96162003-03-06 00:02:04 +0000398 /* arm simple, non interrupt dependent timer */
399 reset_timer_masked();
400
401 if (info->protect[sect] == 0) { /* not protected */
wdenk47cd00f2003-03-06 13:39:27 +0000402 u16 * volatile addr = (u16 * volatile)(info->start[sect]);
wdenk43d96162003-03-06 00:02:04 +0000403
wdenk47cd00f2003-03-06 13:39:27 +0000404 PRINTK("unlocking sector\n");
405 *addr = 0x0060;
406 *addr = 0x00d0;
407 *addr = 0x00ff;
wdenk43d96162003-03-06 00:02:04 +0000408
wdenk47cd00f2003-03-06 13:39:27 +0000409 PRINTK("erasing sector\n");
410 *addr = 0x0020;
411 PRINTK("confirming erase\n");
412 *addr = 0x00D0;
wdenk43d96162003-03-06 00:02:04 +0000413
wdenk47cd00f2003-03-06 13:39:27 +0000414 while ((*addr & 0x0080) != 0x0080) {
415 PRINTK(".");
wdenk43d96162003-03-06 00:02:04 +0000416 if (get_timer_masked() > CFG_FLASH_ERASE_TOUT) {
wdenk47cd00f2003-03-06 13:39:27 +0000417 *addr = 0x00B0; /* suspend erase*/
418 *addr = 0x00FF; /* read mode */
wdenk43d96162003-03-06 00:02:04 +0000419 rc = ERR_TIMOUT;
420 goto outahere;
421 }
422 }
wdenk3bac3512003-03-12 10:41:04 +0000423
wdenk47cd00f2003-03-06 13:39:27 +0000424 PRINTK("clearing status register\n");
wdenk3bac3512003-03-12 10:41:04 +0000425 *addr = 0x0050;
wdenk47cd00f2003-03-06 13:39:27 +0000426 PRINTK("resetting to read mode");
wdenk3bac3512003-03-12 10:41:04 +0000427 *addr = 0x00FF;
wdenk43d96162003-03-06 00:02:04 +0000428 }
wdenk3bac3512003-03-12 10:41:04 +0000429
wdenk43d96162003-03-06 00:02:04 +0000430 printf("ok.\n");
431 }
432
433 if (ctrlc()) printf("User Interrupt!\n");
434
435 outahere:
436
437 /* allow flash to settle - wait 10 ms */
438 udelay_masked(10000);
439
440 if (flag) enable_interrupts();
441
442 return rc;
443}
444
445
446/**
447 * write_word: - copy memory to flash
448 *
449 * @param info:
450 * @param dest:
451 * @param data:
452 * @return:
453 */
454
455static int write_word (flash_info_t *info, ulong dest, ushort data)
456{
wdenk47cd00f2003-03-06 13:39:27 +0000457 volatile u16 *addr = (u16 *)dest, val;
wdenk43d96162003-03-06 00:02:04 +0000458 int rc = ERR_OK;
459 int flag;
460
461 /* Check if Flash is (sufficiently) erased */
462 if ((*addr & data) != data) return ERR_NOT_ERASED;
463
464 /*
465 * Disable interrupts which might cause a timeout
466 * here. Remember that our exception vectors are
467 * at address 0 in the flash, and we don't want a
468 * (ticker) exception to happen while the flash
469 * chip is in programming mode.
470 */
471 flag = disable_interrupts();
472
473 /* clear status register command */
474 *addr = 0x50;
475
476 /* program set-up command */
477 *addr = 0x40;
478
479 /* latch address/data */
480 *addr = data;
481
482 /* arm simple, non interrupt dependent timer */
483 reset_timer_masked();
484
485 /* wait while polling the status register */
486 while(((val = *addr) & 0x80) != 0x80) {
487 if (get_timer_masked() > CFG_FLASH_WRITE_TOUT) {
488 rc = ERR_TIMOUT;
489 *addr = 0xB0; /* suspend program command */
490 goto outahere;
491 }
492 }
493
494 if(val & 0x1A) { /* check for error */
495 printf("\nFlash write error %02x at address %08lx\n",
496 (int)val, (unsigned long)dest);
497 if(val & (1<<3)) {
498 printf("Voltage range error.\n");
499 rc = ERR_PROG_ERROR;
500 goto outahere;
501 }
502 if(val & (1<<1)) {
503 printf("Device protect error.\n");
504 rc = ERR_PROTECTED;
505 goto outahere;
506 }
507 if(val & (1<<4)) {
508 printf("Programming error.\n");
509 rc = ERR_PROG_ERROR;
510 goto outahere;
511 }
512 rc = ERR_PROG_ERROR;
513 goto outahere;
514 }
515
516 outahere:
517
518 *addr = 0xFF; /* read array command */
519 if (flag) enable_interrupts();
520
521 return rc;
522}
523
524
525/**
526 * write_buf: - Copy memory to flash.
527 *
528 * @param info:
529 * @param src: source of copy transaction
530 * @param addr: where to copy to
531 * @param cnt: number of bytes to copy
532 *
533 * @return error code
534 */
535
536int write_buff (flash_info_t *info, uchar *src, ulong addr, ulong cnt)
537{
538 ulong cp, wp;
539 ushort data;
540 int l;
541 int i, rc;
542
543 wp = (addr & ~1); /* get lower word aligned address */
544
545 /*
546 * handle unaligned start bytes
547 */
548 if ((l = addr - wp) != 0) {
549 data = 0;
550 for (i=0, cp=wp; i<l; ++i, ++cp) {
551 data = (data >> 8) | (*(uchar *)cp << 8);
552 }
553 for (; i<2 && cnt>0; ++i) {
554 data = (data >> 8) | (*src++ << 8);
555 --cnt;
556 ++cp;
557 }
558 for (; cnt==0 && i<2; ++i, ++cp) {
559 data = (data >> 8) | (*(uchar *)cp << 8);
560 }
561
562 if ((rc = write_word(info, wp, data)) != 0) {
563 return (rc);
564 }
565 wp += 2;
566 }
567
568 /*
569 * handle word aligned part
570 */
571 while (cnt >= 2) {
572 /* data = *((vushort*)src); */
573 data = *((ushort*)src);
574 if ((rc = write_word(info, wp, data)) != 0) {
575 return (rc);
576 }
577 src += 2;
578 wp += 2;
579 cnt -= 2;
580 }
581
582 if (cnt == 0) return ERR_OK;
583
584 /*
585 * handle unaligned tail bytes
586 */
587 data = 0;
588 for (i=0, cp=wp; i<2 && cnt>0; ++i, ++cp) {
589 data = (data >> 8) | (*src++ << 8);
590 --cnt;
591 }
592 for (; i<2; ++i, ++cp) {
593 data = (data >> 8) | (*(uchar *)cp << 8);
594 }
595
596 return write_word(info, wp, data);
597}
598