1210284Sjmallett/***********************license start***************
2232812Sjmallett * Copyright (c) 2003-2010  Cavium Inc. (support@cavium.com). All rights
3215990Sjmallett * reserved.
4210284Sjmallett *
5210284Sjmallett *
6215990Sjmallett * Redistribution and use in source and binary forms, with or without
7215990Sjmallett * modification, are permitted provided that the following conditions are
8215990Sjmallett * met:
9210284Sjmallett *
10215990Sjmallett *   * Redistributions of source code must retain the above copyright
11215990Sjmallett *     notice, this list of conditions and the following disclaimer.
12210284Sjmallett *
13215990Sjmallett *   * Redistributions in binary form must reproduce the above
14215990Sjmallett *     copyright notice, this list of conditions and the following
15215990Sjmallett *     disclaimer in the documentation and/or other materials provided
16215990Sjmallett *     with the distribution.
17215990Sjmallett
18232812Sjmallett *   * Neither the name of Cavium Inc. nor the names of
19215990Sjmallett *     its contributors may be used to endorse or promote products
20215990Sjmallett *     derived from this software without specific prior written
21215990Sjmallett *     permission.
22215990Sjmallett
23215990Sjmallett * This Software, including technical data, may be subject to U.S. export  control
24215990Sjmallett * laws, including the U.S. Export Administration Act and its  associated
25215990Sjmallett * regulations, and may be subject to export or import  regulations in other
26215990Sjmallett * countries.
27215990Sjmallett
28215990Sjmallett * TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED "AS IS"
29232812Sjmallett * AND WITH ALL FAULTS AND CAVIUM INC. MAKES NO PROMISES, REPRESENTATIONS OR
30215990Sjmallett * WARRANTIES, EITHER EXPRESS, IMPLIED, STATUTORY, OR OTHERWISE, WITH RESPECT TO
31215990Sjmallett * THE SOFTWARE, INCLUDING ITS CONDITION, ITS CONFORMITY TO ANY REPRESENTATION OR
32215990Sjmallett * DESCRIPTION, OR THE EXISTENCE OF ANY LATENT OR PATENT DEFECTS, AND CAVIUM
33215990Sjmallett * SPECIFICALLY DISCLAIMS ALL IMPLIED (IF ANY) WARRANTIES OF TITLE,
34215990Sjmallett * MERCHANTABILITY, NONINFRINGEMENT, FITNESS FOR A PARTICULAR PURPOSE, LACK OF
35215990Sjmallett * VIRUSES, ACCURACY OR COMPLETENESS, QUIET ENJOYMENT, QUIET POSSESSION OR
36215990Sjmallett * CORRESPONDENCE TO DESCRIPTION. THE ENTIRE  RISK ARISING OUT OF USE OR
37215990Sjmallett * PERFORMANCE OF THE SOFTWARE LIES WITH YOU.
38210284Sjmallett ***********************license end**************************************/
39210284Sjmallett
40210284Sjmallett
41210284Sjmallett
42210284Sjmallett
43210284Sjmallett
44210284Sjmallett
45215990Sjmallett
46210284Sjmallett/**
47210284Sjmallett * @file
48210284Sjmallett *
49210284Sjmallett * This file provides bootbus flash operations
50210284Sjmallett *
51232812Sjmallett * <hr>$Revision: 70030 $<hr>
52210284Sjmallett *
53210284Sjmallett *
54210284Sjmallett */
55210284Sjmallett
56210284Sjmallett#include "cvmx-config.h"
57210284Sjmallett#include "cvmx.h"
58210284Sjmallett#include "cvmx-sysinfo.h"
59210284Sjmallett#include "cvmx-spinlock.h"
60210284Sjmallett#include "cvmx-flash.h"
61210284Sjmallett
62210284Sjmallett#define MAX_NUM_FLASH_CHIPS 8   /* Maximum number of flash chips */
63210284Sjmallett#define MAX_NUM_REGIONS     8   /* Maximum number of block regions per chip */
64210284Sjmallett#define DEBUG 1
65210284Sjmallett
66210284Sjmallett#define CFI_CMDSET_NONE             0
67210284Sjmallett#define CFI_CMDSET_INTEL_EXTENDED   1
68210284Sjmallett#define CFI_CMDSET_AMD_STANDARD     2
69210284Sjmallett#define CFI_CMDSET_INTEL_STANDARD   3
70210284Sjmallett#define CFI_CMDSET_AMD_EXTENDED     4
71210284Sjmallett#define CFI_CMDSET_MITSU_STANDARD   256
72210284Sjmallett#define CFI_CMDSET_MITSU_EXTENDED   257
73210284Sjmallett#define CFI_CMDSET_SST              258
74210284Sjmallett
75210284Sjmalletttypedef struct
76210284Sjmallett{
77210284Sjmallett    void *              base_ptr;       /**< Memory pointer to start of flash */
78210284Sjmallett    int                 is_16bit;       /**< Chip is 16bits wide in 8bit mode */
79210284Sjmallett    uint16_t            vendor;         /**< Vendor ID of Chip */
80210284Sjmallett    int                 size;           /**< Size of the chip in bytes */
81210284Sjmallett    uint64_t            erase_timeout;  /**< Erase timeout in cycles */
82210284Sjmallett    uint64_t            write_timeout;  /**< Write timeout in cycles */
83210284Sjmallett    int                 num_regions;    /**< Number of block regions */
84210284Sjmallett    cvmx_flash_region_t region[MAX_NUM_REGIONS];
85210284Sjmallett} cvmx_flash_t;
86210284Sjmallett
87210284Sjmallettstatic CVMX_SHARED cvmx_flash_t flash_info[MAX_NUM_FLASH_CHIPS];
88210284Sjmallettstatic CVMX_SHARED cvmx_spinlock_t flash_lock = CVMX_SPINLOCK_UNLOCKED_INITIALIZER;
89210284Sjmallett
90210284Sjmallett
91210284Sjmallett/**
92210284Sjmallett * @INTERNAL
93210284Sjmallett * Read a byte from flash
94210284Sjmallett *
95210284Sjmallett * @param chip_id Chip to read from
96210284Sjmallett * @param offset  Offset into the chip
97210284Sjmallett * @return Value read
98210284Sjmallett */
99210284Sjmallettstatic uint8_t __cvmx_flash_read8(int chip_id, int offset)
100210284Sjmallett{
101210284Sjmallett    return *(volatile uint8_t *)(flash_info[chip_id].base_ptr + offset);
102210284Sjmallett}
103210284Sjmallett
104210284Sjmallett
105210284Sjmallett/**
106210284Sjmallett * @INTERNAL
107210284Sjmallett * Read a byte from flash (for commands)
108210284Sjmallett *
109210284Sjmallett * @param chip_id Chip to read from
110210284Sjmallett * @param offset  Offset into the chip
111210284Sjmallett * @return Value read
112210284Sjmallett */
113210284Sjmallettstatic uint8_t __cvmx_flash_read_cmd(int chip_id, int offset)
114210284Sjmallett{
115210284Sjmallett    if (flash_info[chip_id].is_16bit)
116210284Sjmallett        offset<<=1;
117210284Sjmallett    return __cvmx_flash_read8(chip_id, offset);
118210284Sjmallett}
119210284Sjmallett
120210284Sjmallett
121210284Sjmallett/**
122210284Sjmallett * @INTERNAL
123210284Sjmallett * Read 16bits from flash (for commands)
124210284Sjmallett *
125210284Sjmallett * @param chip_id Chip to read from
126210284Sjmallett * @param offset  Offset into the chip
127210284Sjmallett * @return Value read
128210284Sjmallett */
129210284Sjmallettstatic uint16_t __cvmx_flash_read_cmd16(int chip_id, int offset)
130210284Sjmallett{
131210284Sjmallett    uint16_t v = __cvmx_flash_read_cmd(chip_id, offset);
132210284Sjmallett    v |= __cvmx_flash_read_cmd(chip_id, offset + 1)<<8;
133210284Sjmallett    return v;
134210284Sjmallett}
135210284Sjmallett
136210284Sjmallett
137210284Sjmallett/**
138210284Sjmallett * @INTERNAL
139210284Sjmallett * Write a byte to flash
140210284Sjmallett *
141210284Sjmallett * @param chip_id Chip to write to
142210284Sjmallett * @param offset  Offset into the chip
143210284Sjmallett * @param data    Value to write
144210284Sjmallett */
145210284Sjmallettstatic void __cvmx_flash_write8(int chip_id, int offset, uint8_t data)
146210284Sjmallett{
147210284Sjmallett    volatile uint8_t *flash_ptr = (volatile uint8_t *)flash_info[chip_id].base_ptr;
148210284Sjmallett    flash_ptr[offset] = data;
149210284Sjmallett}
150210284Sjmallett
151210284Sjmallett
152210284Sjmallett/**
153210284Sjmallett * @INTERNAL
154210284Sjmallett * Write a byte to flash (for commands)
155210284Sjmallett *
156210284Sjmallett * @param chip_id Chip to write to
157210284Sjmallett * @param offset  Offset into the chip
158210284Sjmallett * @param data    Value to write
159210284Sjmallett */
160210284Sjmallettstatic void __cvmx_flash_write_cmd(int chip_id, int offset, uint8_t data)
161210284Sjmallett{
162210284Sjmallett    volatile uint8_t *flash_ptr = (volatile uint8_t *)flash_info[chip_id].base_ptr;
163210284Sjmallett    flash_ptr[offset<<flash_info[chip_id].is_16bit] = data;
164210284Sjmallett}
165210284Sjmallett
166210284Sjmallett
167210284Sjmallett/**
168210284Sjmallett * @INTERNAL
169210284Sjmallett * Query a address and see if a CFI flash chip is there.
170210284Sjmallett *
171210284Sjmallett * @param chip_id  Chip ID data to fill in if the chip is there
172210284Sjmallett * @param base_ptr Memory pointer to the start address to query
173210284Sjmallett * @return Zero on success, Negative on failure
174210284Sjmallett */
175210284Sjmallettstatic int __cvmx_flash_queury_cfi(int chip_id, void *base_ptr)
176210284Sjmallett{
177210284Sjmallett    int region;
178210284Sjmallett    cvmx_flash_t *flash = flash_info + chip_id;
179210284Sjmallett
180210284Sjmallett    /* Set the minimum needed for the read and write primitives to work */
181210284Sjmallett    flash->base_ptr = base_ptr;
182210284Sjmallett    flash->is_16bit = 1;   /* FIXME: Currently assumes the chip is 16bits */
183210284Sjmallett
184210284Sjmallett    /* Put flash in CFI query mode */
185210284Sjmallett    __cvmx_flash_write_cmd(chip_id, 0x00, 0xf0); /* Reset the flash chip */
186210284Sjmallett    __cvmx_flash_write_cmd(chip_id, 0x55, 0x98);
187210284Sjmallett
188210284Sjmallett    /* Make sure we get the QRY response we should */
189210284Sjmallett    if ((__cvmx_flash_read_cmd(chip_id, 0x10) != 'Q') ||
190210284Sjmallett        (__cvmx_flash_read_cmd(chip_id, 0x11) != 'R') ||
191210284Sjmallett        (__cvmx_flash_read_cmd(chip_id, 0x12) != 'Y'))
192210284Sjmallett    {
193210284Sjmallett        flash->base_ptr = NULL;
194210284Sjmallett        return -1;
195210284Sjmallett    }
196210284Sjmallett
197210284Sjmallett    /* Read the 16bit vendor ID */
198210284Sjmallett    flash->vendor = __cvmx_flash_read_cmd16(chip_id, 0x13);
199210284Sjmallett
200210284Sjmallett    /* Read the write timeout. The timeout is microseconds(us) is 2^0x1f
201210284Sjmallett        typically. The worst case is this value time 2^0x23 */
202210284Sjmallett    flash->write_timeout = 1ull << (__cvmx_flash_read_cmd(chip_id, 0x1f) +
203210284Sjmallett                                    __cvmx_flash_read_cmd(chip_id, 0x23));
204210284Sjmallett
205210284Sjmallett    /* Read the erase timeout. The timeout is milliseconds(ms) is 2^0x21
206210284Sjmallett        typically. The worst case is this value time 2^0x25 */
207210284Sjmallett    flash->erase_timeout = 1ull << (__cvmx_flash_read_cmd(chip_id, 0x21) +
208210284Sjmallett                                    __cvmx_flash_read_cmd(chip_id, 0x25));
209210284Sjmallett
210210284Sjmallett    /* Get the flash size. This is 2^0x27 */
211210284Sjmallett    flash->size = 1<<__cvmx_flash_read_cmd(chip_id, 0x27);
212210284Sjmallett
213210284Sjmallett    /* Get the number of different sized block regions from 0x2c */
214210284Sjmallett    flash->num_regions = __cvmx_flash_read_cmd(chip_id, 0x2c);
215210284Sjmallett
216210284Sjmallett    int start_offset = 0;
217210284Sjmallett    /* Loop through all regions get information about each */
218210284Sjmallett    for (region=0; region<flash->num_regions; region++)
219210284Sjmallett    {
220210284Sjmallett        cvmx_flash_region_t *rgn_ptr = flash->region + region;
221210284Sjmallett        rgn_ptr->start_offset = start_offset;
222210284Sjmallett
223210284Sjmallett        /* The number of blocks in each region is a 16 bit little endian
224210284Sjmallett            endian field. It is encoded at 0x2d + region*4 as (blocks-1) */
225210284Sjmallett        uint16_t blocks = __cvmx_flash_read_cmd16(chip_id, 0x2d + region*4);
226210284Sjmallett        rgn_ptr->num_blocks =  1u + blocks;
227210284Sjmallett
228210284Sjmallett        /* The size of each block is a 16 bit little endian endian field. It
229210284Sjmallett            is encoded at 0x2d + region*4 + 2 as (size/256). Zero is a special
230210284Sjmallett            case representing 128 */
231210284Sjmallett        uint16_t size = __cvmx_flash_read_cmd16(chip_id, 0x2d + region*4 + 2);
232210284Sjmallett        if (size == 0)
233210284Sjmallett            rgn_ptr->block_size = 128;
234210284Sjmallett        else
235210284Sjmallett            rgn_ptr->block_size = 256u * size;
236210284Sjmallett
237210284Sjmallett        start_offset += rgn_ptr->block_size * rgn_ptr->num_blocks;
238210284Sjmallett    }
239210284Sjmallett
240210284Sjmallett    /* Take the chip out of CFI query mode */
241210284Sjmallett    switch (flash_info[chip_id].vendor)
242210284Sjmallett    {
243210284Sjmallett        case CFI_CMDSET_AMD_STANDARD:
244210284Sjmallett            __cvmx_flash_write_cmd(chip_id, 0x00, 0xf0);
245210284Sjmallett        case CFI_CMDSET_INTEL_STANDARD:
246210284Sjmallett        case CFI_CMDSET_INTEL_EXTENDED:
247210284Sjmallett            __cvmx_flash_write_cmd(chip_id, 0x00, 0xff);
248210284Sjmallett            break;
249210284Sjmallett    }
250210284Sjmallett
251210284Sjmallett    /* Convert the timeouts to cycles */
252215990Sjmallett    flash->write_timeout *= cvmx_clock_get_rate(CVMX_CLOCK_CORE) / 1000000;
253215990Sjmallett    flash->erase_timeout *= cvmx_clock_get_rate(CVMX_CLOCK_CORE) / 1000;
254210284Sjmallett
255210284Sjmallett#if DEBUG
256210284Sjmallett    /* Print the information about the chip */
257210284Sjmallett    cvmx_dprintf("cvmx-flash: Base pointer:  %p\n"
258210284Sjmallett           "            Vendor:        0x%04x\n"
259210284Sjmallett           "            Size:          %d bytes\n"
260210284Sjmallett           "            Num regions:   %d\n"
261210284Sjmallett           "            Erase timeout: %llu cycles\n"
262210284Sjmallett           "            Write timeout: %llu cycles\n",
263210284Sjmallett           flash->base_ptr,
264210284Sjmallett           (unsigned int)flash->vendor,
265210284Sjmallett           flash->size,
266210284Sjmallett           flash->num_regions,
267210284Sjmallett           (unsigned long long)flash->erase_timeout,
268210284Sjmallett           (unsigned long long)flash->write_timeout);
269210284Sjmallett
270210284Sjmallett    for (region=0; region<flash->num_regions; region++)
271210284Sjmallett    {
272210284Sjmallett        cvmx_dprintf("            Region %d: offset 0x%x, %d blocks, %d bytes/block\n",
273210284Sjmallett               region,
274210284Sjmallett               flash->region[region].start_offset,
275210284Sjmallett               flash->region[region].num_blocks,
276210284Sjmallett               flash->region[region].block_size);
277210284Sjmallett    }
278210284Sjmallett#endif
279210284Sjmallett
280210284Sjmallett    return 0;
281210284Sjmallett}
282210284Sjmallett
283210284Sjmallett
284210284Sjmallett/**
285210284Sjmallett * Initialize the flash access library
286210284Sjmallett */
287210284Sjmallettvoid cvmx_flash_initialize(void)
288210284Sjmallett{
289210284Sjmallett    int boot_region;
290210284Sjmallett    int chip_id = 0;
291210284Sjmallett
292210284Sjmallett    memset(flash_info, 0, sizeof(flash_info));
293210284Sjmallett
294210284Sjmallett    /* Loop through each boot bus chip select region */
295210284Sjmallett    for (boot_region=0; boot_region<MAX_NUM_FLASH_CHIPS; boot_region++)
296210284Sjmallett    {
297210284Sjmallett        cvmx_mio_boot_reg_cfgx_t region_cfg;
298210284Sjmallett        region_cfg.u64 = cvmx_read_csr(CVMX_MIO_BOOT_REG_CFG0 + boot_region*8);
299210284Sjmallett        /* Only try chip select regions that are enabled. This assumes the
300210284Sjmallett            bootloader already setup the flash */
301210284Sjmallett        if (region_cfg.s.en)
302210284Sjmallett        {
303210284Sjmallett            /* Convert the hardware address to a pointer. Note that the bootbus,
304210284Sjmallett                unlike memory, isn't 1:1 mapped in the simple exec */
305210284Sjmallett            void *base_ptr = cvmx_phys_to_ptr((region_cfg.s.base<<16) | 0xffffffff80000000ull);
306210284Sjmallett            if (__cvmx_flash_queury_cfi(chip_id, base_ptr) == 0)
307210284Sjmallett            {
308210284Sjmallett                /* Valid CFI flash chip found */
309210284Sjmallett                chip_id++;
310210284Sjmallett            }
311210284Sjmallett        }
312210284Sjmallett    }
313210284Sjmallett
314210284Sjmallett    if (chip_id == 0)
315210284Sjmallett        cvmx_dprintf("cvmx-flash: No CFI chips found\n");
316210284Sjmallett}
317210284Sjmallett
318210284Sjmallett
319210284Sjmallett/**
320210284Sjmallett * Return a pointer to the flash chip
321210284Sjmallett *
322210284Sjmallett * @param chip_id Chip ID to return
323210284Sjmallett * @return NULL if the chip doesn't exist
324210284Sjmallett */
325210284Sjmallettvoid *cvmx_flash_get_base(int chip_id)
326210284Sjmallett{
327210284Sjmallett    return flash_info[chip_id].base_ptr;
328210284Sjmallett}
329210284Sjmallett
330210284Sjmallett
331210284Sjmallett/**
332210284Sjmallett * Return the number of erasable regions on the chip
333210284Sjmallett *
334210284Sjmallett * @param chip_id Chip to return info for
335210284Sjmallett * @return Number of regions
336210284Sjmallett */
337210284Sjmallettint cvmx_flash_get_num_regions(int chip_id)
338210284Sjmallett{
339210284Sjmallett    return flash_info[chip_id].num_regions;
340210284Sjmallett}
341210284Sjmallett
342210284Sjmallett
343210284Sjmallett/**
344210284Sjmallett * Return information about a flash chips region
345210284Sjmallett *
346210284Sjmallett * @param chip_id Chip to get info for
347210284Sjmallett * @param region  Region to get info for
348210284Sjmallett * @return Region information
349210284Sjmallett */
350210284Sjmallettconst cvmx_flash_region_t *cvmx_flash_get_region_info(int chip_id, int region)
351210284Sjmallett{
352210284Sjmallett    return flash_info[chip_id].region + region;
353210284Sjmallett}
354210284Sjmallett
355210284Sjmallett
356210284Sjmallett/**
357210284Sjmallett * Erase a block on the flash chip
358210284Sjmallett *
359210284Sjmallett * @param chip_id Chip to erase a block on
360210284Sjmallett * @param region  Region to erase a block in
361210284Sjmallett * @param block   Block number to erase
362210284Sjmallett * @return Zero on success. Negative on failure
363210284Sjmallett */
364210284Sjmallettint cvmx_flash_erase_block(int chip_id, int region, int block)
365210284Sjmallett{
366210284Sjmallett    cvmx_spinlock_lock(&flash_lock);
367210284Sjmallett#if DEBUG
368210284Sjmallett    cvmx_dprintf("cvmx-flash: Erasing chip %d, region %d, block %d\n",
369210284Sjmallett           chip_id, region, block);
370210284Sjmallett#endif
371210284Sjmallett
372210284Sjmallett    int offset = flash_info[chip_id].region[region].start_offset +
373210284Sjmallett                block * flash_info[chip_id].region[region].block_size;
374210284Sjmallett
375210284Sjmallett    switch (flash_info[chip_id].vendor)
376210284Sjmallett    {
377210284Sjmallett        case CFI_CMDSET_AMD_STANDARD:
378210284Sjmallett        {
379210284Sjmallett            /* Send the erase sector command sequence */
380210284Sjmallett            __cvmx_flash_write_cmd(chip_id, 0x00, 0xf0); /* Reset the flash chip */
381210284Sjmallett            __cvmx_flash_write_cmd(chip_id, 0x555, 0xaa);
382210284Sjmallett            __cvmx_flash_write_cmd(chip_id, 0x2aa, 0x55);
383210284Sjmallett            __cvmx_flash_write_cmd(chip_id, 0x555, 0x80);
384210284Sjmallett            __cvmx_flash_write_cmd(chip_id, 0x555, 0xaa);
385210284Sjmallett            __cvmx_flash_write_cmd(chip_id, 0x2aa, 0x55);
386210284Sjmallett            __cvmx_flash_write8(chip_id, offset, 0x30);
387210284Sjmallett
388210284Sjmallett            /* Loop checking status */
389210284Sjmallett            uint8_t status = __cvmx_flash_read8(chip_id, offset);
390210284Sjmallett            uint64_t start_cycle = cvmx_get_cycle();
391210284Sjmallett            while (1)
392210284Sjmallett            {
393210284Sjmallett                /* Read the status and xor it with the old status so we can
394210284Sjmallett                    find toggling bits */
395210284Sjmallett                uint8_t old_status = status;
396210284Sjmallett                status = __cvmx_flash_read8(chip_id, offset);
397210284Sjmallett                uint8_t toggle = status ^ old_status;
398210284Sjmallett
399210284Sjmallett                /* Check if the erase in progress bit is toggling */
400210284Sjmallett                if (toggle & (1<<6))
401210284Sjmallett                {
402210284Sjmallett                    /* Check hardware timeout */
403210284Sjmallett                    if (status & (1<<5))
404210284Sjmallett                    {
405210284Sjmallett                        /* Chip has signalled a timeout. Reread the status */
406210284Sjmallett                        old_status = __cvmx_flash_read8(chip_id, offset);
407210284Sjmallett                        status = __cvmx_flash_read8(chip_id, offset);
408210284Sjmallett                        toggle = status ^ old_status;
409210284Sjmallett
410210284Sjmallett                        /* Check if the erase in progress bit is toggling */
411210284Sjmallett                        if (toggle & (1<<6))
412210284Sjmallett                        {
413210284Sjmallett                            cvmx_dprintf("cvmx-flash: Hardware timeout erasing block\n");
414210284Sjmallett                            cvmx_spinlock_unlock(&flash_lock);
415210284Sjmallett                            return -1;
416210284Sjmallett                        }
417210284Sjmallett                        else
418210284Sjmallett                            break;  /* Not toggling, erase complete */
419210284Sjmallett                    }
420210284Sjmallett                }
421210284Sjmallett                else
422210284Sjmallett                    break;  /* Not toggling, erase complete */
423210284Sjmallett
424210284Sjmallett                if (cvmx_get_cycle() > start_cycle + flash_info[chip_id].erase_timeout)
425210284Sjmallett                {
426210284Sjmallett                    cvmx_dprintf("cvmx-flash: Timeout erasing block\n");
427210284Sjmallett                    cvmx_spinlock_unlock(&flash_lock);
428210284Sjmallett                    return -1;
429210284Sjmallett                }
430210284Sjmallett            }
431210284Sjmallett
432210284Sjmallett            __cvmx_flash_write_cmd(chip_id, 0x00, 0xf0); /* Reset the flash chip */
433210284Sjmallett            cvmx_spinlock_unlock(&flash_lock);
434210284Sjmallett            return 0;
435210284Sjmallett        }
436210284Sjmallett        case CFI_CMDSET_INTEL_STANDARD:
437210284Sjmallett        case CFI_CMDSET_INTEL_EXTENDED:
438210284Sjmallett        {
439210284Sjmallett            /* Send the erase sector command sequence */
440210284Sjmallett            __cvmx_flash_write_cmd(chip_id, 0x00, 0xff); /* Reset the flash chip */
441210284Sjmallett            __cvmx_flash_write8(chip_id, offset, 0x20);
442210284Sjmallett            __cvmx_flash_write8(chip_id, offset, 0xd0);
443210284Sjmallett
444210284Sjmallett            /* Loop checking status */
445210284Sjmallett            uint8_t status = __cvmx_flash_read8(chip_id, offset);
446210284Sjmallett            uint64_t start_cycle = cvmx_get_cycle();
447210284Sjmallett            while ((status & 0x80) == 0)
448210284Sjmallett            {
449210284Sjmallett                if (cvmx_get_cycle() > start_cycle + flash_info[chip_id].erase_timeout)
450210284Sjmallett                {
451210284Sjmallett                    cvmx_dprintf("cvmx-flash: Timeout erasing block\n");
452210284Sjmallett                    cvmx_spinlock_unlock(&flash_lock);
453210284Sjmallett                    return -1;
454210284Sjmallett                }
455210284Sjmallett                status = __cvmx_flash_read8(chip_id, offset);
456210284Sjmallett            }
457210284Sjmallett
458210284Sjmallett            /* Check the final status */
459210284Sjmallett            if (status & 0x7f)
460210284Sjmallett            {
461210284Sjmallett                cvmx_dprintf("cvmx-flash: Hardware failure erasing block\n");
462210284Sjmallett                cvmx_spinlock_unlock(&flash_lock);
463210284Sjmallett                return -1;
464210284Sjmallett            }
465210284Sjmallett
466210284Sjmallett            __cvmx_flash_write_cmd(chip_id, 0x00, 0xff); /* Reset the flash chip */
467210284Sjmallett            cvmx_spinlock_unlock(&flash_lock);
468210284Sjmallett            return 0;
469210284Sjmallett        }
470210284Sjmallett    }
471210284Sjmallett
472210284Sjmallett    cvmx_dprintf("cvmx-flash: Unsupported flash vendor\n");
473210284Sjmallett    cvmx_spinlock_unlock(&flash_lock);
474210284Sjmallett    return -1;
475210284Sjmallett}
476210284Sjmallett
477210284Sjmallett
478210284Sjmallett/**
479210284Sjmallett * Write a block on the flash chip
480210284Sjmallett *
481210284Sjmallett * @param chip_id Chip to write a block on
482210284Sjmallett * @param region  Region to write a block in
483210284Sjmallett * @param block   Block number to write
484210284Sjmallett * @param data    Data to write
485210284Sjmallett * @return Zero on success. Negative on failure
486210284Sjmallett */
487210284Sjmallettint cvmx_flash_write_block(int chip_id, int region, int block, const void *data)
488210284Sjmallett{
489210284Sjmallett    cvmx_spinlock_lock(&flash_lock);
490210284Sjmallett#if DEBUG
491210284Sjmallett    cvmx_dprintf("cvmx-flash: Writing chip %d, region %d, block %d\n",
492210284Sjmallett           chip_id, region, block);
493210284Sjmallett#endif
494210284Sjmallett    int offset = flash_info[chip_id].region[region].start_offset +
495210284Sjmallett                block * flash_info[chip_id].region[region].block_size;
496210284Sjmallett    int len = flash_info[chip_id].region[region].block_size;
497210284Sjmallett    const uint8_t *ptr = (const uint8_t *)data;
498210284Sjmallett
499210284Sjmallett    switch (flash_info[chip_id].vendor)
500210284Sjmallett    {
501210284Sjmallett        case CFI_CMDSET_AMD_STANDARD:
502210284Sjmallett        {
503210284Sjmallett            /* Loop through one byte at a time */
504210284Sjmallett            while (len--)
505210284Sjmallett            {
506210284Sjmallett                /* Send the program sequence */
507210284Sjmallett                __cvmx_flash_write_cmd(chip_id, 0x00, 0xf0); /* Reset the flash chip */
508210284Sjmallett                __cvmx_flash_write_cmd(chip_id, 0x555, 0xaa);
509210284Sjmallett                __cvmx_flash_write_cmd(chip_id, 0x2aa, 0x55);
510210284Sjmallett                __cvmx_flash_write_cmd(chip_id, 0x555, 0xa0);
511210284Sjmallett                __cvmx_flash_write8(chip_id, offset, *ptr);
512210284Sjmallett
513210284Sjmallett                /* Loop polling for status */
514210284Sjmallett                uint64_t start_cycle = cvmx_get_cycle();
515210284Sjmallett                while (1)
516210284Sjmallett                {
517210284Sjmallett                    uint8_t status = __cvmx_flash_read8(chip_id, offset);
518210284Sjmallett                    if (((status ^ *ptr) & (1<<7)) == 0)
519210284Sjmallett                        break;  /* Data matches, this byte is done */
520210284Sjmallett                    else if (status & (1<<5))
521210284Sjmallett                    {
522210284Sjmallett                        /* Hardware timeout, recheck status */
523210284Sjmallett                        status = __cvmx_flash_read8(chip_id, offset);
524210284Sjmallett                        if (((status ^ *ptr) & (1<<7)) == 0)
525210284Sjmallett                            break;  /* Data matches, this byte is done */
526210284Sjmallett                        else
527210284Sjmallett                        {
528210284Sjmallett                            cvmx_dprintf("cvmx-flash: Hardware write timeout\n");
529210284Sjmallett                            cvmx_spinlock_unlock(&flash_lock);
530210284Sjmallett                            return -1;
531210284Sjmallett                        }
532210284Sjmallett                    }
533210284Sjmallett
534210284Sjmallett                    if (cvmx_get_cycle() > start_cycle + flash_info[chip_id].write_timeout)
535210284Sjmallett                    {
536210284Sjmallett                        cvmx_dprintf("cvmx-flash: Timeout writing block\n");
537210284Sjmallett                        cvmx_spinlock_unlock(&flash_lock);
538210284Sjmallett                        return -1;
539210284Sjmallett                    }
540210284Sjmallett                }
541210284Sjmallett
542210284Sjmallett                /* Increment to the next byte */
543210284Sjmallett                ptr++;
544210284Sjmallett                offset++;
545210284Sjmallett            }
546210284Sjmallett
547210284Sjmallett            __cvmx_flash_write_cmd(chip_id, 0x00, 0xf0); /* Reset the flash chip */
548210284Sjmallett            cvmx_spinlock_unlock(&flash_lock);
549210284Sjmallett            return 0;
550210284Sjmallett        }
551210284Sjmallett        case CFI_CMDSET_INTEL_STANDARD:
552210284Sjmallett        case CFI_CMDSET_INTEL_EXTENDED:
553210284Sjmallett        {
554210284Sjmallettcvmx_dprintf("%s:%d len=%d\n", __FUNCTION__, __LINE__, len);
555210284Sjmallett            /* Loop through one byte at a time */
556210284Sjmallett            while (len--)
557210284Sjmallett            {
558210284Sjmallett                /* Send the program sequence */
559210284Sjmallett                __cvmx_flash_write_cmd(chip_id, 0x00, 0xff); /* Reset the flash chip */
560210284Sjmallett                __cvmx_flash_write8(chip_id, offset, 0x40);
561210284Sjmallett                __cvmx_flash_write8(chip_id, offset, *ptr);
562210284Sjmallett
563210284Sjmallett                /* Loop polling for status */
564210284Sjmallett                uint8_t status = __cvmx_flash_read8(chip_id, offset);
565210284Sjmallett                uint64_t start_cycle = cvmx_get_cycle();
566210284Sjmallett                while ((status & 0x80) == 0)
567210284Sjmallett                {
568210284Sjmallett                    if (cvmx_get_cycle() > start_cycle + flash_info[chip_id].write_timeout)
569210284Sjmallett                    {
570210284Sjmallett                        cvmx_dprintf("cvmx-flash: Timeout writing block\n");
571210284Sjmallett                        cvmx_spinlock_unlock(&flash_lock);
572210284Sjmallett                        return -1;
573210284Sjmallett                    }
574210284Sjmallett                    status = __cvmx_flash_read8(chip_id, offset);
575210284Sjmallett                }
576210284Sjmallett
577210284Sjmallett                /* Check the final status */
578210284Sjmallett                if (status & 0x7f)
579210284Sjmallett                {
580210284Sjmallett                    cvmx_dprintf("cvmx-flash: Hardware failure erasing block\n");
581210284Sjmallett                    cvmx_spinlock_unlock(&flash_lock);
582210284Sjmallett                    return -1;
583210284Sjmallett                }
584210284Sjmallett
585210284Sjmallett                /* Increment to the next byte */
586210284Sjmallett                ptr++;
587210284Sjmallett                offset++;
588210284Sjmallett            }
589210284Sjmallettcvmx_dprintf("%s:%d\n", __FUNCTION__, __LINE__);
590210284Sjmallett
591210284Sjmallett            __cvmx_flash_write_cmd(chip_id, 0x00, 0xff); /* Reset the flash chip */
592210284Sjmallett            cvmx_spinlock_unlock(&flash_lock);
593210284Sjmallett            return 0;
594210284Sjmallett        }
595210284Sjmallett    }
596210284Sjmallett
597210284Sjmallett    cvmx_dprintf("cvmx-flash: Unsupported flash vendor\n");
598210284Sjmallett    cvmx_spinlock_unlock(&flash_lock);
599210284Sjmallett    return -1;
600210284Sjmallett}
601210284Sjmallett
602210284Sjmallett
603210284Sjmallett/**
604210284Sjmallett * Erase and write data to a flash
605210284Sjmallett *
606210284Sjmallett * @param address Memory address to write to
607210284Sjmallett * @param data    Data to write
608210284Sjmallett * @param len     Length of the data
609210284Sjmallett * @return Zero on success. Negative on failure
610210284Sjmallett */
611210284Sjmallettint cvmx_flash_write(void *address, const void *data, int len)
612210284Sjmallett{
613210284Sjmallett    int chip_id;
614210284Sjmallett
615210284Sjmallett    /* Find which chip controls this address. Don't allow the write to span
616210284Sjmallett        multiple chips */
617210284Sjmallett    for (chip_id=0; chip_id<MAX_NUM_FLASH_CHIPS; chip_id++)
618210284Sjmallett    {
619210284Sjmallett        if ((flash_info[chip_id].base_ptr <= address) &&
620210284Sjmallett            (flash_info[chip_id].base_ptr + flash_info[chip_id].size >= address + len))
621210284Sjmallett            break;
622210284Sjmallett    }
623210284Sjmallett
624210284Sjmallett    if (chip_id == MAX_NUM_FLASH_CHIPS)
625210284Sjmallett    {
626210284Sjmallett        cvmx_dprintf("cvmx-flash: Unable to find chip that contains address %p\n", address);
627210284Sjmallett        return -1;
628210284Sjmallett    }
629210284Sjmallett
630210284Sjmallett    cvmx_flash_t *flash = flash_info + chip_id;
631210284Sjmallett
632210284Sjmallett    /* Determine which block region we need to start writing to */
633210284Sjmallett    void *region_base = flash->base_ptr;
634210284Sjmallett    int region = 0;
635210284Sjmallett    while (region_base + flash->region[region].num_blocks * flash->region[region].block_size <= address)
636210284Sjmallett    {
637210284Sjmallett        region++;
638210284Sjmallett        region_base = flash->base_ptr + flash->region[region].start_offset;
639210284Sjmallett    }
640210284Sjmallett
641210284Sjmallett    /* Determine which block in the region to start at */
642210284Sjmallett    int block = (address - region_base) / flash->region[region].block_size;
643210284Sjmallett
644210284Sjmallett    /* Require all writes to start on block boundries */
645210284Sjmallett    if (address != region_base + block*flash->region[region].block_size)
646210284Sjmallett    {
647210284Sjmallett        cvmx_dprintf("cvmx-flash: Write address not aligned on a block boundry\n");
648210284Sjmallett        return -1;
649210284Sjmallett    }
650210284Sjmallett
651210284Sjmallett    /* Loop until we're out of data */
652210284Sjmallett    while (len > 0)
653210284Sjmallett    {
654210284Sjmallett        /* Erase the current block */
655210284Sjmallett        if (cvmx_flash_erase_block(chip_id, region, block))
656210284Sjmallett            return -1;
657210284Sjmallett        /* Write the new data */
658210284Sjmallett        if (cvmx_flash_write_block(chip_id, region, block, data))
659210284Sjmallett            return -1;
660210284Sjmallett
661210284Sjmallett        /* Increment to the next block */
662210284Sjmallett        data += flash->region[region].block_size;
663210284Sjmallett        len -= flash->region[region].block_size;
664210284Sjmallett        block++;
665210284Sjmallett        if (block >= flash->region[region].num_blocks)
666210284Sjmallett        {
667210284Sjmallett            block = 0;
668210284Sjmallett            region++;
669210284Sjmallett        }
670210284Sjmallett    }
671210284Sjmallett
672210284Sjmallett    return 0;
673210284Sjmallett}
674210284Sjmallett
675