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