Root/qiboot/src/cpu/s3c2442/nand_read.c

1/*
2 * nand_read.c: Simple NAND read functions for booting from NAND
3 *
4 * This is used by cpu/arm920/start.S assembler code,
5 * and the board-specific linker script must make sure this
6 * file is linked within the first 4kB of NAND flash.
7 *
8 * Taken from GPLv2 licensed vivi bootloader,
9 * Copyright (C) 2002 MIZI Research, Inc.
10 *
11 * Author: Hwang, Chideok <hwang@mizi.com>
12 * Date : $Date: 2004/02/04 10:37:37 $
13 *
14 * u-boot integration and bad-block skipping (C) 2006 by OpenMoko, Inc.
15 * Author: Harald Welte <laforge@openmoko.org>
16 */
17
18/* NOTE this stuff runs in steppingstone context! */
19
20/* the API refers to 512-byte blocks */
21
22#include <qi.h>
23#include "nand_read.h"
24
25#define NAND_CMD_READ0 0
26#define NAND_CMD_READSTART 0x30
27
28#define __REGb(x) (*(volatile unsigned char *)(x))
29#define __REGw(x) (*(volatile unsigned short *)(x))
30#define __REGi(x) (*(volatile unsigned int *)(x))
31#define NF_BASE 0x4e000000
32#define NFCONF __REGi(NF_BASE + 0x0)
33#define NFCONT __REGi(NF_BASE + 0x4)
34#define NFCMD __REGb(NF_BASE + 0x8)
35#define NFADDR __REGb(NF_BASE + 0xc)
36#define NFDATA __REGb(NF_BASE + 0x10)
37#define NFDATA16 __REGw(NF_BASE + 0x10)
38#define NFSTAT __REGb(NF_BASE + 0x20)
39#define NFSTAT_BUSY 1
40#define nand_select() (NFCONT &= ~(1 << 1))
41#define nand_deselect() (NFCONT |= (1 << 1))
42#define nand_clear_RnB() (NFSTAT |= (1 << 2))
43
44static inline void nand_wait(void)
45{
46    int i;
47
48    while (!(NFSTAT & NFSTAT_BUSY))
49        for (i=0; i<10; i++);
50}
51
52/* configuration for 2440 with 2048byte sized flash */
53#define NAND_5_ADDR_CYCLE
54#define NAND_PAGE_SIZE 2048
55#define BAD_BLOCK_OFFSET NAND_PAGE_SIZE
56#define NAND_BLOCK_MASK (NAND_PAGE_SIZE - 1)
57#define NAND_BLOCK_SIZE (NAND_PAGE_SIZE * 64)
58
59int s3c2442_nand_is_bad_block(unsigned long block_index)
60{
61    unsigned char data;
62    unsigned long page_num;
63
64    nand_select();
65    nand_clear_RnB();
66    page_num = block_index >> 2; /* addr / 2048 */
67    NFCMD = NAND_CMD_READ0;
68    NFADDR = BAD_BLOCK_OFFSET & 0xff;
69    NFADDR = (BAD_BLOCK_OFFSET >> 8) & 0xff;
70    NFADDR = page_num & 0xff;
71    NFADDR = (page_num >> 8) & 0xff;
72    NFADDR = (page_num >> 16) & 0xff;
73    NFCMD = NAND_CMD_READSTART;
74    nand_wait();
75    data = (NFDATA & 0xff);
76
77    if (data != 0xff)
78        return 1;
79
80    return 0;
81}
82
83static int nand_read_page_ll(unsigned char *buf, unsigned long block512, int blocks512)
84{
85    unsigned short *ptr16 = (unsigned short *)buf;
86    unsigned int i, page_num;
87    unsigned int block_amount;
88    int blocks_possible = (3 - (block512 & 3)) + 1;
89
90
91    if (blocks512 > blocks_possible)
92        blocks512 = blocks_possible;
93
94    block_amount = (NAND_PAGE_SIZE / 4 / 2) * blocks512;
95
96    nand_clear_RnB();
97
98    NFCMD = NAND_CMD_READ0;
99
100    page_num = block512 >> 2; /* 512 block -> 2048 block */
101    /* Write Address */
102    NFADDR = 0;
103    NFADDR = (block512 & 3) << 1; /* which 512 block in 2048 */
104    NFADDR = page_num & 0xff;
105    NFADDR = (page_num >> 8) & 0xff;
106    NFADDR = (page_num >> 16) & 0xff;
107    NFCMD = NAND_CMD_READSTART;
108    nand_wait();
109
110    for (i = 0; i < block_amount; i++)
111        *ptr16++ = NFDATA16;
112
113    return blocks512;
114}
115
116
117/* low level nand read function */
118int nand_read_ll(unsigned char *buf, unsigned long start_block512,
119                                  int blocks512)
120{
121    int i, j;
122
123    /* chip Enable */
124    nand_select();
125    nand_clear_RnB();
126
127    for (i = 0; i < 10; i++)
128        ;
129
130    while (blocks512 > 0) {
131        if (s3c2442_nand_is_bad_block(start_block512)) {
132            start_block512 += 4;
133            if (start_block512 >> 2 > BAD_BLOCK_OFFSET)
134                /* end of NAND */
135                return -1;
136            continue;
137        }
138
139        j = nand_read_page_ll(buf, start_block512, blocks512);
140        start_block512 += j;
141        buf += j << 9;
142        blocks512 -= j;
143
144        if (this_board->get_ui_keys)
145            if ((this_board->get_ui_keys)() & UI_ACTION_SKIPKERNEL) {
146                puts(" ** skipping \n");
147                return -3;
148            }
149    }
150
151    /* chip Disable */
152    nand_deselect();
153
154    return 0;
155}
156
157

Archive Download this file



interactive