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