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 41215990Sjmallett 42210284Sjmallett/** 43210284Sjmallett * @file 44210284Sjmallett * 45210284Sjmallett * Interface to the NAND flash controller. 46210284Sjmallett * See cvmx-nand.h for usage documentation and notes. 47210284Sjmallett * 48210284Sjmallett * <hr>$Revision: 35726 $<hr> 49210284Sjmallett */ 50210284Sjmallett 51215990Sjmallett#ifdef CVMX_BUILD_FOR_LINUX_KERNEL 52215990Sjmallett#include <linux/module.h> 53215990Sjmallett 54215990Sjmallett#include <asm/octeon/cvmx.h> 55215990Sjmallett#include <asm/octeon/cvmx-clock.h> 56215990Sjmallett#include <asm/octeon/cvmx-nand.h> 57215990Sjmallett#include <asm/octeon/cvmx-ndf-defs.h> 58215990Sjmallett#include <asm/octeon/cvmx-swap.h> 59215990Sjmallett#include <asm/octeon/cvmx-bootmem.h> 60215990Sjmallett#else 61210284Sjmallett#include "cvmx.h" 62210284Sjmallett#include "cvmx-nand.h" 63210284Sjmallett#include "cvmx-swap.h" 64210284Sjmallett#include "cvmx-bootmem.h" 65215990Sjmallett#endif 66232812Sjmallett#if defined(__U_BOOT__) && defined(CONFIG_HW_WATCHDOG) 67232812Sjmallett# include <watchdog.h> 68232812Sjmallett#else 69232812Sjmallett# define WATCHDOG_RESET() 70232812Sjmallett#endif 71210284Sjmallett 72210284Sjmallett#define NAND_COMMAND_READ_ID 0x90 73210284Sjmallett#define NAND_COMMAND_READ_PARAM_PAGE 0xec 74210284Sjmallett#define NAND_COMMAND_RESET 0xff 75210284Sjmallett#define NAND_COMMAND_STATUS 0x70 76210284Sjmallett#define NAND_COMMAND_READ 0x00 77210284Sjmallett#define NAND_COMMAND_READ_FIN 0x30 78210284Sjmallett#define NAND_COMMAND_ERASE 0x60 79210284Sjmallett#define NAND_COMMAND_ERASE_FIN 0xd0 80210284Sjmallett#define NAND_COMMAND_PROGRAM 0x80 81210284Sjmallett#define NAND_COMMAND_PROGRAM_FIN 0x10 82232812Sjmallett#define NAND_TIMEOUT_USECS_READ 100000 83232812Sjmallett#define NAND_TIMEOUT_USECS_WRITE 1000000 84232812Sjmallett#define NAND_TIMEOUT_USECS_BLOCK_ERASE 1000000 85210284Sjmallett 86232812Sjmallett#define CVMX_NAND_ROUNDUP(_Dividend, _Divisor) (((_Dividend)+((_Divisor)-1))/(_Divisor)) 87210284Sjmallett#undef min 88210284Sjmallett#define min(X, Y) \ 89232812Sjmallett ({ typeof (X) __x = (X); \ 90232812Sjmallett typeof (Y) __y = (Y); \ 91210284Sjmallett (__x < __y) ? __x : __y; }) 92210284Sjmallett 93210284Sjmallett#undef max 94210284Sjmallett#define max(X, Y) \ 95232812Sjmallett ({ typeof (X) __x = (X); \ 96232812Sjmallett typeof (Y) __y = (Y); \ 97210284Sjmallett (__x > __y) ? __x : __y; }) 98210284Sjmallett 99210284Sjmallett 100210284Sjmallett/* Structure to store the parameters that we care about that 101210284Sjmallett** describe the ONFI speed modes. This is used to configure 102215990Sjmallett** the flash timing to match what is reported in the 103210284Sjmallett** parameter page of the ONFI flash chip. */ 104210284Sjmalletttypedef struct 105210284Sjmallett{ 106210284Sjmallett int twp; 107210284Sjmallett int twh; 108210284Sjmallett int twc; 109210284Sjmallett int tclh; 110210284Sjmallett int tals; 111210284Sjmallett} onfi_speed_mode_desc_t; 112210284Sjmallettstatic const onfi_speed_mode_desc_t onfi_speed_modes[] = 113210284Sjmallett{ 114210284Sjmallett 115210284Sjmallett {50,30,100,20,50}, /* Mode 0 */ 116210284Sjmallett {25,15, 45,10,25}, /* Mode 1 */ 117210284Sjmallett {17,15, 35,10,15}, /* Mode 2 */ 118210284Sjmallett {15,10, 30, 5,10}, /* Mode 3 */ 119210284Sjmallett {12,10, 25, 5,10}, /* Mode 4, requires EDO timings */ 120210284Sjmallett {10, 7, 20, 5,10}, /* Mode 5, requries EDO timings */ 121232812Sjmallett {10,10, 25, 5,12}, /* Mode 6, requires EDO timings */ 122210284Sjmallett}; 123210284Sjmallett 124215990Sjmallett 125215990Sjmallett 126215990Sjmalletttypedef enum 127215990Sjmallett{ 128215990Sjmallett CVMX_NAND_STATE_16BIT = 1<<0, 129215990Sjmallett} cvmx_nand_state_flags_t; 130215990Sjmallett 131210284Sjmallett/** 132210284Sjmallett * Structure used to store data about the NAND devices hooked 133210284Sjmallett * to the bootbus. 134210284Sjmallett */ 135210284Sjmalletttypedef struct 136210284Sjmallett{ 137210284Sjmallett int page_size; 138210284Sjmallett int oob_size; 139210284Sjmallett int pages_per_block; 140210284Sjmallett int blocks; 141210284Sjmallett int tim_mult; 142210284Sjmallett int tim_par[8]; 143210284Sjmallett int clen[4]; 144210284Sjmallett int alen[4]; 145210284Sjmallett int rdn[4]; 146210284Sjmallett int wrn[2]; 147210284Sjmallett int onfi_timing; 148215990Sjmallett cvmx_nand_state_flags_t flags; 149210284Sjmallett} cvmx_nand_state_t; 150210284Sjmallett 151210284Sjmallett/** 152210284Sjmallett * Array indexed by bootbus chip select with information 153210284Sjmallett * about NAND devices. 154210284Sjmallett */ 155232812Sjmallett#if defined(__U_BOOT__) 156210284Sjmallett/* For u-boot nand boot we need to play some tricks to be able 157210284Sjmallett** to use this early in boot. We put them in a special section that is merged 158210284Sjmallett** with the text segment. (Using the text segment directly results in an assembler warning.) 159210284Sjmallett*/ 160232812Sjmallett/*#define USE_DATA_IN_TEXT*/ 161210284Sjmallett#endif 162210284Sjmallett 163210284Sjmallett#ifdef USE_DATA_IN_TEXT 164215990Sjmallettstatic uint8_t cvmx_nand_buffer[CVMX_NAND_MAX_PAGE_AND_OOB_SIZE] __attribute__((aligned(8))) __attribute__ ((section (".data_in_text"))); 165210284Sjmallettstatic cvmx_nand_state_t cvmx_nand_state[8] __attribute__ ((section (".data_in_text"))); 166215990Sjmallettstatic cvmx_nand_state_t cvmx_nand_default __attribute__ ((section (".data_in_text"))); 167210284Sjmallettstatic cvmx_nand_initialize_flags_t cvmx_nand_flags __attribute__ ((section (".data_in_text"))); 168210284Sjmallettstatic int debug_indent __attribute__ ((section (".data_in_text"))); 169210284Sjmallett#else 170210284Sjmallettstatic CVMX_SHARED cvmx_nand_state_t cvmx_nand_state[8]; 171215990Sjmallettstatic CVMX_SHARED cvmx_nand_state_t cvmx_nand_default; 172210284Sjmallettstatic CVMX_SHARED cvmx_nand_initialize_flags_t cvmx_nand_flags; 173210284Sjmallettstatic CVMX_SHARED uint8_t *cvmx_nand_buffer = NULL; 174210284Sjmallettstatic int debug_indent = 0; 175210284Sjmallett#endif 176210284Sjmallett 177210284Sjmallettstatic CVMX_SHARED const char *cvmx_nand_opcode_labels[] = 178210284Sjmallett{ 179210284Sjmallett "NOP", /* 0 */ 180210284Sjmallett "Timing", /* 1 */ 181210284Sjmallett "Wait", /* 2 */ 182210284Sjmallett "Chip Enable / Disable", /* 3 */ 183210284Sjmallett "CLE", /* 4 */ 184210284Sjmallett "ALE", /* 5 */ 185210284Sjmallett "6 - Unknown", /* 6 */ 186210284Sjmallett "7 - Unknown", /* 7 */ 187210284Sjmallett "Write", /* 8 */ 188210284Sjmallett "Read", /* 9 */ 189210284Sjmallett "Read EDO", /* 10 */ 190210284Sjmallett "Wait Status", /* 11 */ 191210284Sjmallett "12 - Unknown", /* 12 */ 192210284Sjmallett "13 - Unknown", /* 13 */ 193210284Sjmallett "14 - Unknown", /* 14 */ 194210284Sjmallett "Bus Aquire / Release" /* 15 */ 195210284Sjmallett}; 196210284Sjmallett 197210284Sjmallett#define ULL unsigned long long 198210284Sjmallett/* This macro logs out whenever a function is called if debugging is on */ 199210284Sjmallett#define CVMX_NAND_LOG_CALLED() \ 200210284Sjmallett if (cvmx_unlikely(cvmx_nand_flags & CVMX_NAND_INITIALIZE_FLAGS_DEBUG)) \ 201210284Sjmallett cvmx_dprintf("%*s%s: called\n", 2*debug_indent++, "", __FUNCTION__); 202210284Sjmallett 203210284Sjmallett/* This macro logs out each function parameter if debugging is on */ 204210284Sjmallett#define CVMX_NAND_LOG_PARAM(format, param) \ 205210284Sjmallett if (cvmx_unlikely(cvmx_nand_flags & CVMX_NAND_INITIALIZE_FLAGS_DEBUG)) \ 206210284Sjmallett cvmx_dprintf("%*s%s: param %s = " format "\n", 2*debug_indent, "", __FUNCTION__, #param, param); 207210284Sjmallett 208210284Sjmallett/* This macro logs out when a function returns a value */ 209210284Sjmallett#define CVMX_NAND_RETURN(v) \ 210210284Sjmallett do { \ 211210284Sjmallett typeof(v) r = v; \ 212210284Sjmallett if (cvmx_unlikely(cvmx_nand_flags & CVMX_NAND_INITIALIZE_FLAGS_DEBUG)) \ 213210284Sjmallett cvmx_dprintf("%*s%s: returned %s(%d)\n", 2*--debug_indent, "", __FUNCTION__, #v, r); \ 214210284Sjmallett return r; \ 215210284Sjmallett } while (0); 216210284Sjmallett 217210284Sjmallett/* This macro logs out when a function doesn't return a value */ 218210284Sjmallett#define CVMX_NAND_RETURN_NOTHING() \ 219210284Sjmallett do { \ 220210284Sjmallett if (cvmx_unlikely(cvmx_nand_flags & CVMX_NAND_INITIALIZE_FLAGS_DEBUG)) \ 221210284Sjmallett cvmx_dprintf("%*s%s: returned\n", 2*--debug_indent, "", __FUNCTION__); \ 222210284Sjmallett return; \ 223210284Sjmallett } while (0); 224210284Sjmallett 225210284Sjmallett 226210284Sjmallett 227210284Sjmallett 228210284Sjmallett 229210284Sjmallett 230210284Sjmallett/* Compute the CRC for the ONFI parameter page. Adapted from sample code 231210284Sjmallett** in the specification. 232210284Sjmallett*/ 233210284Sjmallettstatic uint16_t __onfi_parameter_crc_compute(uint8_t *data) 234210284Sjmallett{ 235210284Sjmallett const int order = 16; // Order of the CRC-16 236210284Sjmallett unsigned long i, j, c, bit; 237210284Sjmallett unsigned long crc = 0x4F4E; // Initialize the shift register with 0x4F4E 238210284Sjmallett unsigned long crcmask = ((((unsigned long)1<<(order-1))-1)<<1)|1; 239210284Sjmallett unsigned long crchighbit = (unsigned long)1<<(order-1); 240210284Sjmallett 241210284Sjmallett for (i = 0; i < 254; i++) 242210284Sjmallett { 243210284Sjmallett c = (unsigned long)data[i]; 244210284Sjmallett for (j = 0x80; j; j >>= 1) { 245210284Sjmallett bit = crc & crchighbit; 246210284Sjmallett crc <<= 1; 247210284Sjmallett if (c & j) 248210284Sjmallett bit ^= crchighbit; 249210284Sjmallett if (bit) 250210284Sjmallett crc ^= 0x8005; 251210284Sjmallett } 252210284Sjmallett crc &= crcmask; 253210284Sjmallett } 254210284Sjmallett return(crc); 255210284Sjmallett} 256210284Sjmallett 257210284Sjmallett 258210284Sjmallett/** 259210284Sjmallett * Validate the ONFI parameter page and return a pointer to 260210284Sjmallett * the config values. 261210284Sjmallett * 262210284Sjmallett * @param param_page Pointer to the raw NAND data returned after a parameter page read. It will 263210284Sjmallett * contain at least 4 copies of the parameter structure. 264210284Sjmallett * 265210284Sjmallett * @return Pointer to a validated paramter page, or NULL if one couldn't be found. 266210284Sjmallett */ 267210284Sjmallettstatic cvmx_nand_onfi_param_page_t *__cvmx_nand_onfi_process(cvmx_nand_onfi_param_page_t param_page[4]) 268210284Sjmallett{ 269210284Sjmallett int index; 270210284Sjmallett 271210284Sjmallett for (index=0; index<4; index++) 272210284Sjmallett { 273210284Sjmallett uint16_t crc = __onfi_parameter_crc_compute((void *)¶m_page[index]); 274210284Sjmallett if (crc == cvmx_le16_to_cpu(param_page[index].crc)) 275210284Sjmallett break; 276210284Sjmallett if (cvmx_unlikely(cvmx_nand_flags & CVMX_NAND_INITIALIZE_FLAGS_DEBUG)) 277210284Sjmallett cvmx_dprintf("%s: Paramter page %d is corrupt. (Expected CRC: 0x%04x, computed: 0x%04x)\n", 278210284Sjmallett __FUNCTION__, index, cvmx_le16_to_cpu(param_page[index].crc), crc); 279210284Sjmallett } 280210284Sjmallett 281210284Sjmallett if (index == 4) 282210284Sjmallett { 283210284Sjmallett if (cvmx_unlikely(cvmx_nand_flags & CVMX_NAND_INITIALIZE_FLAGS_DEBUG)) 284210284Sjmallett cvmx_dprintf("%s: All parameter pages fail CRC check. Checking to see if any look sane.\n", __FUNCTION__); 285210284Sjmallett 286210284Sjmallett if (!memcmp(param_page, param_page + 1, 256)) 287210284Sjmallett { 288210284Sjmallett /* First and second copies match, now check some values */ 289210284Sjmallett if (param_page[0].pages_per_block != 0 && param_page[0].pages_per_block != 0xFFFFFFFF 290210284Sjmallett && param_page[0].page_data_bytes != 0 && param_page[0].page_data_bytes != 0xFFFFFFFF 291210284Sjmallett && param_page[0].page_spare_bytes != 0 && param_page[0].page_spare_bytes != 0xFFFF 292210284Sjmallett && param_page[0].blocks_per_lun != 0 && param_page[0].blocks_per_lun != 0xFFFFFFFF 293210284Sjmallett && param_page[0].timing_mode != 0 && param_page[0].timing_mode != 0xFFFF) 294210284Sjmallett { 295210284Sjmallett /* Looks like we have enough values to use */ 296210284Sjmallett if (cvmx_unlikely(cvmx_nand_flags & CVMX_NAND_INITIALIZE_FLAGS_DEBUG)) 297210284Sjmallett cvmx_dprintf("%s: Page 0 looks sane, using even though CRC fails.\n", __FUNCTION__); 298210284Sjmallett index = 0; 299210284Sjmallett } 300210284Sjmallett } 301210284Sjmallett } 302210284Sjmallett 303210284Sjmallett if (index == 4) 304210284Sjmallett { 305215990Sjmallett cvmx_dprintf("%s: WARNING: ONFI part but no valid ONFI parameter pages found.\n", __FUNCTION__); 306210284Sjmallett return NULL; 307210284Sjmallett } 308210284Sjmallett 309210284Sjmallett if (cvmx_unlikely(cvmx_nand_flags & CVMX_NAND_INITIALIZE_FLAGS_DEBUG)) 310210284Sjmallett { 311215990Sjmallett cvmx_dprintf("%*sONFI Information (from copy %d in param page)\n", 2*debug_indent, "", index); 312210284Sjmallett debug_indent++; 313210284Sjmallett cvmx_dprintf("%*sonfi = %c%c%c%c\n", 2*debug_indent, "", param_page[index].onfi[0], param_page[index].onfi[1], 314210284Sjmallett param_page[index].onfi[2], param_page[index].onfi[3]); 315210284Sjmallett cvmx_dprintf("%*srevision_number = 0x%x\n", 2*debug_indent, "", cvmx_le16_to_cpu(param_page[index].revision_number)); 316210284Sjmallett cvmx_dprintf("%*sfeatures = 0x%x\n", 2*debug_indent, "", cvmx_le16_to_cpu(param_page[index].features)); 317210284Sjmallett cvmx_dprintf("%*soptional_commands = 0x%x\n", 2*debug_indent, "", cvmx_le16_to_cpu(param_page[index].optional_commands)); 318210284Sjmallett 319210284Sjmallett cvmx_dprintf("%*smanufacturer = %12.12s\n", 2*debug_indent, "", param_page[index].manufacturer); 320210284Sjmallett cvmx_dprintf("%*smodel = %20.20s\n", 2*debug_indent, "", param_page[index].model); 321210284Sjmallett cvmx_dprintf("%*sjedec_id = 0x%x\n", 2*debug_indent, "", param_page[index].jedec_id); 322210284Sjmallett cvmx_dprintf("%*sdate_code = 0x%x\n", 2*debug_indent, "", cvmx_le16_to_cpu(param_page[index].date_code)); 323210284Sjmallett 324210284Sjmallett cvmx_dprintf("%*spage_data_bytes = %u\n", 2*debug_indent, "", (int)cvmx_le32_to_cpu(param_page[index].page_data_bytes)); 325210284Sjmallett cvmx_dprintf("%*spage_spare_bytes = %u\n", 2*debug_indent, "", cvmx_le16_to_cpu(param_page[index].page_spare_bytes)); 326210284Sjmallett cvmx_dprintf("%*spartial_page_data_bytes = %u\n", 2*debug_indent, "", (int)cvmx_le32_to_cpu(param_page[index].partial_page_data_bytes)); 327210284Sjmallett cvmx_dprintf("%*spartial_page_spare_bytes = %u\n", 2*debug_indent, "", cvmx_le16_to_cpu(param_page[index].partial_page_spare_bytes)); 328210284Sjmallett cvmx_dprintf("%*spages_per_block = %u\n", 2*debug_indent, "", (int)cvmx_le32_to_cpu(param_page[index].pages_per_block)); 329210284Sjmallett cvmx_dprintf("%*sblocks_per_lun = %u\n", 2*debug_indent, "", (int)cvmx_le32_to_cpu(param_page[index].blocks_per_lun)); 330210284Sjmallett cvmx_dprintf("%*snumber_lun = %u\n", 2*debug_indent, "", param_page[index].number_lun); 331210284Sjmallett cvmx_dprintf("%*saddress_cycles = 0x%x\n", 2*debug_indent, "", param_page[index].address_cycles); 332210284Sjmallett cvmx_dprintf("%*sbits_per_cell = %u\n", 2*debug_indent, "", param_page[index].bits_per_cell); 333210284Sjmallett cvmx_dprintf("%*sbad_block_per_lun = %u\n", 2*debug_indent, "", cvmx_le16_to_cpu(param_page[index].bad_block_per_lun)); 334210284Sjmallett cvmx_dprintf("%*sblock_endurance = %u\n", 2*debug_indent, "", cvmx_le16_to_cpu(param_page[index].block_endurance)); 335210284Sjmallett cvmx_dprintf("%*sgood_blocks = %u\n", 2*debug_indent, "", param_page[index].good_blocks); 336210284Sjmallett cvmx_dprintf("%*sgood_block_endurance = %u\n", 2*debug_indent, "", cvmx_le16_to_cpu(param_page[index].good_block_endurance)); 337210284Sjmallett cvmx_dprintf("%*sprograms_per_page = %u\n", 2*debug_indent, "", param_page[index].programs_per_page); 338210284Sjmallett cvmx_dprintf("%*spartial_program_attrib = 0x%x\n", 2*debug_indent, "", param_page[index].partial_program_attrib); 339210284Sjmallett cvmx_dprintf("%*sbits_ecc = %u\n", 2*debug_indent, "", param_page[index].bits_ecc); 340210284Sjmallett cvmx_dprintf("%*sinterleaved_address_bits = 0x%x\n", 2*debug_indent, "", param_page[index].interleaved_address_bits); 341210284Sjmallett cvmx_dprintf("%*sinterleaved_attrib = 0x%x\n", 2*debug_indent, "", param_page[index].interleaved_attrib); 342210284Sjmallett 343210284Sjmallett cvmx_dprintf("%*spin_capacitance = %u\n", 2*debug_indent, "", param_page[index].pin_capacitance); 344210284Sjmallett cvmx_dprintf("%*stiming_mode = 0x%x\n", 2*debug_indent, "", cvmx_le16_to_cpu(param_page[index].timing_mode)); 345210284Sjmallett cvmx_dprintf("%*scache_timing_mode = 0x%x\n", 2*debug_indent, "", cvmx_le16_to_cpu(param_page[index].cache_timing_mode)); 346210284Sjmallett cvmx_dprintf("%*st_prog = %d us\n", 2*debug_indent, "", cvmx_le16_to_cpu(param_page[index].t_prog)); 347210284Sjmallett cvmx_dprintf("%*st_bers = %u us\n", 2*debug_indent, "", cvmx_le16_to_cpu(param_page[index].t_bers)); 348210284Sjmallett cvmx_dprintf("%*st_r = %u us\n", 2*debug_indent, "", cvmx_le16_to_cpu(param_page[index].t_r)); 349210284Sjmallett cvmx_dprintf("%*st_ccs = %u ns\n", 2*debug_indent, "", cvmx_le16_to_cpu(param_page[index].t_ccs)); 350210284Sjmallett cvmx_dprintf("%*svendor_revision = 0x%x\n", 2*debug_indent, "", cvmx_le16_to_cpu(param_page[index].vendor_revision)); 351210284Sjmallett //uint8_t vendor_specific[88]; /**< Byte 166-253: Vendor specific */ 352210284Sjmallett cvmx_dprintf("%*scrc = 0x%x\n", 2*debug_indent, "", param_page[index].crc); 353210284Sjmallett debug_indent--; 354210284Sjmallett } 355210284Sjmallett return param_page + index; 356210284Sjmallett} 357210284Sjmallett 358210284Sjmallettvoid __set_onfi_timing_mode(int *tim_par, int clocks_us, int mode) 359210284Sjmallett{ 360210284Sjmallett const onfi_speed_mode_desc_t *mp = &onfi_speed_modes[mode]; /* use shorter name to fill in timing array */ 361210284Sjmallett int margin; 362210284Sjmallett int pulse_adjust; 363210284Sjmallett 364232812Sjmallett if (mode > 6) 365210284Sjmallett { 366210284Sjmallett cvmx_dprintf("%s: invalid ONFI timing mode: %d\n", __FUNCTION__, mode); 367210284Sjmallett return; 368210284Sjmallett } 369210284Sjmallett 370210284Sjmallett /* Adjust the read/write pulse duty cycle to make it more even. The cycle time 371210284Sjmallett ** requirement is longer than the sum of the high low times, so we exend both the high 372210284Sjmallett ** and low times to meet the cycle time requirement. 373210284Sjmallett */ 374210284Sjmallett pulse_adjust = ((mp->twc - mp->twh - mp->twp)/2 + 1) * clocks_us; 375210284Sjmallett 376210284Sjmallett /* Add a small margin to all timings. */ 377210284Sjmallett margin = 2 * clocks_us; 378210284Sjmallett /* Update timing parameters based on supported mode */ 379210284Sjmallett tim_par[1] = CVMX_NAND_ROUNDUP(mp->twp * clocks_us + margin + pulse_adjust, 1000); /* Twp, WE# pulse width */ 380210284Sjmallett tim_par[2] = CVMX_NAND_ROUNDUP(max(mp->twh, mp->twc - mp->twp) * clocks_us + margin + pulse_adjust, 1000); /* Tw, WE# pulse width high */ 381210284Sjmallett tim_par[3] = CVMX_NAND_ROUNDUP(mp->tclh * clocks_us + margin, 1000); /* Tclh, CLE hold time */ 382210284Sjmallett tim_par[4] = CVMX_NAND_ROUNDUP(mp->tals * clocks_us + margin, 1000); /* Tals, ALE setup time */ 383210284Sjmallett tim_par[5] = tim_par[3]; /* Talh, ALE hold time */ 384210284Sjmallett tim_par[6] = tim_par[1]; /* Trp, RE# pulse width*/ 385210284Sjmallett tim_par[7] = tim_par[2]; /* Treh, RE# high hold time */ 386210284Sjmallett 387210284Sjmallett} 388210284Sjmallett 389215990Sjmallett 390215990Sjmallett/* Internal helper function to set chip configuration to use default values */ 391215990Sjmallettstatic void __set_chip_defaults(int chip, int clocks_us) 392215990Sjmallett{ 393215990Sjmallett if (!cvmx_nand_default.page_size) 394215990Sjmallett return; 395215990Sjmallett cvmx_nand_state[chip].page_size = cvmx_nand_default.page_size; /* NAND page size in bytes */ 396215990Sjmallett cvmx_nand_state[chip].oob_size = cvmx_nand_default.oob_size; /* NAND OOB (spare) size in bytes (per page) */ 397215990Sjmallett cvmx_nand_state[chip].pages_per_block = cvmx_nand_default.pages_per_block; 398215990Sjmallett cvmx_nand_state[chip].blocks = cvmx_nand_default.blocks; 399215990Sjmallett cvmx_nand_state[chip].onfi_timing = cvmx_nand_default.onfi_timing; 400215990Sjmallett __set_onfi_timing_mode(cvmx_nand_state[chip].tim_par, clocks_us, cvmx_nand_state[chip].onfi_timing); 401215990Sjmallett if (cvmx_unlikely(cvmx_nand_flags & CVMX_NAND_INITIALIZE_FLAGS_DEBUG)) 402215990Sjmallett { 403215990Sjmallett 404215990Sjmallett cvmx_dprintf("%s: Using default NAND parameters.\n", __FUNCTION__); 405215990Sjmallett cvmx_dprintf("%s: Defaults: page size: %d, OOB size: %d, pages per block %d, blocks: %d, timing mode: %d\n", 406215990Sjmallett __FUNCTION__, cvmx_nand_state[chip].page_size, cvmx_nand_state[chip].oob_size, cvmx_nand_state[chip].pages_per_block, 407215990Sjmallett cvmx_nand_state[chip].blocks, cvmx_nand_state[chip].onfi_timing); 408215990Sjmallett } 409215990Sjmallett} 410215990Sjmallett/* Do the proper wait for the ready/busy signal. First wait 411215990Sjmallett** for busy to be valid, then wait for busy to de-assert. 412215990Sjmallett*/ 413215990Sjmallettstatic int __wait_for_busy_done(int chip) 414215990Sjmallett{ 415215990Sjmallett cvmx_nand_cmd_t cmd; 416215990Sjmallett 417215990Sjmallett CVMX_NAND_LOG_CALLED(); 418215990Sjmallett CVMX_NAND_LOG_PARAM("%d", chip); 419215990Sjmallett 420215990Sjmallett memset(&cmd, 0, sizeof(cmd)); 421215990Sjmallett cmd.wait.two = 2; 422215990Sjmallett cmd.wait.r_b=0; 423215990Sjmallett cmd.wait.n = 2; 424215990Sjmallett 425215990Sjmallett /* Wait for RB to be valied (tWB). 426215990Sjmallett ** Use 5 * tWC as proxy. In some modes this is 427215990Sjmallett ** much longer than required, but does not affect performance 428215990Sjmallett ** since we will wait much longer for busy to de-assert. 429215990Sjmallett */ 430215990Sjmallett if (cvmx_nand_submit(cmd)) 431215990Sjmallett CVMX_NAND_RETURN(CVMX_NAND_NO_MEMORY); 432215990Sjmallett if (cvmx_nand_submit(cmd)) 433215990Sjmallett CVMX_NAND_RETURN(CVMX_NAND_NO_MEMORY); 434215990Sjmallett if (cvmx_nand_submit(cmd)) 435215990Sjmallett CVMX_NAND_RETURN(CVMX_NAND_NO_MEMORY); 436215990Sjmallett if (cvmx_nand_submit(cmd)) 437215990Sjmallett CVMX_NAND_RETURN(CVMX_NAND_NO_MEMORY); 438215990Sjmallett cmd.wait.r_b=1; /* Now wait for busy to be de-asserted */ 439215990Sjmallett if (cvmx_nand_submit(cmd)) 440215990Sjmallett CVMX_NAND_RETURN(CVMX_NAND_NO_MEMORY); 441215990Sjmallett 442215990Sjmallett CVMX_NAND_RETURN(CVMX_NAND_SUCCESS); 443215990Sjmallett} 444210284Sjmallett/** 445210284Sjmallett * Called to initialize the NAND controller for use. Note that 446210284Sjmallett * you must be running out of L2 or memory and not NAND before 447210284Sjmallett * calling this function. 448215990Sjmallett * When probing for NAND chips, this function attempts to autoconfigure based on the NAND parts detected. 449215990Sjmallett * It currently supports autodetection for ONFI parts (with valid parameter pages), and some Samsung NAND 450215990Sjmallett * parts (decoding ID bits.) If autoconfiguration fails, the defaults set with __set_chip_defaults() 451215990Sjmallett * prior to calling cvmx_nand_initialize() are used. 452215990Sjmallett * If defaults are set and the CVMX_NAND_INITIALIZE_FLAGS_DONT_PROBE flag is provided, the defaults are used 453215990Sjmallett * for all chips in the active_chips mask. 454210284Sjmallett * 455210284Sjmallett * @param flags Optional initialization flags 456215990Sjmallett * If the CVMX_NAND_INITIALIZE_FLAGS_DONT_PROBE flag is passed, chips are not probed, 457215990Sjmallett * and the default parameters (if set with cvmx_nand_set_defaults) are used for all chips 458215990Sjmallett * in the active_chips mask. 459210284Sjmallett * @param active_chips 460210284Sjmallett * Each bit in this parameter represents a chip select that might 461210284Sjmallett * contain NAND flash. Any chip select present in this bitmask may 462210284Sjmallett * be connected to NAND. It is normally safe to pass 0xff here and 463210284Sjmallett * let the API probe all 8 chip selects. 464210284Sjmallett * 465215990Sjmallett * @return Zero on success, a negative cvmx_nand_status error code on failure 466210284Sjmallett */ 467210284Sjmallettcvmx_nand_status_t cvmx_nand_initialize(cvmx_nand_initialize_flags_t flags, int active_chips) 468210284Sjmallett{ 469210284Sjmallett int chip; 470210284Sjmallett int start_chip; 471210284Sjmallett int stop_chip; 472210284Sjmallett uint64_t clocks_us; 473215990Sjmallett union cvmx_ndf_misc ndf_misc; 474215990Sjmallett uint8_t nand_id_buffer[16]; 475210284Sjmallett 476232812Sjmallett if (!octeon_has_feature(OCTEON_FEATURE_NAND)) 477232812Sjmallett CVMX_NAND_RETURN(CVMX_NAND_NO_DEVICE); 478232812Sjmallett 479210284Sjmallett cvmx_nand_flags = flags; 480210284Sjmallett CVMX_NAND_LOG_CALLED(); 481210284Sjmallett CVMX_NAND_LOG_PARAM("0x%x", flags); 482210284Sjmallett 483210284Sjmallett memset(&cvmx_nand_state, 0, sizeof(cvmx_nand_state)); 484210284Sjmallett 485210284Sjmallett#ifndef USE_DATA_IN_TEXT 486215990Sjmallett /* cvmx_nand_buffer is statically allocated in the TEXT_IN_DATA case */ 487210284Sjmallett if (!cvmx_nand_buffer) 488232812Sjmallett { 489232812Sjmallett cvmx_nand_buffer = cvmx_bootmem_alloc_named_flags(CVMX_NAND_MAX_PAGE_AND_OOB_SIZE, 128, "__nand_buffer", CVMX_BOOTMEM_FLAG_END_ALLOC); 490232812Sjmallett } 491232812Sjmallett if (!cvmx_nand_buffer) { 492232812Sjmallett const cvmx_bootmem_named_block_desc_t *block_desc = cvmx_bootmem_find_named_block("__nand_buffer"); 493232812Sjmallett if (block_desc) 494232812Sjmallett cvmx_nand_buffer = cvmx_phys_to_ptr(block_desc->base_addr); 495232812Sjmallett } 496232812Sjmallett 497210284Sjmallett if (!cvmx_nand_buffer) 498210284Sjmallett CVMX_NAND_RETURN(CVMX_NAND_NO_MEMORY); 499215990Sjmallett#endif 500210284Sjmallett 501210284Sjmallett /* Disable boot mode and reset the fifo */ 502210284Sjmallett ndf_misc.u64 = cvmx_read_csr(CVMX_NDF_MISC); 503210284Sjmallett ndf_misc.s.rd_cmd = 0; 504210284Sjmallett ndf_misc.s.bt_dma = 0; 505210284Sjmallett ndf_misc.s.bt_dis = 1; 506210284Sjmallett ndf_misc.s.ex_dis = 0; 507210284Sjmallett ndf_misc.s.rst_ff = 1; 508210284Sjmallett cvmx_write_csr(CVMX_NDF_MISC, ndf_misc.u64); 509210284Sjmallett cvmx_read_csr(CVMX_NDF_MISC); 510210284Sjmallett 511210284Sjmallett /* Bring the fifo out of reset */ 512210284Sjmallett cvmx_wait_usec(1); 513210284Sjmallett ndf_misc.s.rst_ff = 0; 514210284Sjmallett cvmx_write_csr(CVMX_NDF_MISC, ndf_misc.u64); 515210284Sjmallett cvmx_read_csr(CVMX_NDF_MISC); 516210284Sjmallett cvmx_wait_usec(1); 517210284Sjmallett 518210284Sjmallett /* Clear the ECC counter */ 519210284Sjmallett //cvmx_write_csr(CVMX_NDF_ECC_CNT, cvmx_read_csr(CVMX_NDF_ECC_CNT)); 520210284Sjmallett 521210284Sjmallett /* Clear the interrupt state */ 522210284Sjmallett cvmx_write_csr(CVMX_NDF_INT, cvmx_read_csr(CVMX_NDF_INT)); 523210284Sjmallett cvmx_write_csr(CVMX_NDF_INT_EN, 0); 524210284Sjmallett cvmx_write_csr(CVMX_MIO_NDF_DMA_INT, cvmx_read_csr(CVMX_MIO_NDF_DMA_INT)); 525210284Sjmallett cvmx_write_csr(CVMX_MIO_NDF_DMA_INT_EN, 0); 526210284Sjmallett 527210284Sjmallett 528210284Sjmallett /* The simulator crashes if you access non existant devices. Assume 529210284Sjmallett only chip select 1 is connected to NAND */ 530210284Sjmallett if (cvmx_sysinfo_get()->board_type == CVMX_BOARD_TYPE_SIM) 531210284Sjmallett { 532210284Sjmallett start_chip = 1; 533210284Sjmallett stop_chip = 2; 534210284Sjmallett } 535210284Sjmallett else 536210284Sjmallett { 537210284Sjmallett start_chip = 0; 538210284Sjmallett stop_chip = 8; 539210284Sjmallett } 540210284Sjmallett 541210284Sjmallett /* Figure out how many clocks are in one microsecond, rounding up */ 542215990Sjmallett clocks_us = CVMX_NAND_ROUNDUP(cvmx_clock_get_rate(CVMX_CLOCK_SCLK), 1000000); 543210284Sjmallett 544215990Sjmallett /* If the CVMX_NAND_INITIALIZE_FLAGS_DONT_PROBE flag is set, then 545215990Sjmallett ** use the supplied default values to configured the chips in the 546215990Sjmallett ** active_chips mask */ 547215990Sjmallett if (cvmx_nand_flags & CVMX_NAND_INITIALIZE_FLAGS_DONT_PROBE) 548215990Sjmallett { 549215990Sjmallett if (cvmx_nand_default.page_size) 550215990Sjmallett { 551215990Sjmallett for (chip=start_chip; chip<stop_chip; chip++) 552215990Sjmallett { 553215990Sjmallett /* Skip chip selects that the caller didn't supply in the active chip bits */ 554215990Sjmallett if (((1<<chip) & active_chips) == 0) 555215990Sjmallett continue; 556215990Sjmallett __set_chip_defaults(chip, clocks_us); 557215990Sjmallett } 558215990Sjmallett } 559215990Sjmallett CVMX_NAND_RETURN(CVMX_NAND_SUCCESS); 560215990Sjmallett } 561215990Sjmallett 562210284Sjmallett /* Probe and see what NAND flash we can find */ 563210284Sjmallett for (chip=start_chip; chip<stop_chip; chip++) 564210284Sjmallett { 565215990Sjmallett union cvmx_mio_boot_reg_cfgx mio_boot_reg_cfg; 566210284Sjmallett cvmx_nand_onfi_param_page_t *onfi_param_page; 567215990Sjmallett int probe_failed; 568215990Sjmallett int width_16; 569210284Sjmallett 570210284Sjmallett /* Skip chip selects that the caller didn't supply in the active chip bits */ 571210284Sjmallett if (((1<<chip) & active_chips) == 0) 572210284Sjmallett continue; 573210284Sjmallett 574210284Sjmallett mio_boot_reg_cfg.u64 = cvmx_read_csr(CVMX_MIO_BOOT_REG_CFGX(chip)); 575210284Sjmallett /* Enabled regions can't be connected to NAND flash */ 576210284Sjmallett if (mio_boot_reg_cfg.s.en) 577210284Sjmallett continue; 578210284Sjmallett 579210284Sjmallett /* Start out with some sane, but slow, defaults */ 580210284Sjmallett cvmx_nand_state[chip].page_size = 0; 581210284Sjmallett cvmx_nand_state[chip].oob_size = 64; 582210284Sjmallett cvmx_nand_state[chip].pages_per_block = 64; 583210284Sjmallett cvmx_nand_state[chip].blocks = 100; 584210284Sjmallett 585210284Sjmallett 586210284Sjmallett /* Set timing mode to ONFI mode 0 for initial accesses */ 587210284Sjmallett __set_onfi_timing_mode(cvmx_nand_state[chip].tim_par, clocks_us, 0); 588210284Sjmallett 589210284Sjmallett /* Put the index of which timing parameter to use. The indexes are into the tim_par 590210284Sjmallett ** which match the indexes of the 8 timing parameters that the hardware supports. 591210284Sjmallett ** Index 0 is not software controlled, and is fixed by hardware. */ 592210284Sjmallett cvmx_nand_state[chip].clen[0] = 0; /* Command doesn't need to be held before WE */ 593210284Sjmallett cvmx_nand_state[chip].clen[1] = 1; /* Twp, WE# pulse width */ 594210284Sjmallett cvmx_nand_state[chip].clen[2] = 3; /* Tclh, CLE hold time */ 595210284Sjmallett cvmx_nand_state[chip].clen[3] = 1; 596210284Sjmallett 597210284Sjmallett cvmx_nand_state[chip].alen[0] = 4; /* Tals, ALE setup time */ 598210284Sjmallett cvmx_nand_state[chip].alen[1] = 1; /* Twp, WE# pulse width */ 599210284Sjmallett cvmx_nand_state[chip].alen[2] = 2; /* Twh, WE# pulse width high */ 600210284Sjmallett cvmx_nand_state[chip].alen[3] = 5; /* Talh, ALE hold time */ 601210284Sjmallett 602210284Sjmallett cvmx_nand_state[chip].rdn[0] = 0; 603210284Sjmallett cvmx_nand_state[chip].rdn[1] = 6; /* Trp, RE# pulse width*/ 604210284Sjmallett cvmx_nand_state[chip].rdn[2] = 7; /* Treh, RE# high hold time */ 605210284Sjmallett cvmx_nand_state[chip].rdn[3] = 0; 606210284Sjmallett 607210284Sjmallett cvmx_nand_state[chip].wrn[0] = 1; /* Twp, WE# pulse width */ 608210284Sjmallett cvmx_nand_state[chip].wrn[1] = 2; /* Twh, WE# pulse width high */ 609210284Sjmallett 610215990Sjmallett /* Probe and see if we get an answer. Read more than required, as in 611215990Sjmallett ** 16 bit mode only every other byte is valid. 612215990Sjmallett ** Here we probe twice, once in 8 bit mode, and once in 16 bit mode to autodetect 613215990Sjmallett ** the width. 614215990Sjmallett */ 615215990Sjmallett probe_failed = 1; 616215990Sjmallett for (width_16 = 0; width_16 <= 1 && probe_failed; width_16++) 617210284Sjmallett { 618215990Sjmallett probe_failed = 0; 619215990Sjmallett 620215990Sjmallett if (width_16) 621215990Sjmallett cvmx_nand_state[chip].flags |= CVMX_NAND_STATE_16BIT; 622215990Sjmallett memset(cvmx_nand_buffer, 0xff, 16); 623215990Sjmallett if (cvmx_nand_read_id(chip, 0x0, cvmx_ptr_to_phys(cvmx_nand_buffer), 16) < 16) 624215990Sjmallett { 625215990Sjmallett if (cvmx_unlikely(cvmx_nand_flags & CVMX_NAND_INITIALIZE_FLAGS_DEBUG)) 626215990Sjmallett cvmx_dprintf("%s: Failed to probe chip %d\n", __FUNCTION__, chip); 627215990Sjmallett probe_failed = 1; 628215990Sjmallett 629215990Sjmallett } 630215990Sjmallett if (*(uint32_t*)cvmx_nand_buffer == 0xffffffff || *(uint32_t*)cvmx_nand_buffer == 0x0) 631215990Sjmallett { 632215990Sjmallett if (cvmx_unlikely(cvmx_nand_flags & CVMX_NAND_INITIALIZE_FLAGS_DEBUG)) 633215990Sjmallett cvmx_dprintf("%s: Probe returned nothing for chip %d\n", __FUNCTION__, chip); 634215990Sjmallett probe_failed = 1; 635215990Sjmallett } 636210284Sjmallett } 637215990Sjmallett /* Neither 8 or 16 bit mode worked, so go on to next chip select */ 638215990Sjmallett if (probe_failed) 639210284Sjmallett continue; 640210284Sjmallett 641215990Sjmallett /* Save copy of ID for later use */ 642215990Sjmallett memcpy(nand_id_buffer, cvmx_nand_buffer, sizeof(nand_id_buffer)); 643215990Sjmallett 644210284Sjmallett if (cvmx_unlikely(cvmx_nand_flags & CVMX_NAND_INITIALIZE_FLAGS_DEBUG)) 645210284Sjmallett cvmx_dprintf("%s: NAND chip %d has ID 0x%08llx\n", __FUNCTION__, chip, (unsigned long long int)*(uint64_t*)cvmx_nand_buffer); 646215990Sjmallett /* Read more than required, as in 16 bit mode only every other byte is valid. */ 647215990Sjmallett if (cvmx_nand_read_id(chip, 0x20, cvmx_ptr_to_phys(cvmx_nand_buffer), 8) < 8) 648210284Sjmallett { 649210284Sjmallett if (cvmx_unlikely(cvmx_nand_flags & CVMX_NAND_INITIALIZE_FLAGS_DEBUG)) 650210284Sjmallett cvmx_dprintf("%s: Failed to probe chip %d\n", __FUNCTION__, chip); 651210284Sjmallett continue; 652210284Sjmallett } 653210284Sjmallett 654215990Sjmallett if (((cvmx_nand_buffer[0] == 'O') && (cvmx_nand_buffer[1] == 'N') && 655210284Sjmallett (cvmx_nand_buffer[2] == 'F') && (cvmx_nand_buffer[3] == 'I'))) 656210284Sjmallett { 657215990Sjmallett /* We have an ONFI part, so read the parameter page */ 658210284Sjmallett 659215990Sjmallett cvmx_nand_read_param_page(chip, cvmx_ptr_to_phys(cvmx_nand_buffer), 2048); 660215990Sjmallett onfi_param_page = __cvmx_nand_onfi_process((cvmx_nand_onfi_param_page_t *)cvmx_nand_buffer); 661215990Sjmallett if (onfi_param_page) 662215990Sjmallett { 663215990Sjmallett /* ONFI NAND parts are described by a parameter page. Here we extract the configuration values 664215990Sjmallett ** from the parameter page that we need to access the chip. */ 665215990Sjmallett cvmx_nand_state[chip].page_size = cvmx_le32_to_cpu(onfi_param_page->page_data_bytes); 666215990Sjmallett cvmx_nand_state[chip].oob_size = cvmx_le16_to_cpu(onfi_param_page->page_spare_bytes); 667215990Sjmallett cvmx_nand_state[chip].pages_per_block = cvmx_le32_to_cpu(onfi_param_page->pages_per_block); 668215990Sjmallett cvmx_nand_state[chip].blocks = cvmx_le32_to_cpu(onfi_param_page->blocks_per_lun) * onfi_param_page->number_lun; 669210284Sjmallett 670215990Sjmallett if (cvmx_le16_to_cpu(onfi_param_page->timing_mode) <= 0x3f) 671210284Sjmallett { 672215990Sjmallett int mode_mask = cvmx_le16_to_cpu(onfi_param_page->timing_mode); 673215990Sjmallett int mode = 0; 674215990Sjmallett int i; 675215990Sjmallett for (i = 0; i < 6;i++) 676215990Sjmallett { 677215990Sjmallett if (mode_mask & (1 << i)) 678215990Sjmallett mode = i; 679215990Sjmallett } 680215990Sjmallett cvmx_nand_state[chip].onfi_timing = mode; 681210284Sjmallett } 682215990Sjmallett else 683215990Sjmallett { 684215990Sjmallett cvmx_dprintf("%s: Invalid timing mode (%d) in ONFI parameter page, ignoring\n", __FUNCTION__, cvmx_nand_state[chip].onfi_timing); 685215990Sjmallett cvmx_nand_state[chip].onfi_timing = 0; 686215990Sjmallett 687215990Sjmallett } 688215990Sjmallett if (cvmx_unlikely(cvmx_nand_flags & CVMX_NAND_INITIALIZE_FLAGS_DEBUG)) 689215990Sjmallett cvmx_dprintf("%s: Using ONFI timing mode: %d\n", __FUNCTION__, cvmx_nand_state[chip].onfi_timing); 690215990Sjmallett __set_onfi_timing_mode(cvmx_nand_state[chip].tim_par, clocks_us, cvmx_nand_state[chip].onfi_timing); 691215990Sjmallett if (cvmx_nand_state[chip].page_size + cvmx_nand_state[chip].oob_size > CVMX_NAND_MAX_PAGE_AND_OOB_SIZE) 692215990Sjmallett { 693215990Sjmallett cvmx_dprintf("%s: ERROR: Page size (%d) + OOB size (%d) is greater than max size (%d)\n", 694215990Sjmallett __FUNCTION__, cvmx_nand_state[chip].page_size, cvmx_nand_state[chip].oob_size, CVMX_NAND_MAX_PAGE_AND_OOB_SIZE); 695215990Sjmallett return(CVMX_NAND_ERROR); 696215990Sjmallett } 697215990Sjmallett /* We have completed setup for this ONFI chip, so go on to next chip. */ 698215990Sjmallett continue; 699210284Sjmallett } 700210284Sjmallett else 701210284Sjmallett { 702215990Sjmallett /* Parameter page is not valid */ 703215990Sjmallett if (cvmx_unlikely(cvmx_nand_flags & CVMX_NAND_INITIALIZE_FLAGS_DEBUG)) 704215990Sjmallett cvmx_dprintf("%s: ONFI paramater page missing or invalid.\n", __FUNCTION__); 705210284Sjmallett 706210284Sjmallett } 707215990Sjmallett 708215990Sjmallett 709210284Sjmallett } 710210284Sjmallett else 711210284Sjmallett { 712215990Sjmallett /* We have a non-ONFI part. */ 713215990Sjmallett if (cvmx_unlikely(cvmx_nand_flags & CVMX_NAND_INITIALIZE_FLAGS_DEBUG)) 714215990Sjmallett cvmx_dprintf("%s: Chip %d doesn't support ONFI.\n", __FUNCTION__, chip); 715210284Sjmallett 716210284Sjmallett 717215990Sjmallett if (nand_id_buffer[0] == 0xEC) 718215990Sjmallett { 719215990Sjmallett /* We have a Samsung part, so decode part info from ID bytes */ 720215990Sjmallett uint64_t nand_size_bits = (64*1024*1024ULL) << ((nand_id_buffer[4] & 0x70) >> 4); /* Plane size */ 721215990Sjmallett cvmx_nand_state[chip].page_size = 1024 << (nand_id_buffer[3] & 0x3); /* NAND page size in bytes */ 722232812Sjmallett /* NAND OOB (spare) size in bytes (per page) */ 723232812Sjmallett cvmx_nand_state[chip].oob_size = (cvmx_nand_state[chip].page_size / 512) * ((nand_id_buffer[3] & 4) ? 16 : 8); 724215990Sjmallett cvmx_nand_state[chip].pages_per_block = (0x10000 << ((nand_id_buffer[3] & 0x30) >> 4))/cvmx_nand_state[chip].page_size; 725215990Sjmallett 726215990Sjmallett nand_size_bits *= 1 << ((nand_id_buffer[4] & 0xc) >> 2); 727215990Sjmallett 728215990Sjmallett cvmx_nand_state[chip].oob_size = cvmx_nand_state[chip].page_size/64; 729215990Sjmallett if (nand_id_buffer[3] & 0x4) 730215990Sjmallett cvmx_nand_state[chip].oob_size *= 2; 731215990Sjmallett 732215990Sjmallett cvmx_nand_state[chip].blocks = nand_size_bits/(8ULL*cvmx_nand_state[chip].page_size*cvmx_nand_state[chip].pages_per_block); 733232812Sjmallett switch (nand_id_buffer[1]) { 734232812Sjmallett case 0xD3: /* K9F8G08U0M */ 735232812Sjmallett case 0xDC: /* K9F4G08U0B */ 736232812Sjmallett cvmx_nand_state[chip].onfi_timing = 6; 737232812Sjmallett break; 738232812Sjmallett default: 739232812Sjmallett cvmx_nand_state[chip].onfi_timing = 2; 740232812Sjmallett break; 741232812Sjmallett } 742215990Sjmallett 743215990Sjmallett if (cvmx_unlikely(cvmx_nand_flags & CVMX_NAND_INITIALIZE_FLAGS_DEBUG)) 744215990Sjmallett { 745215990Sjmallett cvmx_dprintf("%s: Samsung NAND chip detected, using parameters decoded from ID bytes.\n", __FUNCTION__); 746215990Sjmallett cvmx_dprintf("%s: Defaults: page size: %d, OOB size: %d, pages per block %d, part size: %d MBytes, timing mode: %d\n", 747215990Sjmallett __FUNCTION__, cvmx_nand_state[chip].page_size, cvmx_nand_state[chip].oob_size, cvmx_nand_state[chip].pages_per_block, 748215990Sjmallett (int)(nand_size_bits/(8*1024*1024)), cvmx_nand_state[chip].onfi_timing); 749215990Sjmallett } 750215990Sjmallett 751215990Sjmallett __set_onfi_timing_mode(cvmx_nand_state[chip].tim_par, clocks_us, cvmx_nand_state[chip].onfi_timing); 752215990Sjmallett if (cvmx_nand_state[chip].page_size + cvmx_nand_state[chip].oob_size > CVMX_NAND_MAX_PAGE_AND_OOB_SIZE) 753215990Sjmallett { 754215990Sjmallett cvmx_dprintf("%s: ERROR: Page size (%d) + OOB size (%d) is greater than max size (%d)\n", 755215990Sjmallett __FUNCTION__, cvmx_nand_state[chip].page_size, cvmx_nand_state[chip].oob_size, CVMX_NAND_MAX_PAGE_AND_OOB_SIZE); 756215990Sjmallett return(CVMX_NAND_ERROR); 757215990Sjmallett } 758215990Sjmallett 759215990Sjmallett /* We have completed setup for this Samsung chip, so go on to next chip. */ 760215990Sjmallett continue; 761215990Sjmallett 762215990Sjmallett 763215990Sjmallett } 764215990Sjmallett 765210284Sjmallett } 766210284Sjmallett 767210284Sjmallett 768215990Sjmallett 769215990Sjmallett /* We were not able to automatically identify the NAND chip parameters. If default values were configured, 770215990Sjmallett ** use them. */ 771215990Sjmallett if (cvmx_nand_default.page_size) 772215990Sjmallett { 773215990Sjmallett __set_chip_defaults(chip, clocks_us); 774215990Sjmallett } 775215990Sjmallett else 776215990Sjmallett { 777215990Sjmallett 778215990Sjmallett if (cvmx_unlikely(cvmx_nand_flags & CVMX_NAND_INITIALIZE_FLAGS_DEBUG)) 779215990Sjmallett cvmx_dprintf("%s: Unable to determine NAND parameters, and no defaults supplied.\n", __FUNCTION__); 780215990Sjmallett } 781210284Sjmallett } 782210284Sjmallett CVMX_NAND_RETURN(CVMX_NAND_SUCCESS); 783210284Sjmallett} 784215990Sjmallett#ifdef CVMX_BUILD_FOR_LINUX_KERNEL 785215990SjmallettEXPORT_SYMBOL(cvmx_nand_initialize); 786215990Sjmallett#endif 787210284Sjmallett 788210284Sjmallett 789210284Sjmallett/** 790210284Sjmallett * Call to shutdown the NAND controller after all transactions 791210284Sjmallett * are done. In most setups this will never be called. 792210284Sjmallett * 793210284Sjmallett * @return Zero on success, a negative cvmx_nand_status_t error code on failure 794210284Sjmallett */ 795210284Sjmallettcvmx_nand_status_t cvmx_nand_shutdown(void) 796210284Sjmallett{ 797210284Sjmallett CVMX_NAND_LOG_CALLED(); 798210284Sjmallett memset(&cvmx_nand_state, 0, sizeof(cvmx_nand_state)); 799210284Sjmallett CVMX_NAND_RETURN(CVMX_NAND_SUCCESS); 800210284Sjmallett} 801210284Sjmallett 802210284Sjmallett 803210284Sjmallett/** 804210284Sjmallett * Returns a bitmask representing the chip selects that are 805210284Sjmallett * connected to NAND chips. This can be called after the 806210284Sjmallett * initialize to determine the actual number of NAND chips 807210284Sjmallett * found. Each bit in the response coresponds to a chip select. 808210284Sjmallett * 809210284Sjmallett * @return Zero if no NAND chips were found. Otherwise a bit is set for 810210284Sjmallett * each chip select (1<<chip). 811210284Sjmallett */ 812210284Sjmallettint cvmx_nand_get_active_chips(void) 813210284Sjmallett{ 814210284Sjmallett int chip; 815210284Sjmallett int result = 0; 816210284Sjmallett for (chip=0; chip<8; chip++) 817210284Sjmallett { 818210284Sjmallett if (cvmx_nand_state[chip].page_size) 819210284Sjmallett result |= 1<<chip; 820210284Sjmallett } 821210284Sjmallett return result; 822210284Sjmallett} 823215990Sjmallett#ifdef CVMX_BUILD_FOR_LINUX_KERNEL 824215990SjmallettEXPORT_SYMBOL(cvmx_nand_get_active_chips); 825215990Sjmallett#endif 826210284Sjmallett 827210284Sjmallett 828210284Sjmallett/** 829210284Sjmallett * Override the timing parameters for a NAND chip 830210284Sjmallett * 831210284Sjmallett * @param chip Chip select to override 832210284Sjmallett * @param tim_mult 833210284Sjmallett * @param tim_par 834210284Sjmallett * @param clen 835210284Sjmallett * @param alen 836210284Sjmallett * @param rdn 837210284Sjmallett * @param wrn 838210284Sjmallett * 839210284Sjmallett * @return Zero on success, a negative cvmx_nand_status_t error code on failure 840210284Sjmallett */ 841210284Sjmallettcvmx_nand_status_t cvmx_nand_set_timing(int chip, int tim_mult, int tim_par[8], int clen[4], int alen[4], int rdn[4], int wrn[2]) 842210284Sjmallett{ 843210284Sjmallett int i; 844210284Sjmallett CVMX_NAND_LOG_CALLED(); 845210284Sjmallett 846210284Sjmallett if ((chip < 0) || (chip > 7)) 847210284Sjmallett CVMX_NAND_RETURN(CVMX_NAND_INVALID_PARAM); 848210284Sjmallett if (!cvmx_nand_state[chip].page_size) 849210284Sjmallett CVMX_NAND_RETURN(CVMX_NAND_INVALID_PARAM); 850210284Sjmallett 851210284Sjmallett cvmx_nand_state[chip].tim_mult = tim_mult; 852210284Sjmallett for (i=0;i<8;i++) 853210284Sjmallett cvmx_nand_state[chip].tim_par[i] = tim_par[i]; 854210284Sjmallett for (i=0;i<4;i++) 855210284Sjmallett cvmx_nand_state[chip].clen[i] = clen[i]; 856210284Sjmallett for (i=0;i<4;i++) 857210284Sjmallett cvmx_nand_state[chip].alen[i] = alen[i]; 858210284Sjmallett for (i=0;i<4;i++) 859210284Sjmallett cvmx_nand_state[chip].rdn[i] = rdn[i]; 860210284Sjmallett for (i=0;i<2;i++) 861210284Sjmallett cvmx_nand_state[chip].wrn[i] = wrn[i]; 862210284Sjmallett 863210284Sjmallett CVMX_NAND_RETURN(CVMX_NAND_SUCCESS); 864210284Sjmallett} 865210284Sjmallett 866210284Sjmallett 867210284Sjmallett/** 868210284Sjmallett * @INTERNAL 869210284Sjmallett * Get the number of free bytes in the NAND command queue 870210284Sjmallett * 871210284Sjmallett * @return Number of bytes in queue 872210284Sjmallett */ 873210284Sjmallettstatic inline int __cvmx_nand_get_free_cmd_bytes(void) 874210284Sjmallett{ 875215990Sjmallett union cvmx_ndf_misc ndf_misc; 876210284Sjmallett CVMX_NAND_LOG_CALLED(); 877210284Sjmallett ndf_misc.u64 = cvmx_read_csr(CVMX_NDF_MISC); 878210284Sjmallett CVMX_NAND_RETURN((int)ndf_misc.s.fr_byt); 879210284Sjmallett} 880210284Sjmallett 881210284Sjmallett 882210284Sjmallett/** 883210284Sjmallett * Submit a command to the NAND command queue. Generally this 884210284Sjmallett * will not be used directly. Instead most programs will use the other 885210284Sjmallett * higher level NAND functions. 886210284Sjmallett * 887210284Sjmallett * @param cmd Command to submit 888210284Sjmallett * 889210284Sjmallett * @return Zero on success, a negative cvmx_nand_status_t error code on failure 890210284Sjmallett */ 891210284Sjmallettcvmx_nand_status_t cvmx_nand_submit(cvmx_nand_cmd_t cmd) 892210284Sjmallett{ 893210284Sjmallett CVMX_NAND_LOG_CALLED(); 894210284Sjmallett CVMX_NAND_LOG_PARAM("0x%llx", (ULL)cmd.u64[0]); 895210284Sjmallett CVMX_NAND_LOG_PARAM("0x%llx", (ULL)cmd.u64[1]); 896210284Sjmallett CVMX_NAND_LOG_PARAM("%s", cvmx_nand_opcode_labels[cmd.s.op_code]); 897210284Sjmallett switch (cmd.s.op_code) 898210284Sjmallett { 899210284Sjmallett /* All these commands fit in one 64bit word */ 900210284Sjmallett case 0: /* NOP */ 901210284Sjmallett case 1: /* Timing */ 902210284Sjmallett case 2: /* WAIT */ 903210284Sjmallett case 3: /* Chip Enable/Disable */ 904210284Sjmallett case 4: /* CLE */ 905210284Sjmallett case 8: /* Write */ 906210284Sjmallett case 9: /* Read */ 907210284Sjmallett case 10: /* Read EDO */ 908210284Sjmallett case 15: /* Bus Aquire/Release */ 909210284Sjmallett if (__cvmx_nand_get_free_cmd_bytes() < 8) 910210284Sjmallett CVMX_NAND_RETURN(CVMX_NAND_NO_MEMORY); 911210284Sjmallett cvmx_write_csr(CVMX_NDF_CMD, cmd.u64[1]); 912210284Sjmallett CVMX_NAND_RETURN(CVMX_NAND_SUCCESS); 913210284Sjmallett 914210284Sjmallett case 5: /* ALE commands take either one or two 64bit words */ 915210284Sjmallett if (cmd.ale.adr_byte_num < 5) 916210284Sjmallett { 917210284Sjmallett if (__cvmx_nand_get_free_cmd_bytes() < 8) 918210284Sjmallett CVMX_NAND_RETURN(CVMX_NAND_NO_MEMORY); 919210284Sjmallett cvmx_write_csr(CVMX_NDF_CMD, cmd.u64[1]); 920210284Sjmallett CVMX_NAND_RETURN(CVMX_NAND_SUCCESS); 921210284Sjmallett } 922210284Sjmallett else 923210284Sjmallett { 924210284Sjmallett if (__cvmx_nand_get_free_cmd_bytes() < 16) 925210284Sjmallett CVMX_NAND_RETURN(CVMX_NAND_NO_MEMORY); 926210284Sjmallett cvmx_write_csr(CVMX_NDF_CMD, cmd.u64[1]); 927210284Sjmallett cvmx_write_csr(CVMX_NDF_CMD, cmd.u64[0]); 928210284Sjmallett CVMX_NAND_RETURN(CVMX_NAND_SUCCESS); 929210284Sjmallett } 930210284Sjmallett 931210284Sjmallett case 11: /* Wait status commands take two 64bit words */ 932210284Sjmallett if (__cvmx_nand_get_free_cmd_bytes() < 16) 933210284Sjmallett CVMX_NAND_RETURN(CVMX_NAND_NO_MEMORY); 934210284Sjmallett cvmx_write_csr(CVMX_NDF_CMD, cmd.u64[1]); 935210284Sjmallett cvmx_write_csr(CVMX_NDF_CMD, cmd.u64[0]); 936210284Sjmallett CVMX_NAND_RETURN(CVMX_NAND_SUCCESS); 937210284Sjmallett 938210284Sjmallett default: 939210284Sjmallett CVMX_NAND_RETURN(CVMX_NAND_INVALID_PARAM); 940210284Sjmallett } 941210284Sjmallett} 942210284Sjmallett 943210284Sjmallett 944210284Sjmallett/** 945210284Sjmallett * @INTERNAL 946210284Sjmallett * Get the number of bits required to encode the column bits. This 947210284Sjmallett * does not include padding to align on a byte boundary. 948210284Sjmallett * 949210284Sjmallett * @param chip NAND chip to get data for 950210284Sjmallett * 951210284Sjmallett * @return Number of column bits 952210284Sjmallett */ 953210284Sjmallettstatic inline int __cvmx_nand_get_column_bits(int chip) 954210284Sjmallett{ 955210284Sjmallett return cvmx_pop(cvmx_nand_state[chip].page_size - 1); 956210284Sjmallett} 957210284Sjmallett 958210284Sjmallett 959210284Sjmallett/** 960210284Sjmallett * @INTERNAL 961210284Sjmallett * Get the number of bits required to encode the row bits. This 962210284Sjmallett * does not include padding to align on a byte boundary. 963210284Sjmallett * 964210284Sjmallett * @param chip NAND chip to get data for 965210284Sjmallett * 966210284Sjmallett * @return Number of row bits 967210284Sjmallett */ 968210284Sjmallettstatic inline int __cvmx_nand_get_row_bits(int chip) 969210284Sjmallett{ 970210284Sjmallett return cvmx_pop(cvmx_nand_state[chip].blocks-1) + cvmx_pop(cvmx_nand_state[chip].pages_per_block-1); 971210284Sjmallett} 972210284Sjmallett 973210284Sjmallett 974210284Sjmallett/** 975210284Sjmallett * @INTERNAL 976210284Sjmallett * Get the number of address cycles required for this NAND part. 977210284Sjmallett * This include column bits, padding, page bits, and block bits. 978210284Sjmallett * 979210284Sjmallett * @param chip NAND chip to get data for 980210284Sjmallett * 981210284Sjmallett * @return Number of address cycles on the bus 982210284Sjmallett */ 983210284Sjmallettstatic inline int __cvmx_nand_get_address_cycles(int chip) 984210284Sjmallett{ 985210284Sjmallett int address_bits = ((__cvmx_nand_get_column_bits(chip) + 7) >> 3) << 3; 986210284Sjmallett address_bits += ((__cvmx_nand_get_row_bits(chip) + 7) >> 3) << 3; 987210284Sjmallett return (address_bits + 7) >> 3; 988210284Sjmallett} 989210284Sjmallett 990210284Sjmallett 991210284Sjmallett/** 992210284Sjmallett * @INTERNAL 993210284Sjmallett * Build the set of command common to most transactions 994210284Sjmallett * @param chip NAND chip to program 995232812Sjmallett * @param cmd_data NAND command for CLE cycle 1 996210284Sjmallett * @param num_address_cycles 997210284Sjmallett * Number of address cycles to put on the bus 998210284Sjmallett * @param nand_address 999210284Sjmallett * Data to be put on the bus. It is translated according to 1000210284Sjmallett * the rules in the file information section. 1001210284Sjmallett * 1002210284Sjmallett * @param cmd_data2 If non zero, adds a second CLE cycle used by a number of NAND 1003210284Sjmallett * transactions. 1004210284Sjmallett * 1005210284Sjmallett * @return Zero on success, a negative cvmx_nand_status_t error code on failure 1006210284Sjmallett */ 1007210284Sjmallettstatic inline cvmx_nand_status_t __cvmx_nand_build_pre_cmd(int chip, int cmd_data, int num_address_cycles, uint64_t nand_address, int cmd_data2) 1008210284Sjmallett{ 1009210284Sjmallett cvmx_nand_status_t result; 1010210284Sjmallett cvmx_nand_cmd_t cmd; 1011210284Sjmallett 1012210284Sjmallett CVMX_NAND_LOG_CALLED(); 1013210284Sjmallett 1014210284Sjmallett /* Send timing parameters */ 1015210284Sjmallett memset(&cmd, 0, sizeof(cmd)); 1016210284Sjmallett cmd.set_tm_par.one = 1; 1017210284Sjmallett cmd.set_tm_par.tim_mult = cvmx_nand_state[chip].tim_mult; 1018210284Sjmallett /* tim_par[0] unused */ 1019210284Sjmallett cmd.set_tm_par.tim_par1 = cvmx_nand_state[chip].tim_par[1]; 1020210284Sjmallett cmd.set_tm_par.tim_par2 = cvmx_nand_state[chip].tim_par[2]; 1021210284Sjmallett cmd.set_tm_par.tim_par3 = cvmx_nand_state[chip].tim_par[3]; 1022210284Sjmallett cmd.set_tm_par.tim_par4 = cvmx_nand_state[chip].tim_par[4]; 1023210284Sjmallett cmd.set_tm_par.tim_par5 = cvmx_nand_state[chip].tim_par[5]; 1024210284Sjmallett cmd.set_tm_par.tim_par6 = cvmx_nand_state[chip].tim_par[6]; 1025210284Sjmallett cmd.set_tm_par.tim_par7 = cvmx_nand_state[chip].tim_par[7]; 1026210284Sjmallett result = cvmx_nand_submit(cmd); 1027210284Sjmallett if (result) 1028210284Sjmallett CVMX_NAND_RETURN(result); 1029210284Sjmallett 1030210284Sjmallett /* Send bus select */ 1031210284Sjmallett memset(&cmd, 0, sizeof(cmd)); 1032210284Sjmallett cmd.bus_acq.fifteen = 15; 1033210284Sjmallett cmd.bus_acq.one = 1; 1034210284Sjmallett result = cvmx_nand_submit(cmd); 1035210284Sjmallett if (result) 1036210284Sjmallett CVMX_NAND_RETURN(result); 1037210284Sjmallett 1038210284Sjmallett /* Send chip select */ 1039210284Sjmallett memset(&cmd, 0, sizeof(cmd)); 1040210284Sjmallett cmd.chip_en.chip = chip; 1041210284Sjmallett cmd.chip_en.one = 1; 1042210284Sjmallett cmd.chip_en.three = 3; 1043215990Sjmallett cmd.chip_en.width = (cvmx_nand_state[chip].flags & CVMX_NAND_STATE_16BIT) ? 2 : 1; 1044210284Sjmallett result = cvmx_nand_submit(cmd); 1045210284Sjmallett if (result) 1046210284Sjmallett CVMX_NAND_RETURN(result); 1047210284Sjmallett 1048210284Sjmallett /* Send wait, fixed time 1049210284Sjmallett ** This meets chip enable to command latch enable timing. 1050210284Sjmallett ** This is tCS - tCLS from the ONFI spec. 1051210284Sjmallett ** Use tWP as a proxy, as this is adequate for 1052210284Sjmallett ** all ONFI 1.0 timing modes. */ 1053210284Sjmallett memset(&cmd, 0, sizeof(cmd)); 1054210284Sjmallett cmd.wait.two = 2; 1055210284Sjmallett cmd.wait.n = 1; 1056210284Sjmallett if (cvmx_nand_submit(cmd)) 1057210284Sjmallett CVMX_NAND_RETURN(CVMX_NAND_NO_MEMORY); 1058210284Sjmallett 1059210284Sjmallett /* Send CLE */ 1060210284Sjmallett memset(&cmd, 0, sizeof(cmd)); 1061210284Sjmallett cmd.cle.cmd_data = cmd_data; 1062210284Sjmallett cmd.cle.clen1 = cvmx_nand_state[chip].clen[0]; 1063210284Sjmallett cmd.cle.clen2 = cvmx_nand_state[chip].clen[1]; 1064210284Sjmallett cmd.cle.clen3 = cvmx_nand_state[chip].clen[2]; 1065210284Sjmallett cmd.cle.four = 4; 1066210284Sjmallett result = cvmx_nand_submit(cmd); 1067210284Sjmallett if (result) 1068210284Sjmallett CVMX_NAND_RETURN(result); 1069210284Sjmallett 1070210284Sjmallett /* Send ALE */ 1071210284Sjmallett if (num_address_cycles) 1072210284Sjmallett { 1073210284Sjmallett memset(&cmd, 0, sizeof(cmd)); 1074210284Sjmallett cmd.ale.adr_byte_num = num_address_cycles; 1075210284Sjmallett if (num_address_cycles < __cvmx_nand_get_address_cycles(chip)) 1076210284Sjmallett { 1077210284Sjmallett cmd.ale.adr_bytes_l = nand_address; 1078210284Sjmallett cmd.ale.adr_bytes_h = nand_address >> 32; 1079210284Sjmallett } 1080210284Sjmallett else 1081210284Sjmallett { 1082210284Sjmallett int column_bits = __cvmx_nand_get_column_bits(chip); 1083210284Sjmallett int column_shift = ((column_bits + 7) >> 3) << 3; 1084210284Sjmallett int column = nand_address & (cvmx_nand_state[chip].page_size-1); 1085210284Sjmallett int row = nand_address >> column_bits; 1086210284Sjmallett cmd.ale.adr_bytes_l = column + (row << column_shift); 1087210284Sjmallett cmd.ale.adr_bytes_h = row >> (32 - column_shift); 1088210284Sjmallett } 1089210284Sjmallett cmd.ale.alen1 = cvmx_nand_state[chip].alen[0]; 1090210284Sjmallett cmd.ale.alen2 = cvmx_nand_state[chip].alen[1]; 1091210284Sjmallett cmd.ale.alen3 = cvmx_nand_state[chip].alen[2]; 1092210284Sjmallett cmd.ale.alen4 = cvmx_nand_state[chip].alen[3]; 1093210284Sjmallett cmd.ale.five = 5; 1094210284Sjmallett result = cvmx_nand_submit(cmd); 1095210284Sjmallett if (result) 1096210284Sjmallett CVMX_NAND_RETURN(result); 1097210284Sjmallett } 1098210284Sjmallett 1099210284Sjmallett /* Send CLE 2 */ 1100210284Sjmallett if (cmd_data2) 1101210284Sjmallett { 1102210284Sjmallett memset(&cmd, 0, sizeof(cmd)); 1103210284Sjmallett cmd.cle.cmd_data = cmd_data2; 1104210284Sjmallett cmd.cle.clen1 = cvmx_nand_state[chip].clen[0]; 1105210284Sjmallett cmd.cle.clen2 = cvmx_nand_state[chip].clen[1]; 1106210284Sjmallett cmd.cle.clen3 = cvmx_nand_state[chip].clen[2]; 1107210284Sjmallett cmd.cle.four = 4; 1108210284Sjmallett result = cvmx_nand_submit(cmd); 1109210284Sjmallett if (result) 1110210284Sjmallett CVMX_NAND_RETURN(result); 1111210284Sjmallett } 1112210284Sjmallett 1113210284Sjmallett CVMX_NAND_RETURN(CVMX_NAND_SUCCESS); 1114210284Sjmallett} 1115210284Sjmallett 1116210284Sjmallett 1117210284Sjmallett/** 1118210284Sjmallett * @INTERNAL 1119210284Sjmallett * Build the set of command common to most transactions 1120210284Sjmallett * @return Zero on success, a negative cvmx_nand_status_t error code on failure 1121210284Sjmallett */ 1122210284Sjmallettstatic inline cvmx_nand_status_t __cvmx_nand_build_post_cmd(void) 1123210284Sjmallett{ 1124210284Sjmallett cvmx_nand_status_t result; 1125210284Sjmallett cvmx_nand_cmd_t cmd; 1126210284Sjmallett 1127210284Sjmallett CVMX_NAND_LOG_CALLED(); 1128210284Sjmallett 1129210284Sjmallett /* Send chip deselect */ 1130210284Sjmallett memset(&cmd, 0, sizeof(cmd)); 1131210284Sjmallett cmd.chip_dis.three = 3; 1132210284Sjmallett result = cvmx_nand_submit(cmd); 1133210284Sjmallett if (result) 1134210284Sjmallett CVMX_NAND_RETURN(result); 1135210284Sjmallett 1136210284Sjmallett /* Send bus release */ 1137210284Sjmallett memset(&cmd, 0, sizeof(cmd)); 1138210284Sjmallett cmd.bus_rel.fifteen = 15; 1139210284Sjmallett result = cvmx_nand_submit(cmd); 1140210284Sjmallett if (result) 1141210284Sjmallett CVMX_NAND_RETURN(result); 1142210284Sjmallett 1143210284Sjmallett /* Ring the doorbell */ 1144210284Sjmallett cvmx_write_csr(CVMX_NDF_DRBELL, 1); 1145210284Sjmallett CVMX_NAND_RETURN(CVMX_NAND_SUCCESS); 1146210284Sjmallett} 1147210284Sjmallett 1148210284Sjmallett 1149210284Sjmallett/** 1150210284Sjmallett * @INTERNAL 1151210284Sjmallett * Setup the NAND DMA engine for a transfer 1152210284Sjmallett * 1153210284Sjmallett * @param chip Chip select for NAND flash 1154210284Sjmallett * @param is_write Non zero if this is a write 1155210284Sjmallett * @param buffer_address 1156210284Sjmallett * Physical memory address to DMA to/from 1157210284Sjmallett * @param buffer_length 1158210284Sjmallett * Length of the DMA in bytes 1159210284Sjmallett */ 1160210284Sjmallettstatic inline void __cvmx_nand_setup_dma(int chip, int is_write, uint64_t buffer_address, int buffer_length) 1161210284Sjmallett{ 1162215990Sjmallett union cvmx_mio_ndf_dma_cfg ndf_dma_cfg; 1163210284Sjmallett CVMX_NAND_LOG_CALLED(); 1164210284Sjmallett CVMX_NAND_LOG_PARAM("%d", chip); 1165210284Sjmallett CVMX_NAND_LOG_PARAM("%d", is_write); 1166210284Sjmallett CVMX_NAND_LOG_PARAM("0x%llx", (ULL)buffer_address); 1167210284Sjmallett CVMX_NAND_LOG_PARAM("%d", buffer_length); 1168210284Sjmallett ndf_dma_cfg.u64 = 0; 1169210284Sjmallett ndf_dma_cfg.s.en = 1; 1170210284Sjmallett ndf_dma_cfg.s.rw = is_write; /* One means DMA reads from memory and writes to flash */ 1171210284Sjmallett ndf_dma_cfg.s.clr = 0; 1172210284Sjmallett ndf_dma_cfg.s.size = ((buffer_length + 7) >> 3) - 1; 1173210284Sjmallett ndf_dma_cfg.s.adr = buffer_address; 1174210284Sjmallett CVMX_SYNCWS; 1175210284Sjmallett cvmx_write_csr(CVMX_MIO_NDF_DMA_CFG, ndf_dma_cfg.u64); 1176210284Sjmallett CVMX_NAND_RETURN_NOTHING(); 1177210284Sjmallett} 1178210284Sjmallett 1179210284Sjmallett 1180210284Sjmallett/** 1181210284Sjmallett * Dump a buffer out in hex for debug 1182210284Sjmallett * 1183210284Sjmallett * @param buffer_address 1184210284Sjmallett * Starting physical address 1185210284Sjmallett * @param buffer_length 1186210284Sjmallett * Number of bytes to display 1187210284Sjmallett */ 1188210284Sjmallettstatic void __cvmx_nand_hex_dump(uint64_t buffer_address, int buffer_length) 1189210284Sjmallett{ 1190210284Sjmallett uint8_t *buffer = cvmx_phys_to_ptr(buffer_address); 1191210284Sjmallett int offset = 0; 1192210284Sjmallett while (offset < buffer_length) 1193210284Sjmallett { 1194210284Sjmallett int i; 1195210284Sjmallett cvmx_dprintf("%*s%04x:", 2*debug_indent, "", offset); 1196210284Sjmallett for (i=0; i<32; i++) 1197210284Sjmallett { 1198210284Sjmallett if ((i&3) == 0) 1199210284Sjmallett cvmx_dprintf(" "); 1200210284Sjmallett if (offset+i < buffer_length) 1201210284Sjmallett cvmx_dprintf("%02x", 0xff & buffer[offset+i]); 1202210284Sjmallett else 1203210284Sjmallett cvmx_dprintf(" "); 1204210284Sjmallett } 1205210284Sjmallett cvmx_dprintf("\n"); 1206210284Sjmallett offset += 32; 1207210284Sjmallett } 1208210284Sjmallett} 1209210284Sjmallett 1210210284Sjmallett/** 1211210284Sjmallett * @INTERNAL 1212210284Sjmallett * Perform a low level NAND read command 1213210284Sjmallett * 1214210284Sjmallett * @param chip Chip to read from 1215210284Sjmallett * @param nand_command1 1216210284Sjmallett * First command cycle value 1217210284Sjmallett * @param address_cycles 1218210284Sjmallett * Number of address cycles after comand 1 1219210284Sjmallett * @param nand_address 1220210284Sjmallett * NAND address to use for address cycles 1221210284Sjmallett * @param nand_command2 1222232812Sjmallett * NAND command cycle 2 if not zero 1223210284Sjmallett * @param buffer_address 1224210284Sjmallett * Physical address to DMA into 1225210284Sjmallett * @param buffer_length 1226210284Sjmallett * Length of the transfer in bytes 1227210284Sjmallett * 1228210284Sjmallett * @return Number of bytes transfered or a negative error code 1229210284Sjmallett */ 1230210284Sjmallettstatic inline int __cvmx_nand_low_level_read(int chip, int nand_command1, int address_cycles, uint64_t nand_address, int nand_command2, uint64_t buffer_address, int buffer_length) 1231210284Sjmallett{ 1232210284Sjmallett cvmx_nand_cmd_t cmd; 1233215990Sjmallett union cvmx_mio_ndf_dma_cfg ndf_dma_cfg; 1234210284Sjmallett int bytes; 1235210284Sjmallett 1236210284Sjmallett CVMX_NAND_LOG_CALLED(); 1237210284Sjmallett CVMX_NAND_LOG_PARAM("%d", chip); 1238210284Sjmallett CVMX_NAND_LOG_PARAM("0x%x", nand_command1); 1239210284Sjmallett CVMX_NAND_LOG_PARAM("%d", address_cycles); 1240210284Sjmallett CVMX_NAND_LOG_PARAM("0x%llx", (ULL)nand_address); 1241210284Sjmallett CVMX_NAND_LOG_PARAM("0x%x", nand_command2); 1242210284Sjmallett CVMX_NAND_LOG_PARAM("0x%llx", (ULL)buffer_address); 1243210284Sjmallett CVMX_NAND_LOG_PARAM("%d", buffer_length); 1244210284Sjmallett 1245210284Sjmallett if ((chip < 0) || (chip > 7)) 1246210284Sjmallett CVMX_NAND_RETURN(CVMX_NAND_INVALID_PARAM); 1247210284Sjmallett if (!buffer_address) 1248210284Sjmallett CVMX_NAND_RETURN(CVMX_NAND_INVALID_PARAM); 1249210284Sjmallett if (buffer_address & 7) 1250210284Sjmallett CVMX_NAND_RETURN(CVMX_NAND_INVALID_PARAM); 1251215990Sjmallett if (buffer_length & 7) 1252215990Sjmallett CVMX_NAND_RETURN(CVMX_NAND_INVALID_PARAM); 1253210284Sjmallett if (!buffer_length) 1254210284Sjmallett CVMX_NAND_RETURN(CVMX_NAND_INVALID_PARAM); 1255210284Sjmallett 1256210284Sjmallett /* Build the command and address cycles */ 1257210284Sjmallett if (__cvmx_nand_build_pre_cmd(chip, nand_command1, address_cycles, nand_address, nand_command2)) 1258210284Sjmallett CVMX_NAND_RETURN(CVMX_NAND_NO_MEMORY); 1259210284Sjmallett 1260210284Sjmallett /* Send WAIT. This waits for some time, then 1261210284Sjmallett ** waits for busy to be de-asserted. */ 1262215990Sjmallett if (__wait_for_busy_done(chip)) 1263210284Sjmallett CVMX_NAND_RETURN(CVMX_NAND_NO_MEMORY); 1264210284Sjmallett 1265210284Sjmallett /* Wait for tRR after busy de-asserts. 1266210284Sjmallett ** Use 2* tALS as proxy. This is overkill in 1267210284Sjmallett ** the slow modes, but not bad in the faster ones. */ 1268215990Sjmallett memset(&cmd, 0, sizeof(cmd)); 1269215990Sjmallett cmd.wait.two = 2; 1270210284Sjmallett cmd.wait.n=4; 1271210284Sjmallett if (cvmx_nand_submit(cmd)) 1272210284Sjmallett CVMX_NAND_RETURN(CVMX_NAND_NO_MEMORY); 1273210284Sjmallett if (cvmx_nand_submit(cmd)) 1274210284Sjmallett CVMX_NAND_RETURN(CVMX_NAND_NO_MEMORY); 1275210284Sjmallett 1276210284Sjmallett /* Send READ */ 1277210284Sjmallett memset(&cmd, 0, sizeof(cmd)); 1278210284Sjmallett cmd.rd.data_bytes = buffer_length; 1279210284Sjmallett if (cvmx_nand_state[chip].onfi_timing >= 4) 1280210284Sjmallett cmd.rd.nine = 10; /* READ_EDO command is required for ONFI timing modes 4 and 5 */ 1281210284Sjmallett else 1282210284Sjmallett cmd.rd.nine = 9; 1283210284Sjmallett cmd.rd.rdn1 = cvmx_nand_state[chip].rdn[0]; 1284210284Sjmallett cmd.rd.rdn2 = cvmx_nand_state[chip].rdn[1]; 1285210284Sjmallett cmd.rd.rdn3 = cvmx_nand_state[chip].rdn[2]; 1286210284Sjmallett cmd.rd.rdn4 = cvmx_nand_state[chip].rdn[3]; 1287210284Sjmallett if (cvmx_nand_submit(cmd)) 1288210284Sjmallett CVMX_NAND_RETURN(CVMX_NAND_NO_MEMORY); 1289210284Sjmallett 1290210284Sjmallett __cvmx_nand_setup_dma(chip, 0, buffer_address, buffer_length); 1291210284Sjmallett 1292210284Sjmallett if (__cvmx_nand_build_post_cmd()) 1293210284Sjmallett CVMX_NAND_RETURN(CVMX_NAND_NO_MEMORY); 1294232812Sjmallett WATCHDOG_RESET(); 1295210284Sjmallett /* Wait for the DMA to complete */ 1296232812Sjmallett if (CVMX_WAIT_FOR_FIELD64(CVMX_MIO_NDF_DMA_CFG, cvmx_mio_ndf_dma_cfg_t, en, ==, 0, NAND_TIMEOUT_USECS_READ)) 1297232812Sjmallett { 1298232812Sjmallett WATCHDOG_RESET(); 1299210284Sjmallett CVMX_NAND_RETURN(CVMX_NAND_TIMEOUT); 1300232812Sjmallett } 1301210284Sjmallett /* Return the number of bytes transfered */ 1302210284Sjmallett ndf_dma_cfg.u64 = cvmx_read_csr(CVMX_MIO_NDF_DMA_CFG); 1303210284Sjmallett bytes = ndf_dma_cfg.s.adr - buffer_address; 1304210284Sjmallett 1305210284Sjmallett if (cvmx_unlikely(cvmx_nand_flags & CVMX_NAND_INITIALIZE_FLAGS_DEBUG)) 1306210284Sjmallett __cvmx_nand_hex_dump(buffer_address, bytes); 1307210284Sjmallett 1308210284Sjmallett CVMX_NAND_RETURN(bytes); 1309210284Sjmallett} 1310210284Sjmallett 1311210284Sjmallett 1312210284Sjmallett/** 1313210284Sjmallett * Read a page from NAND. If the buffer has room, the out of band 1314210284Sjmallett * data will be included. 1315210284Sjmallett * 1316210284Sjmallett * @param chip Chip select for NAND flash 1317210284Sjmallett * @param nand_address 1318210284Sjmallett * Location in NAND to read. See description in file comment 1319210284Sjmallett * @param buffer_address 1320210284Sjmallett * Physical address to store the result at 1321210284Sjmallett * @param buffer_length 1322210284Sjmallett * Number of bytes to read 1323210284Sjmallett * 1324210284Sjmallett * @return Bytes read on success, a negative cvmx_nand_status_t error code on failure 1325210284Sjmallett */ 1326210284Sjmallettint cvmx_nand_page_read(int chip, uint64_t nand_address, uint64_t buffer_address, int buffer_length) 1327210284Sjmallett{ 1328210284Sjmallett int bytes; 1329210284Sjmallett 1330210284Sjmallett CVMX_NAND_LOG_CALLED(); 1331210284Sjmallett CVMX_NAND_LOG_PARAM("%d", chip); 1332210284Sjmallett CVMX_NAND_LOG_PARAM("0x%llx", (ULL)nand_address); 1333210284Sjmallett CVMX_NAND_LOG_PARAM("0x%llx", (ULL)buffer_address); 1334210284Sjmallett CVMX_NAND_LOG_PARAM("%d", buffer_length); 1335210284Sjmallett 1336210284Sjmallett if ((chip < 0) || (chip > 7)) 1337210284Sjmallett CVMX_NAND_RETURN(CVMX_NAND_INVALID_PARAM); 1338210284Sjmallett if (!cvmx_nand_state[chip].page_size) 1339210284Sjmallett CVMX_NAND_RETURN(CVMX_NAND_INVALID_PARAM); 1340210284Sjmallett if (!buffer_address) 1341210284Sjmallett CVMX_NAND_RETURN(CVMX_NAND_INVALID_PARAM); 1342210284Sjmallett if (buffer_address & 7) 1343210284Sjmallett CVMX_NAND_RETURN(CVMX_NAND_INVALID_PARAM); 1344215990Sjmallett if (buffer_length & 7) 1345215990Sjmallett CVMX_NAND_RETURN(CVMX_NAND_INVALID_PARAM); 1346210284Sjmallett if (!buffer_length) 1347210284Sjmallett CVMX_NAND_RETURN(CVMX_NAND_INVALID_PARAM); 1348210284Sjmallett 1349215990Sjmallett /* For 16 bit mode, addresses within a page are word address, rather than byte addresses */ 1350215990Sjmallett if (cvmx_nand_state[chip].flags & CVMX_NAND_STATE_16BIT) 1351215990Sjmallett nand_address = (nand_address & ~(cvmx_nand_state[chip].page_size - 1)) | ((nand_address & (cvmx_nand_state[chip].page_size - 1)) >> 1); 1352215990Sjmallett 1353210284Sjmallett bytes = __cvmx_nand_low_level_read(chip, NAND_COMMAND_READ, __cvmx_nand_get_address_cycles(chip), nand_address, NAND_COMMAND_READ_FIN, buffer_address, buffer_length); 1354210284Sjmallett CVMX_NAND_RETURN(bytes); 1355210284Sjmallett} 1356215990Sjmallett#ifdef CVMX_BUILD_FOR_LINUX_KERNEL 1357215990SjmallettEXPORT_SYMBOL(cvmx_nand_page_read); 1358215990Sjmallett#endif 1359210284Sjmallett 1360210284Sjmallett 1361210284Sjmallett/** 1362210284Sjmallett * Write a page to NAND. The buffer must contain the entire page 1363210284Sjmallett * including the out of band data. 1364210284Sjmallett * 1365210284Sjmallett * @param chip Chip select for NAND flash 1366210284Sjmallett * @param nand_address 1367210284Sjmallett * Location in NAND to write. See description in file comment 1368210284Sjmallett * @param buffer_address 1369210284Sjmallett * Physical address to read the data from 1370210284Sjmallett * 1371210284Sjmallett * @return Zero on success, a negative cvmx_nand_status_t error code on failure 1372210284Sjmallett */ 1373210284Sjmallettcvmx_nand_status_t cvmx_nand_page_write(int chip, uint64_t nand_address, uint64_t buffer_address) 1374210284Sjmallett{ 1375210284Sjmallett cvmx_nand_cmd_t cmd; 1376210284Sjmallett int buffer_length; 1377210284Sjmallett 1378210284Sjmallett CVMX_NAND_LOG_CALLED(); 1379210284Sjmallett CVMX_NAND_LOG_PARAM("%d", chip); 1380210284Sjmallett CVMX_NAND_LOG_PARAM("0x%llx", (ULL)nand_address); 1381210284Sjmallett CVMX_NAND_LOG_PARAM("0x%llx", (ULL)buffer_address); 1382210284Sjmallett 1383210284Sjmallett if ((chip < 0) || (chip > 7)) 1384210284Sjmallett CVMX_NAND_RETURN(CVMX_NAND_INVALID_PARAM); 1385210284Sjmallett if (!cvmx_nand_state[chip].page_size) 1386210284Sjmallett CVMX_NAND_RETURN(CVMX_NAND_INVALID_PARAM); 1387210284Sjmallett if (!buffer_address) 1388210284Sjmallett CVMX_NAND_RETURN(CVMX_NAND_INVALID_PARAM); 1389210284Sjmallett if (buffer_address & 7) 1390210284Sjmallett CVMX_NAND_RETURN(CVMX_NAND_INVALID_PARAM); 1391210284Sjmallett 1392215990Sjmallett /* For 16 bit mode, addresses within a page are word address, rather than byte addresses */ 1393215990Sjmallett if (cvmx_nand_state[chip].flags & CVMX_NAND_STATE_16BIT) 1394215990Sjmallett nand_address = (nand_address & ~(cvmx_nand_state[chip].page_size - 1)) | ((nand_address & (cvmx_nand_state[chip].page_size - 1)) >> 1); 1395215990Sjmallett 1396210284Sjmallett buffer_length = cvmx_nand_state[chip].page_size + cvmx_nand_state[chip].oob_size; 1397210284Sjmallett 1398215990Sjmallett /* The NAND DMA engine always does transfers in 8 byte blocks, so round the buffer size down 1399215990Sjmallett ** to a multiple of 8, otherwise we will transfer too much data to the NAND chip. 1400215990Sjmallett ** Note this prevents the last few bytes of the OOB being written. If these bytes 1401215990Sjmallett ** need to be written, then this check needs to be removed, but this will result in 1402215990Sjmallett ** extra write cycles beyond the end of the OOB. */ 1403215990Sjmallett buffer_length &= ~0x7; 1404215990Sjmallett 1405210284Sjmallett /* Build the command and address cycles */ 1406210284Sjmallett if (__cvmx_nand_build_pre_cmd(chip, NAND_COMMAND_PROGRAM, __cvmx_nand_get_address_cycles(chip), nand_address, 0)) 1407210284Sjmallett CVMX_NAND_RETURN(CVMX_NAND_NO_MEMORY); 1408210284Sjmallett 1409210284Sjmallett /* Send WRITE */ 1410210284Sjmallett memset(&cmd, 0, sizeof(cmd)); 1411210284Sjmallett cmd.wr.data_bytes = buffer_length; 1412210284Sjmallett cmd.wr.eight = 8; 1413210284Sjmallett cmd.wr.wrn1 = cvmx_nand_state[chip].wrn[0]; 1414210284Sjmallett cmd.wr.wrn2 = cvmx_nand_state[chip].wrn[1]; 1415210284Sjmallett if (cvmx_nand_submit(cmd)) 1416210284Sjmallett CVMX_NAND_RETURN(CVMX_NAND_NO_MEMORY); 1417210284Sjmallett 1418210284Sjmallett /* Send WRITE command */ 1419210284Sjmallett memset(&cmd, 0, sizeof(cmd)); 1420210284Sjmallett cmd.cle.cmd_data = NAND_COMMAND_PROGRAM_FIN; 1421210284Sjmallett cmd.cle.clen1 = cvmx_nand_state[chip].clen[0]; 1422210284Sjmallett cmd.cle.clen2 = cvmx_nand_state[chip].clen[1]; 1423210284Sjmallett cmd.cle.clen3 = cvmx_nand_state[chip].clen[2]; 1424210284Sjmallett cmd.cle.four = 4; 1425210284Sjmallett if (cvmx_nand_submit(cmd)) 1426210284Sjmallett CVMX_NAND_RETURN(CVMX_NAND_NO_MEMORY); 1427210284Sjmallett 1428210284Sjmallett __cvmx_nand_setup_dma(chip, 1, buffer_address, buffer_length); 1429210284Sjmallett 1430210284Sjmallett /* WAIT for R_B to signal program is complete */ 1431215990Sjmallett if (__wait_for_busy_done(chip)) 1432210284Sjmallett CVMX_NAND_RETURN(CVMX_NAND_NO_MEMORY); 1433210284Sjmallett 1434210284Sjmallett if (__cvmx_nand_build_post_cmd()) 1435210284Sjmallett CVMX_NAND_RETURN(CVMX_NAND_NO_MEMORY); 1436210284Sjmallett 1437210284Sjmallett /* Wait for the DMA to complete */ 1438232812Sjmallett WATCHDOG_RESET(); 1439232812Sjmallett if (CVMX_WAIT_FOR_FIELD64(CVMX_MIO_NDF_DMA_CFG, cvmx_mio_ndf_dma_cfg_t, en, ==, 0, NAND_TIMEOUT_USECS_WRITE)) 1440232812Sjmallett { 1441232812Sjmallett WATCHDOG_RESET(); 1442210284Sjmallett CVMX_NAND_RETURN(CVMX_NAND_TIMEOUT); 1443232812Sjmallett } 1444210284Sjmallett CVMX_NAND_RETURN(CVMX_NAND_SUCCESS); 1445210284Sjmallett} 1446215990Sjmallett#ifdef CVMX_BUILD_FOR_LINUX_KERNEL 1447215990SjmallettEXPORT_SYMBOL(cvmx_nand_page_write); 1448215990Sjmallett#endif 1449210284Sjmallett 1450210284Sjmallett 1451210284Sjmallett/** 1452210284Sjmallett * Erase a NAND block. A single block contains multiple pages. 1453210284Sjmallett * 1454210284Sjmallett * @param chip Chip select for NAND flash 1455210284Sjmallett * @param nand_address 1456210284Sjmallett * Location in NAND to erase. See description in file comment 1457210284Sjmallett * 1458210284Sjmallett * @return Zero on success, a negative cvmx_nand_status_t error code on failure 1459210284Sjmallett */ 1460210284Sjmallettcvmx_nand_status_t cvmx_nand_block_erase(int chip, uint64_t nand_address) 1461210284Sjmallett{ 1462210284Sjmallett CVMX_NAND_LOG_CALLED(); 1463210284Sjmallett CVMX_NAND_LOG_PARAM("%d", chip); 1464210284Sjmallett CVMX_NAND_LOG_PARAM("0x%llx", (ULL)nand_address); 1465210284Sjmallett 1466210284Sjmallett if ((chip < 0) || (chip > 7)) 1467210284Sjmallett CVMX_NAND_RETURN(CVMX_NAND_INVALID_PARAM); 1468210284Sjmallett if (!cvmx_nand_state[chip].page_size) 1469210284Sjmallett CVMX_NAND_RETURN(CVMX_NAND_INVALID_PARAM); 1470210284Sjmallett 1471210284Sjmallett /* Build the command and address cycles */ 1472210284Sjmallett if (__cvmx_nand_build_pre_cmd(chip, NAND_COMMAND_ERASE, 1473210284Sjmallett (__cvmx_nand_get_row_bits(chip)+7) >> 3, 1474210284Sjmallett nand_address >> __cvmx_nand_get_column_bits(chip), 1475210284Sjmallett NAND_COMMAND_ERASE_FIN)) 1476210284Sjmallett CVMX_NAND_RETURN(CVMX_NAND_NO_MEMORY); 1477210284Sjmallett 1478210284Sjmallett /* WAIT for R_B to signal erase is complete */ 1479215990Sjmallett if (__wait_for_busy_done(chip)) 1480210284Sjmallett CVMX_NAND_RETURN(CVMX_NAND_NO_MEMORY); 1481210284Sjmallett 1482210284Sjmallett if (__cvmx_nand_build_post_cmd()) 1483210284Sjmallett CVMX_NAND_RETURN(CVMX_NAND_NO_MEMORY); 1484210284Sjmallett 1485210284Sjmallett /* Wait for the command queue to be idle, which means the wait is done */ 1486232812Sjmallett WATCHDOG_RESET(); 1487232812Sjmallett if (CVMX_WAIT_FOR_FIELD64(CVMX_NDF_ST_REG, cvmx_ndf_st_reg_t, exe_idle, ==, 1, NAND_TIMEOUT_USECS_BLOCK_ERASE)) 1488232812Sjmallett { 1489232812Sjmallett WATCHDOG_RESET(); 1490210284Sjmallett CVMX_NAND_RETURN(CVMX_NAND_TIMEOUT); 1491232812Sjmallett } 1492210284Sjmallett 1493210284Sjmallett CVMX_NAND_RETURN(CVMX_NAND_SUCCESS); 1494210284Sjmallett} 1495215990Sjmallett#ifdef CVMX_BUILD_FOR_LINUX_KERNEL 1496215990SjmallettEXPORT_SYMBOL(cvmx_nand_block_erase); 1497215990Sjmallett#endif 1498210284Sjmallett 1499210284Sjmallett 1500215990Sjmallett/* Some reads (read ID, read parameter page) only use the low 8 bits of the bus 1501215990Sjmallett** in 16 bit mode. We remove the unused bytes so that the data we present to the 1502215990Sjmallett** caller is as expected (same as 8 bit mode.) 1503215990Sjmallett*/ 1504215990Sjmallettstatic void __cvmx_nand_fixup_16bit_id_reads(uint8_t *buf, int buffer_length) 1505215990Sjmallett{ 1506215990Sjmallett /* Decimate data, taking only every other byte. */ 1507215990Sjmallett int i; 1508215990Sjmallett for (i = 0; i < buffer_length/2; i++) 1509215990Sjmallett buf[i] = buf[2*i + 1]; 1510215990Sjmallett} 1511215990Sjmallett 1512210284Sjmallett/** 1513210284Sjmallett * Read the NAND ID information 1514210284Sjmallett * 1515210284Sjmallett * @param chip Chip select for NAND flash 1516210284Sjmallett * @param nand_address 1517210284Sjmallett * NAND address to read ID from. Usually this is either 0x0 or 0x20. 1518210284Sjmallett * @param buffer_address 1519210284Sjmallett * Physical address to store data in 1520210284Sjmallett * @param buffer_length 1521215990Sjmallett * Length of the buffer. Usually this is 4-8 bytes. For 16 bit mode, this must be twice 1522215990Sjmallett * as large as the actual expected data. 1523210284Sjmallett * 1524210284Sjmallett * @return Bytes read on success, a negative cvmx_nand_status_t error code on failure 1525210284Sjmallett */ 1526210284Sjmallettint cvmx_nand_read_id(int chip, uint64_t nand_address, uint64_t buffer_address, int buffer_length) 1527210284Sjmallett{ 1528210284Sjmallett int bytes; 1529210284Sjmallett 1530210284Sjmallett CVMX_NAND_LOG_CALLED(); 1531210284Sjmallett CVMX_NAND_LOG_PARAM("%d", chip); 1532210284Sjmallett CVMX_NAND_LOG_PARAM("0x%llx", (ULL)nand_address); 1533210284Sjmallett CVMX_NAND_LOG_PARAM("0x%llx", (ULL)buffer_address); 1534210284Sjmallett CVMX_NAND_LOG_PARAM("%d", buffer_length); 1535210284Sjmallett 1536210284Sjmallett if ((chip < 0) || (chip > 7)) 1537210284Sjmallett CVMX_NAND_RETURN(CVMX_NAND_INVALID_PARAM); 1538210284Sjmallett if (!buffer_address) 1539210284Sjmallett CVMX_NAND_RETURN(CVMX_NAND_INVALID_PARAM); 1540210284Sjmallett if (buffer_address & 7) 1541210284Sjmallett CVMX_NAND_RETURN(CVMX_NAND_INVALID_PARAM); 1542210284Sjmallett if (!buffer_length) 1543210284Sjmallett CVMX_NAND_RETURN(CVMX_NAND_INVALID_PARAM); 1544210284Sjmallett 1545210284Sjmallett bytes = __cvmx_nand_low_level_read(chip, NAND_COMMAND_READ_ID, 1, nand_address, 0, buffer_address, buffer_length); 1546215990Sjmallett if (cvmx_nand_state[chip].flags & CVMX_NAND_STATE_16BIT) 1547215990Sjmallett __cvmx_nand_fixup_16bit_id_reads(cvmx_phys_to_ptr(buffer_address), buffer_length); 1548215990Sjmallett 1549210284Sjmallett CVMX_NAND_RETURN(bytes); 1550210284Sjmallett} 1551215990Sjmallett#ifdef CVMX_BUILD_FOR_LINUX_KERNEL 1552215990SjmallettEXPORT_SYMBOL(cvmx_nand_read_id); 1553215990Sjmallett#endif 1554210284Sjmallett 1555210284Sjmallett 1556210284Sjmallett/** 1557210284Sjmallett * Read the NAND parameter page 1558210284Sjmallett * 1559210284Sjmallett * @param chip Chip select for NAND flash 1560210284Sjmallett * @param buffer_address 1561210284Sjmallett * Physical address to store data in 1562210284Sjmallett * @param buffer_length 1563215990Sjmallett * Length of the buffer. Usually 1024 bytes for 8 bit, 2048 for 16 bit mode. 1564210284Sjmallett * 1565210284Sjmallett * @return Bytes read on success, a negative cvmx_nand_status_t error code on failure 1566210284Sjmallett */ 1567210284Sjmallettint cvmx_nand_read_param_page(int chip, uint64_t buffer_address, int buffer_length) 1568210284Sjmallett{ 1569210284Sjmallett int bytes; 1570210284Sjmallett 1571210284Sjmallett CVMX_NAND_LOG_CALLED(); 1572210284Sjmallett CVMX_NAND_LOG_PARAM("%d", chip); 1573210284Sjmallett CVMX_NAND_LOG_PARAM("0x%llx", (ULL)buffer_address); 1574210284Sjmallett CVMX_NAND_LOG_PARAM("%d", buffer_length); 1575210284Sjmallett 1576210284Sjmallett if ((chip < 0) || (chip > 7)) 1577210284Sjmallett CVMX_NAND_RETURN(CVMX_NAND_INVALID_PARAM); 1578210284Sjmallett if (!buffer_address) 1579210284Sjmallett CVMX_NAND_RETURN(CVMX_NAND_INVALID_PARAM); 1580210284Sjmallett if (buffer_address & 7) 1581210284Sjmallett CVMX_NAND_RETURN(CVMX_NAND_INVALID_PARAM); 1582215990Sjmallett if (buffer_length & 7) 1583215990Sjmallett CVMX_NAND_RETURN(CVMX_NAND_INVALID_PARAM); 1584210284Sjmallett if (!buffer_length) 1585210284Sjmallett CVMX_NAND_RETURN(CVMX_NAND_INVALID_PARAM); 1586210284Sjmallett 1587210284Sjmallett bytes = __cvmx_nand_low_level_read(chip, NAND_COMMAND_READ_PARAM_PAGE, 1, 0x0, 0, buffer_address, buffer_length); 1588215990Sjmallett if (cvmx_nand_state[chip].flags & CVMX_NAND_STATE_16BIT) 1589215990Sjmallett __cvmx_nand_fixup_16bit_id_reads(cvmx_phys_to_ptr(buffer_address), buffer_length); 1590210284Sjmallett CVMX_NAND_RETURN(bytes); 1591210284Sjmallett} 1592210284Sjmallett 1593210284Sjmallett 1594210284Sjmallett/** 1595210284Sjmallett * Get the status of the NAND flash 1596210284Sjmallett * 1597210284Sjmallett * @param chip Chip select for NAND flash 1598210284Sjmallett * 1599210284Sjmallett * @return NAND status or a negative cvmx_nand_status_t error code on failure 1600210284Sjmallett */ 1601210284Sjmallettint cvmx_nand_get_status(int chip) 1602210284Sjmallett{ 1603210284Sjmallett int status; 1604215990Sjmallett int offset = !!(cvmx_nand_state[chip].flags & CVMX_NAND_STATE_16BIT); /* Normalize flag to 0/1 */ 1605210284Sjmallett 1606210284Sjmallett CVMX_NAND_LOG_CALLED(); 1607210284Sjmallett CVMX_NAND_LOG_PARAM("%d", chip); 1608210284Sjmallett 1609210284Sjmallett if ((chip < 0) || (chip > 7)) 1610210284Sjmallett CVMX_NAND_RETURN(CVMX_NAND_INVALID_PARAM); 1611210284Sjmallett 1612215990Sjmallett *((uint8_t*)cvmx_nand_buffer + offset) = 0xff; 1613215990Sjmallett status = __cvmx_nand_low_level_read(chip, NAND_COMMAND_STATUS, 0, 0, 0, cvmx_ptr_to_phys(cvmx_nand_buffer), 8); 1614210284Sjmallett if (status > 0) 1615215990Sjmallett status = *((uint8_t*)cvmx_nand_buffer + offset); 1616210284Sjmallett 1617210284Sjmallett CVMX_NAND_RETURN(status); 1618210284Sjmallett} 1619215990Sjmallett#ifdef CVMX_BUILD_FOR_LINUX_KERNEL 1620215990SjmallettEXPORT_SYMBOL(cvmx_nand_get_status); 1621215990Sjmallett#endif 1622210284Sjmallett 1623210284Sjmallett 1624210284Sjmallett/** 1625210284Sjmallett * Get the page size, excluding out of band data. This function 1626210284Sjmallett * will return zero for chip selects not connected to NAND. 1627210284Sjmallett * 1628210284Sjmallett * @param chip Chip select for NAND flash 1629210284Sjmallett * 1630210284Sjmallett * @return Page size in bytes or a negative cvmx_nand_status_t error code on failure 1631210284Sjmallett */ 1632210284Sjmallettint cvmx_nand_get_page_size(int chip) 1633210284Sjmallett{ 1634210284Sjmallett CVMX_NAND_LOG_CALLED(); 1635210284Sjmallett CVMX_NAND_LOG_PARAM("%d", chip); 1636210284Sjmallett 1637210284Sjmallett if ((chip < 0) || (chip > 7)) 1638210284Sjmallett CVMX_NAND_RETURN(CVMX_NAND_INVALID_PARAM); 1639210284Sjmallett 1640210284Sjmallett CVMX_NAND_RETURN(cvmx_nand_state[chip].page_size); 1641210284Sjmallett} 1642210284Sjmallett 1643210284Sjmallett 1644210284Sjmallett/** 1645210284Sjmallett * Get the OOB size. 1646210284Sjmallett * 1647210284Sjmallett * @param chip Chip select for NAND flash 1648210284Sjmallett * 1649210284Sjmallett * @return OOB in bytes or a negative cvmx_nand_status_t error code on failure 1650210284Sjmallett */ 1651210284Sjmallettint cvmx_nand_get_oob_size(int chip) 1652210284Sjmallett{ 1653210284Sjmallett CVMX_NAND_LOG_CALLED(); 1654210284Sjmallett CVMX_NAND_LOG_PARAM("%d", chip); 1655210284Sjmallett 1656210284Sjmallett if ((chip < 0) || (chip > 7)) 1657210284Sjmallett CVMX_NAND_RETURN(CVMX_NAND_INVALID_PARAM); 1658210284Sjmallett 1659210284Sjmallett CVMX_NAND_RETURN(cvmx_nand_state[chip].oob_size); 1660210284Sjmallett} 1661210284Sjmallett 1662210284Sjmallett 1663210284Sjmallett/** 1664210284Sjmallett * Get the number of pages per NAND block 1665210284Sjmallett * 1666210284Sjmallett * @param chip Chip select for NAND flash 1667210284Sjmallett * 1668210284Sjmallett * @return Number of pages in each block or a negative cvmx_nand_status_t error 1669210284Sjmallett * code on failure 1670210284Sjmallett */ 1671210284Sjmallettint cvmx_nand_get_pages_per_block(int chip) 1672210284Sjmallett{ 1673210284Sjmallett CVMX_NAND_LOG_CALLED(); 1674210284Sjmallett CVMX_NAND_LOG_PARAM("%d", chip); 1675210284Sjmallett 1676210284Sjmallett if ((chip < 0) || (chip > 7)) 1677210284Sjmallett CVMX_NAND_RETURN(CVMX_NAND_INVALID_PARAM); 1678210284Sjmallett 1679210284Sjmallett CVMX_NAND_RETURN(cvmx_nand_state[chip].pages_per_block); 1680210284Sjmallett} 1681210284Sjmallett 1682210284Sjmallett 1683210284Sjmallett/** 1684210284Sjmallett * Get the number of blocks in the NAND flash 1685210284Sjmallett * 1686210284Sjmallett * @param chip Chip select for NAND flash 1687210284Sjmallett * 1688210284Sjmallett * @return Number of blocks or a negative cvmx_nand_status_t error code on failure 1689210284Sjmallett */ 1690210284Sjmallettint cvmx_nand_get_blocks(int chip) 1691210284Sjmallett{ 1692210284Sjmallett CVMX_NAND_LOG_CALLED(); 1693210284Sjmallett CVMX_NAND_LOG_PARAM("%d", chip); 1694210284Sjmallett 1695210284Sjmallett if ((chip < 0) || (chip > 7)) 1696210284Sjmallett CVMX_NAND_RETURN(CVMX_NAND_INVALID_PARAM); 1697210284Sjmallett 1698210284Sjmallett CVMX_NAND_RETURN(cvmx_nand_state[chip].blocks); 1699210284Sjmallett} 1700210284Sjmallett 1701210284Sjmallett 1702210284Sjmallett/** 1703210284Sjmallett * Reset the NAND flash 1704210284Sjmallett * 1705210284Sjmallett * @param chip Chip select for NAND flash 1706210284Sjmallett * 1707210284Sjmallett * @return Zero on success, a negative cvmx_nand_status_t error code on failure 1708210284Sjmallett */ 1709210284Sjmallettcvmx_nand_status_t cvmx_nand_reset(int chip) 1710210284Sjmallett{ 1711210284Sjmallett CVMX_NAND_LOG_CALLED(); 1712210284Sjmallett CVMX_NAND_LOG_PARAM("%d", chip); 1713210284Sjmallett 1714210284Sjmallett if ((chip < 0) || (chip > 7)) 1715210284Sjmallett CVMX_NAND_RETURN(CVMX_NAND_INVALID_PARAM); 1716210284Sjmallett if (!cvmx_nand_state[chip].page_size) 1717210284Sjmallett CVMX_NAND_RETURN(CVMX_NAND_INVALID_PARAM); 1718210284Sjmallett 1719210284Sjmallett if (__cvmx_nand_build_pre_cmd(chip, NAND_COMMAND_RESET, 0, 0, 0)) 1720210284Sjmallett CVMX_NAND_RETURN(CVMX_NAND_NO_MEMORY); 1721210284Sjmallett 1722210284Sjmallett /* WAIT for R_B to signal reset is complete */ 1723215990Sjmallett if (__wait_for_busy_done(chip)) 1724210284Sjmallett CVMX_NAND_RETURN(CVMX_NAND_NO_MEMORY); 1725210284Sjmallett 1726210284Sjmallett if (__cvmx_nand_build_post_cmd()) 1727210284Sjmallett CVMX_NAND_RETURN(CVMX_NAND_NO_MEMORY); 1728210284Sjmallett 1729210284Sjmallett CVMX_NAND_RETURN(CVMX_NAND_SUCCESS); 1730210284Sjmallett} 1731215990Sjmallett#ifdef CVMX_BUILD_FOR_LINUX_KERNEL 1732215990SjmallettEXPORT_SYMBOL(cvmx_nand_reset); 1733215990Sjmallett#endif 1734210284Sjmallett 1735210284Sjmallett 1736210284Sjmallett 1737210284Sjmallett 1738210284Sjmallett/** 1739210284Sjmallett * This function computes the Octeon specific ECC data used by the NAND boot 1740210284Sjmallett * feature. 1741215990Sjmallett * 1742210284Sjmallett * @param block pointer to 256 bytes of data 1743210284Sjmallett * @param eccp pointer to where 8 bytes of ECC data will be stored 1744210284Sjmallett */ 1745210284Sjmallettvoid cvmx_nand_compute_boot_ecc(unsigned char *block, unsigned char *eccp) 1746210284Sjmallett{ 1747210284Sjmallett unsigned char pd0, pd1, pd2; 1748210284Sjmallett int i, j; 1749210284Sjmallett 1750210284Sjmallett pd0 = pd1 = pd2 = 0; 1751210284Sjmallett 1752210284Sjmallett for (i = 0; i < 256; i++) /* PD0<0> */ 1753210284Sjmallett pd0 ^= (block[i] ^ (block[i] >> 2) ^ (block[i] >> 4) ^ (block[i] >> 6)) & 1; 1754210284Sjmallett for (i = 0; i < 256; i++) /* PD0<1> */ 1755210284Sjmallett pd0 ^= ((block[i] ^ (block[i] >> 1) ^ (block[i] >> 4) ^ (block[i] >> 5)) & 1) << 1; 1756210284Sjmallett for (i = 0; i < 256; i++) /* PD0<2> */ 1757210284Sjmallett pd0 ^= ((block[i] ^ (block[i] >> 1) ^ (block[i] >> 2) ^ (block[i] >> 3)) & 1) << 2; 1758210284Sjmallett for (i = 0; i < 128; i++) /* PD0<3> */ 1759210284Sjmallett pd0 ^= ((block[2*i] ^ (block[2*i] >> 1) ^ (block[2*i] >> 2) ^ 1760210284Sjmallett (block[2*i] >> 3) ^ (block[2*i] >> 4) ^ (block[2*i] >> 5) ^ 1761210284Sjmallett (block[2*i] >> 6) ^ (block[2*i] >> 7)) & 1) << 3; 1762210284Sjmallett for (i = 0; i < 64; i++) /* PD0<4> */ 1763210284Sjmallett for (j = 0; j < 2; j++) 1764210284Sjmallett pd0 ^= ((block[4*i+j] ^ (block[4*i+j] >> 1) ^ (block[4*i+j] >> 2) ^ 1765210284Sjmallett (block[4*i+j] >> 3) ^ (block[4*i+j] >> 4) ^ (block[4*i+j] >> 5) ^ 1766210284Sjmallett (block[4*i+j] >> 6) ^ (block[4*i+j] >> 7)) & 1) << 4; 1767210284Sjmallett for (i = 0; i < 32; i++) /* PD0<5> */ 1768210284Sjmallett for (j = 0; j < 4; j++) 1769210284Sjmallett pd0 ^= ((block[8*i+j] ^ (block[8*i+j] >> 1) ^ (block[8*i+j] >> 2) ^ 1770210284Sjmallett (block[8*i+j] >> 3) ^ (block[8*i+j] >> 4) ^ (block[8*i+j] >> 5) ^ 1771210284Sjmallett (block[8*i+j] >> 6) ^ (block[8*i+j] >> 7)) & 1) << 5; 1772210284Sjmallett for (i = 0; i < 16; i++) /* PD0<6> */ 1773210284Sjmallett for (j = 0; j < 8; j++) 1774210284Sjmallett pd0 ^= ((block[16*i+j] ^ (block[16*i+j] >> 1) ^ (block[16*i+j] >> 2) ^ 1775210284Sjmallett (block[16*i+j] >> 3) ^ (block[16*i+j] >> 4) ^ (block[16*i+j] >> 5) ^ 1776210284Sjmallett (block[16*i+j] >> 6) ^ (block[16*i+j] >> 7)) & 1) << 6; 1777210284Sjmallett for (i = 0; i < 8; i++) /* PD0<7> */ 1778210284Sjmallett for (j = 0; j < 16; j++) 1779210284Sjmallett pd0 ^= ((block[32*i+j] ^ (block[32*i+j] >> 1) ^ (block[32*i+j] >> 2) ^ 1780210284Sjmallett (block[32*i+j] >> 3) ^ (block[32*i+j] >> 4) ^ (block[32*i+j] >> 5) ^ 1781210284Sjmallett (block[32*i+j] >> 6) ^ (block[32*i+j] >> 7)) & 1) << 7; 1782210284Sjmallett for (i = 0; i < 4; i++) /* PD1<0> */ 1783210284Sjmallett for (j = 0; j < 32; j++) 1784210284Sjmallett pd1 ^= ((block[64*i+j] ^ (block[64*i+j] >> 1) ^ (block[64*i+j] >> 2) ^ 1785210284Sjmallett (block[64*i+j] >> 3) ^ (block[64*i+j] >> 4) ^ (block[64*i+j] >> 5) ^ 1786210284Sjmallett (block[64*i+j] >> 6) ^ (block[64*i+j] >> 7)) & 1) << 0; 1787210284Sjmallett for (i = 0; i < 2; i++) /* PD1<1> */ 1788210284Sjmallett for (j = 0; j < 64; j++) 1789210284Sjmallett pd1 ^= ((block[128*i+j] ^ (block[128*i+j] >> 1) ^ (block[128*i+j] >> 2) ^ 1790210284Sjmallett (block[128*i+j] >> 3) ^ (block[128*i+j] >> 4) ^ (block[128*i+j] >> 5) ^ 1791210284Sjmallett (block[128*i+j] >> 6) ^ (block[128*i+j] >> 7)) & 1) << 1; 1792210284Sjmallett for (i = 0; i < 128; i++) /* PD1<2> */ 1793210284Sjmallett pd1 ^= ((block[i] ^ (block[i] >> 1) ^ (block[i] >> 2) ^ 1794210284Sjmallett (block[i] >> 3) ^ (block[i] >> 4) ^ (block[i] >> 5) ^ 1795210284Sjmallett (block[i] >> 6) ^ (block[i] >> 7)) & 1) << 2; 1796210284Sjmallett /* PD1<3> */ 1797210284Sjmallett /* PD1<4> */ 1798210284Sjmallett for (i = 0; i < 256; i++) /* PD1<5> */ 1799210284Sjmallett pd1 ^= (((block[i] >> 1) ^ (block[i] >> 3) ^ (block[i] >> 5) ^ (block[i] >> 7)) & 1) << 5; 1800210284Sjmallett for (i = 0; i < 256; i++) /* PD1<6> */ 1801210284Sjmallett pd1 ^= (((block[i] >> 2) ^ (block[i] >> 3) ^ (block[i] >> 6) ^ (block[i] >> 7)) & 1) << 6; 1802210284Sjmallett for (i = 0; i < 256; i++) /* PD1<7> */ 1803210284Sjmallett pd1 ^= (((block[i] >> 4) ^ (block[i] >> 5) ^ (block[i] >> 6) ^ (block[i] >> 7)) & 1) << 7; 1804210284Sjmallett for (i = 0; i < 128; i++) /* PD2<0> */ 1805210284Sjmallett pd2 ^= ((block[2*i+1] ^ (block[2*i+1] >> 1) ^ (block[2*i+1] >> 2) ^ 1806210284Sjmallett (block[2*i+1] >> 3) ^ (block[2*i+1] >> 4) ^ (block[2*i+1] >> 5) ^ 1807210284Sjmallett (block[2*i+1] >> 6) ^ (block[2*i+1] >> 7)) & 1) << 0; 1808210284Sjmallett for (i = 0; i < 64; i++) /* PD2<1> */ 1809210284Sjmallett for (j = 2; j < 4; j++) 1810210284Sjmallett pd2 ^= ((block[4*i+j] ^ (block[4*i+j] >> 1) ^ (block[4*i+j] >> 2) ^ 1811210284Sjmallett (block[4*i+j] >> 3) ^ (block[4*i+j] >> 4) ^ (block[4*i+j] >> 5) ^ 1812210284Sjmallett (block[4*i+j] >> 6) ^ (block[4*i+j] >> 7)) & 1) << 1; 1813210284Sjmallett for (i = 0; i < 32; i++) /* PD2<2> */ 1814210284Sjmallett for (j = 4; j < 8; j++) 1815210284Sjmallett pd2 ^= ((block[8*i+j] ^ (block[8*i+j] >> 1) ^ (block[8*i+j] >> 2) ^ 1816210284Sjmallett (block[8*i+j] >> 3) ^ (block[8*i+j] >> 4) ^ (block[8*i+j] >> 5) ^ 1817210284Sjmallett (block[8*i+j] >> 6) ^ (block[8*i+j] >> 7)) & 1) << 2; 1818210284Sjmallett for (i = 0; i < 16; i++) /* PD2<3> */ 1819210284Sjmallett for (j = 8; j < 16; j++) 1820210284Sjmallett pd2 ^= ((block[16*i+j] ^ (block[16*i+j] >> 1) ^ (block[16*i+j] >> 2) ^ 1821210284Sjmallett (block[16*i+j] >> 3) ^ (block[16*i+j] >> 4) ^ (block[16*i+j] >> 5) ^ 1822210284Sjmallett (block[16*i+j] >> 6) ^ (block[16*i+j] >> 7)) & 1) << 3; 1823210284Sjmallett for (i = 0; i < 8; i++) /* PD2<4> */ 1824210284Sjmallett for (j = 16; j < 32; j++) 1825210284Sjmallett pd2 ^= ((block[32*i+j] ^ (block[32*i+j] >> 1) ^ (block[32*i+j] >> 2) ^ 1826210284Sjmallett (block[32*i+j] >> 3) ^ (block[32*i+j] >> 4) ^ (block[32*i+j] >> 5) ^ 1827210284Sjmallett (block[32*i+j] >> 6) ^ (block[32*i+j] >> 7)) & 1) << 4; 1828210284Sjmallett for (i = 0; i < 4; i++) /* PD2<5> */ 1829210284Sjmallett for (j = 32; j < 64; j++) 1830210284Sjmallett pd2 ^= ((block[64*i+j] ^ (block[64*i+j] >> 1) ^ (block[64*i+j] >> 2) ^ 1831210284Sjmallett (block[64*i+j] >> 3) ^ (block[64*i+j] >> 4) ^ (block[64*i+j] >> 5) ^ 1832210284Sjmallett (block[64*i+j] >> 6) ^ (block[64*i+j] >> 7)) & 1) << 5; 1833210284Sjmallett for (i = 0; i < 2; i++) /* PD2<6> */ 1834210284Sjmallett for (j = 64; j < 128; j++) 1835210284Sjmallett pd2 ^= ((block[128*i+j] ^ (block[128*i+j] >> 1) ^ (block[128*i+j] >> 2) ^ 1836210284Sjmallett (block[128*i+j] >> 3) ^ (block[128*i+j] >> 4) ^ (block[128*i+j] >> 5) ^ 1837210284Sjmallett (block[128*i+j] >> 6) ^ (block[128*i+j] >> 7)) & 1) << 6; 1838210284Sjmallett for (i = 128; i < 256; i++) /* PD2<7> */ 1839210284Sjmallett pd2 ^= ((block[i] ^ (block[i] >> 1) ^ (block[i] >> 2) ^ 1840210284Sjmallett (block[i] >> 3) ^ (block[i] >> 4) ^ (block[i] >> 5) ^ 1841210284Sjmallett (block[i] >> 6) ^ (block[i] >> 7)) & 1) << 7; 1842210284Sjmallett 1843210284Sjmallett eccp[0] = pd0; 1844210284Sjmallett eccp[1] = pd1; 1845210284Sjmallett eccp[2] = pd2; 1846210284Sjmallett} 1847210284Sjmallett 1848210284Sjmallett/** 1849210284Sjmallett * Check an Octeon ECC block, fixing errors if possible 1850210284Sjmallett * 1851210284Sjmallett * @param block Pointer to block to check 1852210284Sjmallett * 1853210284Sjmallett * @return Zero if block has no errors, one if errors were corrected, two 1854210284Sjmallett * if the errors could not be corrected. 1855210284Sjmallett */ 1856210284Sjmallettint cvmx_nand_correct_boot_ecc(uint8_t *block) 1857210284Sjmallett{ 1858210284Sjmallett unsigned char pd0, pd1, pd2; 1859210284Sjmallett int i, j; 1860210284Sjmallett unsigned char xorpd0, xorpd1, xorpd2; 1861210284Sjmallett int xor_num; 1862210284Sjmallett unsigned int check; 1863210284Sjmallett 1864210284Sjmallett asm volatile ("pref 0,0(%0);pref 0,128(%0);pref 0,256(%0)\n" :: "r" (block)); 1865210284Sjmallett 1866210284Sjmallett pd0 = pd1 = pd2 = 0; 1867210284Sjmallett 1868210284Sjmallett for (i = 0; i < 256; i++) /* PD0<0> */ 1869210284Sjmallett pd0 ^= (block[i] ^ (block[i] >> 2) ^ (block[i] >> 4) ^ (block[i] >> 6)) & 1; 1870210284Sjmallett for (i = 0; i < 256; i++) /* PD0<1> */ 1871210284Sjmallett pd0 ^= ((block[i] ^ (block[i] >> 1) ^ (block[i] >> 4) ^ (block[i] >> 5)) & 1) << 1; 1872210284Sjmallett for (i = 0; i < 256; i++) /* PD0<2> */ 1873210284Sjmallett pd0 ^= ((block[i] ^ (block[i] >> 1) ^ (block[i] >> 2) ^ (block[i] >> 3)) & 1) << 2; 1874210284Sjmallett for (i = 0; i < 128; i++) /* PD0<3> */ 1875210284Sjmallett pd0 ^= ((block[2*i] ^ (block[2*i] >> 1) ^ (block[2*i] >> 2) ^ 1876210284Sjmallett (block[2*i] >> 3) ^ (block[2*i] >> 4) ^ (block[2*i] >> 5) ^ 1877210284Sjmallett (block[2*i] >> 6) ^ (block[2*i] >> 7)) & 1) << 3; 1878210284Sjmallett for (i = 0; i < 64; i++) /* PD0<4> */ 1879210284Sjmallett for (j = 0; j < 2; j++) 1880210284Sjmallett pd0 ^= ((block[4*i+j] ^ (block[4*i+j] >> 1) ^ (block[4*i+j] >> 2) ^ 1881210284Sjmallett (block[4*i+j] >> 3) ^ (block[4*i+j] >> 4) ^ (block[4*i+j] >> 5) ^ 1882210284Sjmallett (block[4*i+j] >> 6) ^ (block[4*i+j] >> 7)) & 1) << 4; 1883210284Sjmallett for (i = 0; i < 32; i++) /* PD0<5> */ 1884210284Sjmallett for (j = 0; j < 4; j++) 1885210284Sjmallett pd0 ^= ((block[8*i+j] ^ (block[8*i+j] >> 1) ^ (block[8*i+j] >> 2) ^ 1886210284Sjmallett (block[8*i+j] >> 3) ^ (block[8*i+j] >> 4) ^ (block[8*i+j] >> 5) ^ 1887210284Sjmallett (block[8*i+j] >> 6) ^ (block[8*i+j] >> 7)) & 1) << 5; 1888210284Sjmallett for (i = 0; i < 16; i++) /* PD0<6> */ 1889210284Sjmallett for (j = 0; j < 8; j++) 1890210284Sjmallett pd0 ^= ((block[16*i+j] ^ (block[16*i+j] >> 1) ^ (block[16*i+j] >> 2) ^ 1891210284Sjmallett (block[16*i+j] >> 3) ^ (block[16*i+j] >> 4) ^ (block[16*i+j] >> 5) ^ 1892210284Sjmallett (block[16*i+j] >> 6) ^ (block[16*i+j] >> 7)) & 1) << 6; 1893210284Sjmallett for (i = 0; i < 8; i++) /* PD0<7> */ 1894210284Sjmallett for (j = 0; j < 16; j++) 1895210284Sjmallett pd0 ^= ((block[32*i+j] ^ (block[32*i+j] >> 1) ^ (block[32*i+j] >> 2) ^ 1896210284Sjmallett (block[32*i+j] >> 3) ^ (block[32*i+j] >> 4) ^ (block[32*i+j] >> 5) ^ 1897210284Sjmallett (block[32*i+j] >> 6) ^ (block[32*i+j] >> 7)) & 1) << 7; 1898210284Sjmallett for (i = 0; i < 4; i++) /* PD1<0> */ 1899210284Sjmallett for (j = 0; j < 32; j++) 1900210284Sjmallett pd1 ^= ((block[64*i+j] ^ (block[64*i+j] >> 1) ^ (block[64*i+j] >> 2) ^ 1901210284Sjmallett (block[64*i+j] >> 3) ^ (block[64*i+j] >> 4) ^ (block[64*i+j] >> 5) ^ 1902210284Sjmallett (block[64*i+j] >> 6) ^ (block[64*i+j] >> 7)) & 1) << 0; 1903210284Sjmallett for (i = 0; i < 2; i++) /* PD1<1> */ 1904210284Sjmallett for (j = 0; j < 64; j++) 1905210284Sjmallett pd1 ^= ((block[128*i+j] ^ (block[128*i+j] >> 1) ^ (block[128*i+j] >> 2) ^ 1906210284Sjmallett (block[128*i+j] >> 3) ^ (block[128*i+j] >> 4) ^ (block[128*i+j] >> 5) ^ 1907210284Sjmallett (block[128*i+j] >> 6) ^ (block[128*i+j] >> 7)) & 1) << 1; 1908210284Sjmallett for (i = 0; i < 128; i++) /* PD1<2> */ 1909210284Sjmallett pd1 ^= ((block[i] ^ (block[i] >> 1) ^ (block[i] >> 2) ^ 1910210284Sjmallett (block[i] >> 3) ^ (block[i] >> 4) ^ (block[i] >> 5) ^ 1911210284Sjmallett (block[i] >> 6) ^ (block[i] >> 7)) & 1) << 2; 1912210284Sjmallett /* PD1<3> */ 1913210284Sjmallett /* PD1<4> */ 1914210284Sjmallett for (i = 0; i < 256; i++) /* PD1<5> */ 1915210284Sjmallett pd1 ^= (((block[i] >> 1) ^ (block[i] >> 3) ^ (block[i] >> 5) ^ (block[i] >> 7)) & 1) << 5; 1916210284Sjmallett for (i = 0; i < 256; i++) /* PD1<6> */ 1917210284Sjmallett pd1 ^= (((block[i] >> 2) ^ (block[i] >> 3) ^ (block[i] >> 6) ^ (block[i] >> 7)) & 1) << 6; 1918210284Sjmallett for (i = 0; i < 256; i++) /* PD1<7> */ 1919210284Sjmallett pd1 ^= (((block[i] >> 4) ^ (block[i] >> 5) ^ (block[i] >> 6) ^ (block[i] >> 7)) & 1) << 7; 1920210284Sjmallett for (i = 0; i < 128; i++) /* PD2<0> */ 1921210284Sjmallett pd2 ^= ((block[2*i+1] ^ (block[2*i+1] >> 1) ^ (block[2*i+1] >> 2) ^ 1922210284Sjmallett (block[2*i+1] >> 3) ^ (block[2*i+1] >> 4) ^ (block[2*i+1] >> 5) ^ 1923210284Sjmallett (block[2*i+1] >> 6) ^ (block[2*i+1] >> 7)) & 1) << 0; 1924210284Sjmallett for (i = 0; i < 64; i++) /* PD2<1> */ 1925210284Sjmallett for (j = 2; j < 4; j++) 1926210284Sjmallett pd2 ^= ((block[4*i+j] ^ (block[4*i+j] >> 1) ^ (block[4*i+j] >> 2) ^ 1927210284Sjmallett (block[4*i+j] >> 3) ^ (block[4*i+j] >> 4) ^ (block[4*i+j] >> 5) ^ 1928210284Sjmallett (block[4*i+j] >> 6) ^ (block[4*i+j] >> 7)) & 1) << 1; 1929210284Sjmallett for (i = 0; i < 32; i++) /* PD2<2> */ 1930210284Sjmallett for (j = 4; j < 8; j++) 1931210284Sjmallett pd2 ^= ((block[8*i+j] ^ (block[8*i+j] >> 1) ^ (block[8*i+j] >> 2) ^ 1932210284Sjmallett (block[8*i+j] >> 3) ^ (block[8*i+j] >> 4) ^ (block[8*i+j] >> 5) ^ 1933210284Sjmallett (block[8*i+j] >> 6) ^ (block[8*i+j] >> 7)) & 1) << 2; 1934210284Sjmallett for (i = 0; i < 16; i++) /* PD2<3> */ 1935210284Sjmallett for (j = 8; j < 16; j++) 1936210284Sjmallett pd2 ^= ((block[16*i+j] ^ (block[16*i+j] >> 1) ^ (block[16*i+j] >> 2) ^ 1937210284Sjmallett (block[16*i+j] >> 3) ^ (block[16*i+j] >> 4) ^ (block[16*i+j] >> 5) ^ 1938210284Sjmallett (block[16*i+j] >> 6) ^ (block[16*i+j] >> 7)) & 1) << 3; 1939210284Sjmallett for (i = 0; i < 8; i++) /* PD2<4> */ 1940210284Sjmallett for (j = 16; j < 32; j++) 1941210284Sjmallett pd2 ^= ((block[32*i+j] ^ (block[32*i+j] >> 1) ^ (block[32*i+j] >> 2) ^ 1942210284Sjmallett (block[32*i+j] >> 3) ^ (block[32*i+j] >> 4) ^ (block[32*i+j] >> 5) ^ 1943210284Sjmallett (block[32*i+j] >> 6) ^ (block[32*i+j] >> 7)) & 1) << 4; 1944210284Sjmallett for (i = 0; i < 4; i++) /* PD2<5> */ 1945210284Sjmallett for (j = 32; j < 64; j++) 1946210284Sjmallett pd2 ^= ((block[64*i+j] ^ (block[64*i+j] >> 1) ^ (block[64*i+j] >> 2) ^ 1947210284Sjmallett (block[64*i+j] >> 3) ^ (block[64*i+j] >> 4) ^ (block[64*i+j] >> 5) ^ 1948210284Sjmallett (block[64*i+j] >> 6) ^ (block[64*i+j] >> 7)) & 1) << 5; 1949210284Sjmallett for (i = 0; i < 2; i++) /* PD2<6> */ 1950210284Sjmallett for (j = 64; j < 128; j++) 1951210284Sjmallett pd2 ^= ((block[128*i+j] ^ (block[128*i+j] >> 1) ^ (block[128*i+j] >> 2) ^ 1952210284Sjmallett (block[128*i+j] >> 3) ^ (block[128*i+j] >> 4) ^ (block[128*i+j] >> 5) ^ 1953210284Sjmallett (block[128*i+j] >> 6) ^ (block[128*i+j] >> 7)) & 1) << 6; 1954210284Sjmallett for (i = 128; i < 256; i++) /* PD2<7> */ 1955210284Sjmallett pd2 ^= ((block[i] ^ (block[i] >> 1) ^ (block[i] >> 2) ^ 1956210284Sjmallett (block[i] >> 3) ^ (block[i] >> 4) ^ (block[i] >> 5) ^ 1957210284Sjmallett (block[i] >> 6) ^ (block[i] >> 7)) & 1) << 7; 1958210284Sjmallett 1959210284Sjmallett xorpd0 = pd0 ^ block[256]; 1960210284Sjmallett xorpd1 = pd1 ^ block[257]; 1961210284Sjmallett xorpd2 = pd2 ^ block[258]; 1962210284Sjmallett 1963210284Sjmallett xor_num = __builtin_popcount((xorpd0 << 16) | (xorpd1 << 8) | xorpd2); 1964210284Sjmallett check = (((xorpd1 & 7) << 8) | xorpd0) ^ ((xorpd2 << 3) | (xorpd1 >> 5)); 1965210284Sjmallett 1966210284Sjmallett if (xor_num == 0) 1967210284Sjmallett return 0; 1968210284Sjmallett else if ((xor_num > 1) && (check != 0x7FF)) 1969210284Sjmallett return 2; 1970210284Sjmallett 1971210284Sjmallett if (check == 0x7FF) 1972210284Sjmallett { 1973210284Sjmallett /* Correct the error */ 1974210284Sjmallett block[xorpd2] ^= 1 << (xorpd1 >> 5); 1975210284Sjmallett } 1976210284Sjmallett 1977210284Sjmallett return 1; 1978210284Sjmallett} 1979215990Sjmallett 1980215990Sjmallettcvmx_nand_status_t cvmx_nand_set_defaults(int page_size, int oob_size, int pages_per_block, int blocks, int onfi_timing_mode) 1981215990Sjmallett{ 1982215990Sjmallett if (!page_size || !oob_size || !pages_per_block || !blocks || onfi_timing_mode > 5) 1983215990Sjmallett CVMX_NAND_RETURN(CVMX_NAND_INVALID_PARAM); 1984215990Sjmallett 1985215990Sjmallett cvmx_nand_default.page_size = page_size; 1986215990Sjmallett cvmx_nand_default.oob_size = oob_size; 1987215990Sjmallett cvmx_nand_default.pages_per_block = pages_per_block; 1988215990Sjmallett cvmx_nand_default.blocks = blocks; 1989215990Sjmallett cvmx_nand_default.onfi_timing = onfi_timing_mode; 1990215990Sjmallett 1991215990Sjmallett CVMX_NAND_RETURN(CVMX_NAND_SUCCESS); 1992215990Sjmallett} 1993