1 /***********************license start***************
2 * Copyright (c) 2003-2010 Cavium Inc. ([email protected]). All rights
3 * reserved.
4 *
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are
8 * met:
9 *
10 * * Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 *
13 * * Redistributions in binary form must reproduce the above
14 * copyright notice, this list of conditions and the following
15 * disclaimer in the documentation and/or other materials provided
16 * with the distribution.
17
18 * * Neither the name of Cavium Inc. nor the names of
19 * its contributors may be used to endorse or promote products
20 * derived from this software without specific prior written
21 * permission.
22
23 * This Software, including technical data, may be subject to U.S. export control
24 * laws, including the U.S. Export Administration Act and its associated
25 * regulations, and may be subject to export or import regulations in other
26 * countries.
27
28 * TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED "AS IS"
29 * AND WITH ALL FAULTS AND CAVIUM INC. MAKES NO PROMISES, REPRESENTATIONS OR
30 * WARRANTIES, EITHER EXPRESS, IMPLIED, STATUTORY, OR OTHERWISE, WITH RESPECT TO
31 * THE SOFTWARE, INCLUDING ITS CONDITION, ITS CONFORMITY TO ANY REPRESENTATION OR
32 * DESCRIPTION, OR THE EXISTENCE OF ANY LATENT OR PATENT DEFECTS, AND CAVIUM
33 * SPECIFICALLY DISCLAIMS ALL IMPLIED (IF ANY) WARRANTIES OF TITLE,
34 * MERCHANTABILITY, NONINFRINGEMENT, FITNESS FOR A PARTICULAR PURPOSE, LACK OF
35 * VIRUSES, ACCURACY OR COMPLETENESS, QUIET ENJOYMENT, QUIET POSSESSION OR
36 * CORRESPONDENCE TO DESCRIPTION. THE ENTIRE RISK ARISING OUT OF USE OR
37 * PERFORMANCE OF THE SOFTWARE LIES WITH YOU.
38 ***********************license end**************************************/
39
40
41
42
43
44
45
46 /**
47 * @file
48 *
49 * This file provides bootbus flash operations
50 *
51 * <hr>$Revision: 70030 $<hr>
52 *
53 *
54 */
55
56 #include "cvmx-config.h"
57 #include "cvmx.h"
58 #include "cvmx-sysinfo.h"
59 #include "cvmx-spinlock.h"
60 #include "cvmx-flash.h"
61
62 #define MAX_NUM_FLASH_CHIPS 8 /* Maximum number of flash chips */
63 #define MAX_NUM_REGIONS 8 /* Maximum number of block regions per chip */
64 #define DEBUG 1
65
66 #define CFI_CMDSET_NONE 0
67 #define CFI_CMDSET_INTEL_EXTENDED 1
68 #define CFI_CMDSET_AMD_STANDARD 2
69 #define CFI_CMDSET_INTEL_STANDARD 3
70 #define CFI_CMDSET_AMD_EXTENDED 4
71 #define CFI_CMDSET_MITSU_STANDARD 256
72 #define CFI_CMDSET_MITSU_EXTENDED 257
73 #define CFI_CMDSET_SST 258
74
75 typedef struct
76 {
77 void * base_ptr; /**< Memory pointer to start of flash */
78 int is_16bit; /**< Chip is 16bits wide in 8bit mode */
79 uint16_t vendor; /**< Vendor ID of Chip */
80 int size; /**< Size of the chip in bytes */
81 uint64_t erase_timeout; /**< Erase timeout in cycles */
82 uint64_t write_timeout; /**< Write timeout in cycles */
83 int num_regions; /**< Number of block regions */
84 cvmx_flash_region_t region[MAX_NUM_REGIONS];
85 } cvmx_flash_t;
86
87 static CVMX_SHARED cvmx_flash_t flash_info[MAX_NUM_FLASH_CHIPS];
88 static CVMX_SHARED cvmx_spinlock_t flash_lock = CVMX_SPINLOCK_UNLOCKED_INITIALIZER;
89
90
91 /**
92 * @INTERNAL
93 * Read a byte from flash
94 *
95 * @param chip_id Chip to read from
96 * @param offset Offset into the chip
97 * @return Value read
98 */
__cvmx_flash_read8(int chip_id,int offset)99 static uint8_t __cvmx_flash_read8(int chip_id, int offset)
100 {
101 return *(volatile uint8_t *)(flash_info[chip_id].base_ptr + offset);
102 }
103
104
105 /**
106 * @INTERNAL
107 * Read a byte from flash (for commands)
108 *
109 * @param chip_id Chip to read from
110 * @param offset Offset into the chip
111 * @return Value read
112 */
__cvmx_flash_read_cmd(int chip_id,int offset)113 static uint8_t __cvmx_flash_read_cmd(int chip_id, int offset)
114 {
115 if (flash_info[chip_id].is_16bit)
116 offset<<=1;
117 return __cvmx_flash_read8(chip_id, offset);
118 }
119
120
121 /**
122 * @INTERNAL
123 * Read 16bits from flash (for commands)
124 *
125 * @param chip_id Chip to read from
126 * @param offset Offset into the chip
127 * @return Value read
128 */
__cvmx_flash_read_cmd16(int chip_id,int offset)129 static uint16_t __cvmx_flash_read_cmd16(int chip_id, int offset)
130 {
131 uint16_t v = __cvmx_flash_read_cmd(chip_id, offset);
132 v |= __cvmx_flash_read_cmd(chip_id, offset + 1)<<8;
133 return v;
134 }
135
136
137 /**
138 * @INTERNAL
139 * Write a byte to flash
140 *
141 * @param chip_id Chip to write to
142 * @param offset Offset into the chip
143 * @param data Value to write
144 */
__cvmx_flash_write8(int chip_id,int offset,uint8_t data)145 static void __cvmx_flash_write8(int chip_id, int offset, uint8_t data)
146 {
147 volatile uint8_t *flash_ptr = (volatile uint8_t *)flash_info[chip_id].base_ptr;
148 flash_ptr[offset] = data;
149 }
150
151
152 /**
153 * @INTERNAL
154 * Write a byte to flash (for commands)
155 *
156 * @param chip_id Chip to write to
157 * @param offset Offset into the chip
158 * @param data Value to write
159 */
__cvmx_flash_write_cmd(int chip_id,int offset,uint8_t data)160 static void __cvmx_flash_write_cmd(int chip_id, int offset, uint8_t data)
161 {
162 volatile uint8_t *flash_ptr = (volatile uint8_t *)flash_info[chip_id].base_ptr;
163 flash_ptr[offset<<flash_info[chip_id].is_16bit] = data;
164 }
165
166
167 /**
168 * @INTERNAL
169 * Query a address and see if a CFI flash chip is there.
170 *
171 * @param chip_id Chip ID data to fill in if the chip is there
172 * @param base_ptr Memory pointer to the start address to query
173 * @return Zero on success, Negative on failure
174 */
__cvmx_flash_queury_cfi(int chip_id,void * base_ptr)175 static int __cvmx_flash_queury_cfi(int chip_id, void *base_ptr)
176 {
177 int region;
178 cvmx_flash_t *flash = flash_info + chip_id;
179
180 /* Set the minimum needed for the read and write primitives to work */
181 flash->base_ptr = base_ptr;
182 flash->is_16bit = 1; /* FIXME: Currently assumes the chip is 16bits */
183
184 /* Put flash in CFI query mode */
185 __cvmx_flash_write_cmd(chip_id, 0x00, 0xf0); /* Reset the flash chip */
186 __cvmx_flash_write_cmd(chip_id, 0x55, 0x98);
187
188 /* Make sure we get the QRY response we should */
189 if ((__cvmx_flash_read_cmd(chip_id, 0x10) != 'Q') ||
190 (__cvmx_flash_read_cmd(chip_id, 0x11) != 'R') ||
191 (__cvmx_flash_read_cmd(chip_id, 0x12) != 'Y'))
192 {
193 flash->base_ptr = NULL;
194 return -1;
195 }
196
197 /* Read the 16bit vendor ID */
198 flash->vendor = __cvmx_flash_read_cmd16(chip_id, 0x13);
199
200 /* Read the write timeout. The timeout is microseconds(us) is 2^0x1f
201 typically. The worst case is this value time 2^0x23 */
202 flash->write_timeout = 1ull << (__cvmx_flash_read_cmd(chip_id, 0x1f) +
203 __cvmx_flash_read_cmd(chip_id, 0x23));
204
205 /* Read the erase timeout. The timeout is milliseconds(ms) is 2^0x21
206 typically. The worst case is this value time 2^0x25 */
207 flash->erase_timeout = 1ull << (__cvmx_flash_read_cmd(chip_id, 0x21) +
208 __cvmx_flash_read_cmd(chip_id, 0x25));
209
210 /* Get the flash size. This is 2^0x27 */
211 flash->size = 1<<__cvmx_flash_read_cmd(chip_id, 0x27);
212
213 /* Get the number of different sized block regions from 0x2c */
214 flash->num_regions = __cvmx_flash_read_cmd(chip_id, 0x2c);
215
216 int start_offset = 0;
217 /* Loop through all regions get information about each */
218 for (region=0; region<flash->num_regions; region++)
219 {
220 cvmx_flash_region_t *rgn_ptr = flash->region + region;
221 rgn_ptr->start_offset = start_offset;
222
223 /* The number of blocks in each region is a 16 bit little endian
224 endian field. It is encoded at 0x2d + region*4 as (blocks-1) */
225 uint16_t blocks = __cvmx_flash_read_cmd16(chip_id, 0x2d + region*4);
226 rgn_ptr->num_blocks = 1u + blocks;
227
228 /* The size of each block is a 16 bit little endian endian field. It
229 is encoded at 0x2d + region*4 + 2 as (size/256). Zero is a special
230 case representing 128 */
231 uint16_t size = __cvmx_flash_read_cmd16(chip_id, 0x2d + region*4 + 2);
232 if (size == 0)
233 rgn_ptr->block_size = 128;
234 else
235 rgn_ptr->block_size = 256u * size;
236
237 start_offset += rgn_ptr->block_size * rgn_ptr->num_blocks;
238 }
239
240 /* Take the chip out of CFI query mode */
241 switch (flash_info[chip_id].vendor)
242 {
243 case CFI_CMDSET_AMD_STANDARD:
244 __cvmx_flash_write_cmd(chip_id, 0x00, 0xf0);
245 case CFI_CMDSET_INTEL_STANDARD:
246 case CFI_CMDSET_INTEL_EXTENDED:
247 __cvmx_flash_write_cmd(chip_id, 0x00, 0xff);
248 break;
249 }
250
251 /* Convert the timeouts to cycles */
252 flash->write_timeout *= cvmx_clock_get_rate(CVMX_CLOCK_CORE) / 1000000;
253 flash->erase_timeout *= cvmx_clock_get_rate(CVMX_CLOCK_CORE) / 1000;
254
255 #if DEBUG
256 /* Print the information about the chip */
257 cvmx_dprintf("cvmx-flash: Base pointer: %p\n"
258 " Vendor: 0x%04x\n"
259 " Size: %d bytes\n"
260 " Num regions: %d\n"
261 " Erase timeout: %llu cycles\n"
262 " Write timeout: %llu cycles\n",
263 flash->base_ptr,
264 (unsigned int)flash->vendor,
265 flash->size,
266 flash->num_regions,
267 (unsigned long long)flash->erase_timeout,
268 (unsigned long long)flash->write_timeout);
269
270 for (region=0; region<flash->num_regions; region++)
271 {
272 cvmx_dprintf(" Region %d: offset 0x%x, %d blocks, %d bytes/block\n",
273 region,
274 flash->region[region].start_offset,
275 flash->region[region].num_blocks,
276 flash->region[region].block_size);
277 }
278 #endif
279
280 return 0;
281 }
282
283
284 /**
285 * Initialize the flash access library
286 */
cvmx_flash_initialize(void)287 void cvmx_flash_initialize(void)
288 {
289 int boot_region;
290 int chip_id = 0;
291
292 memset(flash_info, 0, sizeof(flash_info));
293
294 /* Loop through each boot bus chip select region */
295 for (boot_region=0; boot_region<MAX_NUM_FLASH_CHIPS; boot_region++)
296 {
297 cvmx_mio_boot_reg_cfgx_t region_cfg;
298 region_cfg.u64 = cvmx_read_csr(CVMX_MIO_BOOT_REG_CFG0 + boot_region*8);
299 /* Only try chip select regions that are enabled. This assumes the
300 bootloader already setup the flash */
301 if (region_cfg.s.en)
302 {
303 /* Convert the hardware address to a pointer. Note that the bootbus,
304 unlike memory, isn't 1:1 mapped in the simple exec */
305 void *base_ptr = cvmx_phys_to_ptr((region_cfg.s.base<<16) | 0xffffffff80000000ull);
306 if (__cvmx_flash_queury_cfi(chip_id, base_ptr) == 0)
307 {
308 /* Valid CFI flash chip found */
309 chip_id++;
310 }
311 }
312 }
313
314 if (chip_id == 0)
315 cvmx_dprintf("cvmx-flash: No CFI chips found\n");
316 }
317
318
319 /**
320 * Return a pointer to the flash chip
321 *
322 * @param chip_id Chip ID to return
323 * @return NULL if the chip doesn't exist
324 */
cvmx_flash_get_base(int chip_id)325 void *cvmx_flash_get_base(int chip_id)
326 {
327 return flash_info[chip_id].base_ptr;
328 }
329
330
331 /**
332 * Return the number of erasable regions on the chip
333 *
334 * @param chip_id Chip to return info for
335 * @return Number of regions
336 */
cvmx_flash_get_num_regions(int chip_id)337 int cvmx_flash_get_num_regions(int chip_id)
338 {
339 return flash_info[chip_id].num_regions;
340 }
341
342
343 /**
344 * Return information about a flash chips region
345 *
346 * @param chip_id Chip to get info for
347 * @param region Region to get info for
348 * @return Region information
349 */
cvmx_flash_get_region_info(int chip_id,int region)350 const cvmx_flash_region_t *cvmx_flash_get_region_info(int chip_id, int region)
351 {
352 return flash_info[chip_id].region + region;
353 }
354
355
356 /**
357 * Erase a block on the flash chip
358 *
359 * @param chip_id Chip to erase a block on
360 * @param region Region to erase a block in
361 * @param block Block number to erase
362 * @return Zero on success. Negative on failure
363 */
cvmx_flash_erase_block(int chip_id,int region,int block)364 int cvmx_flash_erase_block(int chip_id, int region, int block)
365 {
366 cvmx_spinlock_lock(&flash_lock);
367 #if DEBUG
368 cvmx_dprintf("cvmx-flash: Erasing chip %d, region %d, block %d\n",
369 chip_id, region, block);
370 #endif
371
372 int offset = flash_info[chip_id].region[region].start_offset +
373 block * flash_info[chip_id].region[region].block_size;
374
375 switch (flash_info[chip_id].vendor)
376 {
377 case CFI_CMDSET_AMD_STANDARD:
378 {
379 /* Send the erase sector command sequence */
380 __cvmx_flash_write_cmd(chip_id, 0x00, 0xf0); /* Reset the flash chip */
381 __cvmx_flash_write_cmd(chip_id, 0x555, 0xaa);
382 __cvmx_flash_write_cmd(chip_id, 0x2aa, 0x55);
383 __cvmx_flash_write_cmd(chip_id, 0x555, 0x80);
384 __cvmx_flash_write_cmd(chip_id, 0x555, 0xaa);
385 __cvmx_flash_write_cmd(chip_id, 0x2aa, 0x55);
386 __cvmx_flash_write8(chip_id, offset, 0x30);
387
388 /* Loop checking status */
389 uint8_t status = __cvmx_flash_read8(chip_id, offset);
390 uint64_t start_cycle = cvmx_get_cycle();
391 while (1)
392 {
393 /* Read the status and xor it with the old status so we can
394 find toggling bits */
395 uint8_t old_status = status;
396 status = __cvmx_flash_read8(chip_id, offset);
397 uint8_t toggle = status ^ old_status;
398
399 /* Check if the erase in progress bit is toggling */
400 if (toggle & (1<<6))
401 {
402 /* Check hardware timeout */
403 if (status & (1<<5))
404 {
405 /* Chip has signalled a timeout. Reread the status */
406 old_status = __cvmx_flash_read8(chip_id, offset);
407 status = __cvmx_flash_read8(chip_id, offset);
408 toggle = status ^ old_status;
409
410 /* Check if the erase in progress bit is toggling */
411 if (toggle & (1<<6))
412 {
413 cvmx_dprintf("cvmx-flash: Hardware timeout erasing block\n");
414 cvmx_spinlock_unlock(&flash_lock);
415 return -1;
416 }
417 else
418 break; /* Not toggling, erase complete */
419 }
420 }
421 else
422 break; /* Not toggling, erase complete */
423
424 if (cvmx_get_cycle() > start_cycle + flash_info[chip_id].erase_timeout)
425 {
426 cvmx_dprintf("cvmx-flash: Timeout erasing block\n");
427 cvmx_spinlock_unlock(&flash_lock);
428 return -1;
429 }
430 }
431
432 __cvmx_flash_write_cmd(chip_id, 0x00, 0xf0); /* Reset the flash chip */
433 cvmx_spinlock_unlock(&flash_lock);
434 return 0;
435 }
436 case CFI_CMDSET_INTEL_STANDARD:
437 case CFI_CMDSET_INTEL_EXTENDED:
438 {
439 /* Send the erase sector command sequence */
440 __cvmx_flash_write_cmd(chip_id, 0x00, 0xff); /* Reset the flash chip */
441 __cvmx_flash_write8(chip_id, offset, 0x20);
442 __cvmx_flash_write8(chip_id, offset, 0xd0);
443
444 /* Loop checking status */
445 uint8_t status = __cvmx_flash_read8(chip_id, offset);
446 uint64_t start_cycle = cvmx_get_cycle();
447 while ((status & 0x80) == 0)
448 {
449 if (cvmx_get_cycle() > start_cycle + flash_info[chip_id].erase_timeout)
450 {
451 cvmx_dprintf("cvmx-flash: Timeout erasing block\n");
452 cvmx_spinlock_unlock(&flash_lock);
453 return -1;
454 }
455 status = __cvmx_flash_read8(chip_id, offset);
456 }
457
458 /* Check the final status */
459 if (status & 0x7f)
460 {
461 cvmx_dprintf("cvmx-flash: Hardware failure erasing block\n");
462 cvmx_spinlock_unlock(&flash_lock);
463 return -1;
464 }
465
466 __cvmx_flash_write_cmd(chip_id, 0x00, 0xff); /* Reset the flash chip */
467 cvmx_spinlock_unlock(&flash_lock);
468 return 0;
469 }
470 }
471
472 cvmx_dprintf("cvmx-flash: Unsupported flash vendor\n");
473 cvmx_spinlock_unlock(&flash_lock);
474 return -1;
475 }
476
477
478 /**
479 * Write a block on the flash chip
480 *
481 * @param chip_id Chip to write a block on
482 * @param region Region to write a block in
483 * @param block Block number to write
484 * @param data Data to write
485 * @return Zero on success. Negative on failure
486 */
cvmx_flash_write_block(int chip_id,int region,int block,const void * data)487 int cvmx_flash_write_block(int chip_id, int region, int block, const void *data)
488 {
489 cvmx_spinlock_lock(&flash_lock);
490 #if DEBUG
491 cvmx_dprintf("cvmx-flash: Writing chip %d, region %d, block %d\n",
492 chip_id, region, block);
493 #endif
494 int offset = flash_info[chip_id].region[region].start_offset +
495 block * flash_info[chip_id].region[region].block_size;
496 int len = flash_info[chip_id].region[region].block_size;
497 const uint8_t *ptr = (const uint8_t *)data;
498
499 switch (flash_info[chip_id].vendor)
500 {
501 case CFI_CMDSET_AMD_STANDARD:
502 {
503 /* Loop through one byte at a time */
504 while (len--)
505 {
506 /* Send the program sequence */
507 __cvmx_flash_write_cmd(chip_id, 0x00, 0xf0); /* Reset the flash chip */
508 __cvmx_flash_write_cmd(chip_id, 0x555, 0xaa);
509 __cvmx_flash_write_cmd(chip_id, 0x2aa, 0x55);
510 __cvmx_flash_write_cmd(chip_id, 0x555, 0xa0);
511 __cvmx_flash_write8(chip_id, offset, *ptr);
512
513 /* Loop polling for status */
514 uint64_t start_cycle = cvmx_get_cycle();
515 while (1)
516 {
517 uint8_t status = __cvmx_flash_read8(chip_id, offset);
518 if (((status ^ *ptr) & (1<<7)) == 0)
519 break; /* Data matches, this byte is done */
520 else if (status & (1<<5))
521 {
522 /* Hardware timeout, recheck status */
523 status = __cvmx_flash_read8(chip_id, offset);
524 if (((status ^ *ptr) & (1<<7)) == 0)
525 break; /* Data matches, this byte is done */
526 else
527 {
528 cvmx_dprintf("cvmx-flash: Hardware write timeout\n");
529 cvmx_spinlock_unlock(&flash_lock);
530 return -1;
531 }
532 }
533
534 if (cvmx_get_cycle() > start_cycle + flash_info[chip_id].write_timeout)
535 {
536 cvmx_dprintf("cvmx-flash: Timeout writing block\n");
537 cvmx_spinlock_unlock(&flash_lock);
538 return -1;
539 }
540 }
541
542 /* Increment to the next byte */
543 ptr++;
544 offset++;
545 }
546
547 __cvmx_flash_write_cmd(chip_id, 0x00, 0xf0); /* Reset the flash chip */
548 cvmx_spinlock_unlock(&flash_lock);
549 return 0;
550 }
551 case CFI_CMDSET_INTEL_STANDARD:
552 case CFI_CMDSET_INTEL_EXTENDED:
553 {
554 cvmx_dprintf("%s:%d len=%d\n", __FUNCTION__, __LINE__, len);
555 /* Loop through one byte at a time */
556 while (len--)
557 {
558 /* Send the program sequence */
559 __cvmx_flash_write_cmd(chip_id, 0x00, 0xff); /* Reset the flash chip */
560 __cvmx_flash_write8(chip_id, offset, 0x40);
561 __cvmx_flash_write8(chip_id, offset, *ptr);
562
563 /* Loop polling for status */
564 uint8_t status = __cvmx_flash_read8(chip_id, offset);
565 uint64_t start_cycle = cvmx_get_cycle();
566 while ((status & 0x80) == 0)
567 {
568 if (cvmx_get_cycle() > start_cycle + flash_info[chip_id].write_timeout)
569 {
570 cvmx_dprintf("cvmx-flash: Timeout writing block\n");
571 cvmx_spinlock_unlock(&flash_lock);
572 return -1;
573 }
574 status = __cvmx_flash_read8(chip_id, offset);
575 }
576
577 /* Check the final status */
578 if (status & 0x7f)
579 {
580 cvmx_dprintf("cvmx-flash: Hardware failure erasing block\n");
581 cvmx_spinlock_unlock(&flash_lock);
582 return -1;
583 }
584
585 /* Increment to the next byte */
586 ptr++;
587 offset++;
588 }
589 cvmx_dprintf("%s:%d\n", __FUNCTION__, __LINE__);
590
591 __cvmx_flash_write_cmd(chip_id, 0x00, 0xff); /* Reset the flash chip */
592 cvmx_spinlock_unlock(&flash_lock);
593 return 0;
594 }
595 }
596
597 cvmx_dprintf("cvmx-flash: Unsupported flash vendor\n");
598 cvmx_spinlock_unlock(&flash_lock);
599 return -1;
600 }
601
602
603 /**
604 * Erase and write data to a flash
605 *
606 * @param address Memory address to write to
607 * @param data Data to write
608 * @param len Length of the data
609 * @return Zero on success. Negative on failure
610 */
cvmx_flash_write(void * address,const void * data,int len)611 int cvmx_flash_write(void *address, const void *data, int len)
612 {
613 int chip_id;
614
615 /* Find which chip controls this address. Don't allow the write to span
616 multiple chips */
617 for (chip_id=0; chip_id<MAX_NUM_FLASH_CHIPS; chip_id++)
618 {
619 if ((flash_info[chip_id].base_ptr <= address) &&
620 (flash_info[chip_id].base_ptr + flash_info[chip_id].size >= address + len))
621 break;
622 }
623
624 if (chip_id == MAX_NUM_FLASH_CHIPS)
625 {
626 cvmx_dprintf("cvmx-flash: Unable to find chip that contains address %p\n", address);
627 return -1;
628 }
629
630 cvmx_flash_t *flash = flash_info + chip_id;
631
632 /* Determine which block region we need to start writing to */
633 void *region_base = flash->base_ptr;
634 int region = 0;
635 while (region_base + flash->region[region].num_blocks * flash->region[region].block_size <= address)
636 {
637 region++;
638 region_base = flash->base_ptr + flash->region[region].start_offset;
639 }
640
641 /* Determine which block in the region to start at */
642 int block = (address - region_base) / flash->region[region].block_size;
643
644 /* Require all writes to start on block boundries */
645 if (address != region_base + block*flash->region[region].block_size)
646 {
647 cvmx_dprintf("cvmx-flash: Write address not aligned on a block boundry\n");
648 return -1;
649 }
650
651 /* Loop until we're out of data */
652 while (len > 0)
653 {
654 /* Erase the current block */
655 if (cvmx_flash_erase_block(chip_id, region, block))
656 return -1;
657 /* Write the new data */
658 if (cvmx_flash_write_block(chip_id, region, block, data))
659 return -1;
660
661 /* Increment to the next block */
662 data += flash->region[region].block_size;
663 len -= flash->region[region].block_size;
664 block++;
665 if (block >= flash->region[region].num_blocks)
666 {
667 block = 0;
668 region++;
669 }
670 }
671
672 return 0;
673 }
674
675