1260684Skaiw/***********************license start***************
2260684Skaiw * Copyright (c) 2003-2010  Cavium Networks (support@cavium.com). All rights
3260684Skaiw * reserved.
4260684Skaiw *
5260684Skaiw *
6260684Skaiw * Redistribution and use in source and binary forms, with or without
7260684Skaiw * modification, are permitted provided that the following conditions are
8260684Skaiw * met:
9260684Skaiw *
10260684Skaiw *   * Redistributions of source code must retain the above copyright
11260684Skaiw *     notice, this list of conditions and the following disclaimer.
12260684Skaiw *
13260684Skaiw *   * Redistributions in binary form must reproduce the above
14260684Skaiw *     copyright notice, this list of conditions and the following
15260684Skaiw *     disclaimer in the documentation and/or other materials provided
16260684Skaiw *     with the distribution.
17260684Skaiw
18260684Skaiw *   * Neither the name of Cavium Networks nor the names of
19260684Skaiw *     its contributors may be used to endorse or promote products
20260684Skaiw *     derived from this software without specific prior written
21260684Skaiw *     permission.
22260684Skaiw
23260684Skaiw * This Software, including technical data, may be subject to U.S. export  control
24367466Sdim * laws, including the U.S. Export Administration Act and its  associated
25260684Skaiw * regulations, and may be subject to export or import  regulations in other
26367466Sdim * countries.
27367466Sdim
28260684Skaiw * TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED "AS IS"
29260684Skaiw * AND WITH ALL FAULTS AND CAVIUM  NETWORKS MAKES NO PROMISES, REPRESENTATIONS OR
30260684Skaiw * WARRANTIES, EITHER EXPRESS, IMPLIED, STATUTORY, OR OTHERWISE, WITH RESPECT TO
31260684Skaiw * THE SOFTWARE, INCLUDING ITS CONDITION, ITS CONFORMITY TO ANY REPRESENTATION OR
32260684Skaiw * DESCRIPTION, OR THE EXISTENCE OF ANY LATENT OR PATENT DEFECTS, AND CAVIUM
33260684Skaiw * SPECIFICALLY DISCLAIMS ALL IMPLIED (IF ANY) WARRANTIES OF TITLE,
34260684Skaiw * MERCHANTABILITY, NONINFRINGEMENT, FITNESS FOR A PARTICULAR PURPOSE, LACK OF
35260684Skaiw * VIRUSES, ACCURACY OR COMPLETENESS, QUIET ENJOYMENT, QUIET POSSESSION OR
36260684Skaiw * CORRESPONDENCE TO DESCRIPTION. THE ENTIRE  RISK ARISING OUT OF USE OR
37260684Skaiw * PERFORMANCE OF THE SOFTWARE LIES WITH YOU.
38260684Skaiw ***********************license end**************************************/
39260684Skaiw
40260684Skaiw
41260684Skaiw
42260684Skaiw
43260684Skaiw
44260684Skaiw
45260684Skaiw
46260684Skaiw/**
47260684Skaiw * @file
48260684Skaiw *
49260684Skaiw * This file provides bootbus flash operations
50260684Skaiw *
51260684Skaiw * <hr>$Revision: 49448 $<hr>
52260684Skaiw *
53260684Skaiw *
54367466Sdim */
55260684Skaiw
56260684Skaiw#include "cvmx-config.h"
57260684Skaiw#include "cvmx.h"
58260684Skaiw#include "cvmx-sysinfo.h"
59260684Skaiw#include "cvmx-spinlock.h"
60260684Skaiw#include "cvmx-flash.h"
61260684Skaiw
62260684Skaiw#define MAX_NUM_FLASH_CHIPS 8   /* Maximum number of flash chips */
63260684Skaiw#define MAX_NUM_REGIONS     8   /* Maximum number of block regions per chip */
64260684Skaiw#define DEBUG 1
65260684Skaiw
66260684Skaiw#define CFI_CMDSET_NONE             0
67260684Skaiw#define CFI_CMDSET_INTEL_EXTENDED   1
68260684Skaiw#define CFI_CMDSET_AMD_STANDARD     2
69260684Skaiw#define CFI_CMDSET_INTEL_STANDARD   3
70260684Skaiw#define CFI_CMDSET_AMD_EXTENDED     4
71260684Skaiw#define CFI_CMDSET_MITSU_STANDARD   256
72260684Skaiw#define CFI_CMDSET_MITSU_EXTENDED   257
73260684Skaiw#define CFI_CMDSET_SST              258
74260684Skaiw
75260684Skaiwtypedef struct
76260684Skaiw{
77260684Skaiw    void *              base_ptr;       /**< Memory pointer to start of flash */
78260684Skaiw    int                 is_16bit;       /**< Chip is 16bits wide in 8bit mode */
79260684Skaiw    uint16_t            vendor;         /**< Vendor ID of Chip */
80260684Skaiw    int                 size;           /**< Size of the chip in bytes */
81260684Skaiw    uint64_t            erase_timeout;  /**< Erase timeout in cycles */
82260684Skaiw    uint64_t            write_timeout;  /**< Write timeout in cycles */
83260684Skaiw    int                 num_regions;    /**< Number of block regions */
84260684Skaiw    cvmx_flash_region_t region[MAX_NUM_REGIONS];
85260684Skaiw} 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