1/***********************license start*************** 2 * Copyright (c) 2003-2010 Cavium Inc. (support@cavium.com). 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 75typedef 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 87static CVMX_SHARED cvmx_flash_t flash_info[MAX_NUM_FLASH_CHIPS]; 88static 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 */ 99static 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 */ 113static 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 */ 129static 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 */ 145static 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 */ 160static 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 */ 175static 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 */ 287void 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 */ 325void *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 */ 337int 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 */ 350const 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 */ 364int 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 */ 487int 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 { 554cvmx_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 } 589cvmx_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 */ 611int 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