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 *)&param_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