]> CyberLeo.Net >> Repos - FreeBSD/releng/9.2.git/blob - sys/contrib/octeon-sdk/cvmx-flash.c
- Copy stable/9 to releng/9.2 as part of the 9.2-RELEASE cycle.
[FreeBSD/releng/9.2.git] / sys / contrib / octeon-sdk / cvmx-flash.c
1 /***********************license start***************
2  * Copyright (c) 2003-2010  Cavium Networks (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 Networks 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  NETWORKS 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: 49448 $<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  */
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  */
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  */
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  */
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  */
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  */
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  */
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  */
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  */
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  */
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  */
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  */
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  */
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