2 * Samsung S3C2410A RISC Microprocessor support (ARM920T based SoC).
4 * Copyright (c) 2007 OpenMoko, Inc.
5 * Author: Andrzej Zaborowski <andrew@openedhand.com>
6 * With: Michel Pollet <buserror@gmail.com>
8 * This code is licenced under the GNU GPL v2.
14 struct s3c2440_nand_s
{
15 struct s3c_nand_driver_s driver
;
17 /* NAND Flash controller */
18 target_phys_addr_t nand_base
;
26 ECCState nfsecc
; /* spare area */
35 extern struct s3c_state_s
*g_s3c
;
37 /* NAND Flash controller */
38 #define S3C_NFCONF 0x00 /* NAND Flash Configuration register */
40 #define S3C_NFCONT 0x04 /* NAND Flash Configuration register */
41 #define S3C_NFCONT_MODE (1 << 0) /* NAND flash controller operating mode */
42 #define S3C_NFCONT_CE (1 << 1) /* NAND Flash Memory nFCE signal control 0: active) */
43 #define S3C_NFCONT_INITECC (1 << 4) /* Initialize ECC decoder/encoder(Write-only) */
44 #define S3C_NFCONT_MECCL (1 << 5) /* Lock Main data area ECC generation */
45 #define S3C_NFCONT_SECCL (1 << 6) /* Lock spare area ECC generation */
46 #define S3C_NFCONT_SLOCK (1 << 12) /* Soft Lock configuration */
47 #define S3C_NFCONT_TLOCK (1 << 13) /* Lock-tight configuration */
48 #define S3C_NFCONT_LOCK (S3C_NFCONT_SLOCK | S3C_NFCONT_TLOCK)
50 #define S3C_NFCMD 0x08 /* NAND Flash Command Set register */
51 #define S3C_NFADDR 0x0c /* NAND Flash Address Set register */
52 #define S3C_NFDATA 0x10 /* NAND Flash Data register */
54 #define S3C_NFMECCD0 0x14 /* NAND Flash ECC register : 1st and 2d ecc*/
55 #define S3C_NFMECCD1 0x18 /* NAND Flash ECC register : 3rd and 4th (unimplemented) */
56 #define S3C_NFSECCD 0x1c /* NAND Flash ECC register Spare Area */
58 #define S3C_NFSTAT 0x20 /* NAND Flash Operation Status register */
60 #define S3C_NFESTAT0 0x24 /* NAND flash ECC Status register for I/O [7:0] (unimplemented)*/
61 #define S3C_NFESTAT1 0x28 /* NAND flash ECC Status register for I/O [15:8] (unimplemented)*/
63 #define S3C_NFMECC0 0x2c /* NAND flash ECC register for data[7:0] (unimplemented)*/
64 #define S3C_NFMECC1 0x30 /* NAND flash ECC register for data[15:8] (unimplemented)*/
65 #define S3C_NFSECC 0x34 /* NAND flash ECC register for I/O [15:0] (spare area) (unimplemented)*/
67 #define S3C_NFSBLK 0x38 /* NAND flash programmable start block address (unimplemented)*/
68 #define S3C_NFEBLK 0x3c /* NAND flash programmable end block address (unimplemented)*/
70 static void s3c2440_nand_reset(void * opaque
)
72 struct s3c2440_nand_s
*s
= (struct s3c2440_nand_s
*)opaque
;
74 (0 << 3) | /* NCON0: not an advanced flash */
75 (1 << 2) | /* GPG13: 0: 256 Word/page, 1: 512 Bytes/page */
76 (1 << 1) | /* GPG14: 0: 3 address cycle 1: 4 address cycle */
77 (0 << 2) /* GPG15: 0: 8-bit bus 1: 16-bit bus */
85 ecc_reset(&s
->nfsecc
);
88 static uint8_t dbu
[16],cmd
;
90 static uint32_t s3c2440_nand_read(void *opaque
, target_phys_addr_t addr
)
92 struct s3c2440_nand_s
*s
= (struct s3c2440_nand_s
*) opaque
;
105 return s
->nfaddr
>> 24; // last 8 bits poked
107 if (s
->nfcont
& S3C_NFCONT_MODE
) {
108 uint32_t value
= nand_getio(s
->nand
);
110 if (s
->nfaddr_cur
< 512) {
111 if (!(s
->nfcont
& S3C_NFCONT_MECCL
)) {
112 value
= ecc_digest(&s
->nfecc
, value
& 0xff);
113 /* printf("ecc %02x -> %08x %08x cp %08x cnt %d\n", value, s->nfecc.lp[0], s->nfecc.lp[1], s->nfecc.cp, s->nfecc.count); */
116 if (!(s
->nfcont
& S3C_NFCONT_SECCL
))
117 value
= ecc_digest(&s
->nfsecc
, value
& 0xff);
119 if (s
->nfaddr_cur
< 16) dbu
[s
->nfaddr_cur
] = value
;
125 nand_getpins(s
->nand
, &rb
);
127 case S3C_NFMECCD0
+ 3: shr
+= 8;
128 case S3C_NFMECCD0
+ 2: shr
+= 8;
129 case S3C_NFMECCD0
+ 1: shr
+= 8;
131 #define ECC(shr, b, shl) ((s->nfecc.lp[b] << (shl - shr)) & (1 << shl))
133 ECC(0, 1, 0) | ECC(0, 0, 1) | ECC(1, 1, 2) | ECC(1, 0, 3) | ECC(2, 1, 4) | ECC(2, 0, 5) | ECC(3, 1, 6) | ECC(3, 0, 7) |
134 ECC(4, 1, 8) | ECC(4, 0, 9) | ECC(5, 1, 10) | ECC(5, 0, 11) | ECC(6, 1, 12) | ECC(6, 0, 13) | ECC(7, 1, 14) | ECC(7, 0, 15) |
135 ECC(8, 1, 16) | ECC(8, 0, 17) | ((s
->nfecc
.cp
& 0x3f) << 18) |
136 ECC(9, 1, 28) | ECC(9, 0, 29) | ECC(10, 1, 30) | ECC(10, 0, 31)
138 /* printf("Read ECC %02x = %08x >> %d [ 0: %08x 1: %08x cnt %d ]\n", addr, ecc, shr, s->nfecc.lp[0], s->nfecc.lp[1], s->nfecc.count); */
139 ecc
= (ecc
>> shr
) & 0xff;
142 case S3C_NFMECCD1
+ 3:
143 case S3C_NFMECCD1
+ 2:
144 case S3C_NFMECCD1
+ 1:
146 /* the ecc digester is limited to 2, the s3c2440 can do 4... */
147 printf("%s: Bad register S3C_NFECCD1 NOT HANDLED\n", __FUNCTION__
);
151 case S3C_NFSECCD
+ 3: shr
+= 8;
152 case S3C_NFSECCD
+ 2: shr
+= 8;
153 case S3C_NFSECCD
+ 1: shr
+= 8;
155 #define ECC(shr, b, shl) ((s->nfsecc.lp[b] << (shl - shr)) & (1 << shl))
157 ECC(0, 1, 0) | ECC(0, 0, 1) | ECC(1, 1, 2) | ECC(1, 0, 3) | ECC(2, 1, 4) | ECC(2, 0, 5) | ECC(3, 1, 6) | ECC(3, 0, 7) |
158 ECC(4, 1, 8) | ECC(4, 0, 9) | ECC(5, 1, 10) | ECC(5, 0, 11) | ECC(6, 1, 12) | ECC(6, 0, 13) | ECC(7, 1, 14) | ECC(7, 0, 15) |
159 ECC(8, 1, 16) | ECC(8, 0, 17) | ((s
->nfecc
.cp
& 0x3f) << 18) |
160 ECC(9, 1, 28) | ECC(9, 0, 29) | ECC(10, 1, 30) | ECC(10, 0, 31)
171 printf("%s: Bad register 0x%lx\n", __FUNCTION__
, (unsigned long)addr
);
178 * 16 and 32 bits access to NFDATA. u-boot uses the 16 bits acess, and the kernel uses
179 * the 32 bits one extensively.
181 static uint32_t s3c2440_nand_read16(void *opaque
, target_phys_addr_t addr
)
183 uint32_t res
= s3c2440_nand_read(opaque
, addr
);
184 if (addr
== S3C_NFDATA
)
185 res
= (s3c2440_nand_read(opaque
, addr
) << 8) | (res
& 0xff);
189 static uint32_t s3c2440_nand_read32(void *opaque
, target_phys_addr_t addr
)
191 uint32_t res
= s3c2440_nand_read16(opaque
, addr
);
193 if (addr
== S3C_NFDATA
) {
194 res
= (s3c2440_nand_read16(opaque
, addr
) << 16) | (res
& 0xffff);
199 static void s3c2440_nand_write(void *opaque
, target_phys_addr_t addr
,
202 struct s3c2440_nand_s
*s
= (struct s3c2440_nand_s
*) opaque
;
206 static int did_write
=0;
210 s
->nfconf
= (s
->nfconf
& 0xe) | (value
& 0xfff1);
213 if (value
& S3C_NFCONT_INITECC
) {
214 ecc_reset(&s
->nfecc
);
215 ecc_reset(&s
->nfsecc
);
217 /* tight lock is sticky */
218 s
->nfcont
= (value
& (0xffff ^ S3C_NFCONT_INITECC
)) | (s
->nfcont
& S3C_NFCONT_TLOCK
);
221 s
->nfcmd
= value
& 0xff;
222 if (s
->nfcont
& S3C_NFCONT_MODE
) {
223 nand_setpins(s
->nand
, 1, 0, s
->nfcont
& S3C_NFCONT_CE
, s
->nfwp
, 0);
224 nand_setio(s
->nand
, s
->nfcmd
);
225 nand_setpins(s
->nand
, 0, 0, s
->nfcont
& S3C_NFCONT_CE
, s
->nfwp
, 0);
228 case S3C_NFSTAT
: // it's OK to write to this on the 2440
234 printf("%08x cmd %02x : %s %d bytes : ", s
->nfaddr
<<1, cmd
, did_write
? "wrote" : "read", s
->nfaddr_cur
);
235 for (i
=0; i
< 16 && i
< s
->nfaddr_cur
; i
++) printf("%02x ", dbu
[i
]);
242 s
->nfaddr
= (s
->nfaddr
>> 8) | (value
<< 24);
244 if (s
->nfcont
& S3C_NFCONT_MODE
) {
245 nand_setpins(s
->nand
, 0, 1, s
->nfcont
& S3C_NFCONT_CE
, s
->nfwp
, 0);
246 nand_setio(s
->nand
, value
& 0xff);
247 nand_setpins(s
->nand
, 0, 0, s
->nfcont
& S3C_NFCONT_CE
, s
->nfwp
, 0);
251 if (s
->nfcont
& S3C_NFCONT_MODE
) {
252 if (s
->nfcont
& S3C_NFCONT_LOCK
) {
253 if (s
->nfaddr_cur
< (s
->nfsblk
<< 6) ||
254 s
->nfaddr_cur
> (s
->nfeblk
<< 6)) {
259 if (s
->nfaddr_cur
< 512) {
260 if (!(s
->nfcont
& S3C_NFCONT_MECCL
)) {
261 value
= ecc_digest(&s
->nfecc
, value
& 0xff);
262 /* printf("ecc %02x -> %08x %08x cp %08x cnt %d\n", value, s->nfecc.lp[0], s->nfecc.lp[1], s->nfecc.cp, s->nfecc.count); */
265 if (!(s
->nfcont
& S3C_NFCONT_SECCL
))
266 value
= ecc_digest(&s
->nfsecc
, value
& 0xff);
269 if (s
->nfaddr_cur
< 16) dbu
[s
->nfaddr_cur
] = value
;
271 nand_setio(s
->nand
, value
& 0xff);
275 s
->nfsblk
= value
& 0xffffff;
276 /* printf("%s: S3C_NFSBLK set to 0x%x\n", __FUNCTION__, value); */
279 s
->nfeblk
= value
& 0xffffff;
280 /* printf("%s: S3C_NFEBLK set to 0x%x\n", __FUNCTION__, value); */
284 printf("%s: Bad register 0x%lx=%x\n", __FUNCTION__
, (unsigned long)addr
, value
);
289 * 16 and 32 bits access to NFDATA. u-boot uses the 16 bits acess, and the kernel uses
290 * the 32 bits one extensively.
292 static void s3c2440_nand_write16(void *opaque
, target_phys_addr_t addr
,
295 s3c2440_nand_write(opaque
, addr
, value
);
296 if (addr
== S3C_NFDATA
) {
297 s3c2440_nand_write(opaque
, addr
, value
>> 8);
301 static void s3c2440_nand_write32(void *opaque
, target_phys_addr_t addr
,
304 s3c2440_nand_write16(opaque
, addr
, value
);
305 if (addr
== S3C_NFDATA
) {
306 s3c2440_nand_write16(opaque
, addr
, value
>> 16);
310 static void s3c2440_nand_register(void * opaque
, NANDFlashState
*chip
)
312 struct s3c2440_nand_s
*s
= (struct s3c2440_nand_s
*) opaque
;
316 static void s3c2440_nand_setwp(void * opaque
, int wp
)
318 struct s3c2440_nand_s
*s
= (struct s3c2440_nand_s
*) opaque
;
322 static CPUReadMemoryFunc
*s3c2440_nand_readfn
[] = {
328 static CPUWriteMemoryFunc
*s3c2440_nand_writefn
[] = {
330 s3c2440_nand_write16
,
331 s3c2440_nand_write32
,
334 static void s3c2440_nand_save(QEMUFile
*f
, void *opaque
)
336 struct s3c2440_nand_s
*s
= (struct s3c2440_nand_s
*) opaque
;
337 qemu_put_be16s(f
, &s
->nfconf
);
338 qemu_put_be16s(f
, &s
->nfcont
);
339 qemu_put_8s(f
, &s
->nfcmd
);
340 qemu_put_be32s(f
, &s
->nfaddr
);
341 qemu_put_be32(f
, s
->nfwp
);
342 ecc_put(f
, &s
->nfecc
);
345 static int s3c2440_nand_load(QEMUFile
*f
, void *opaque
, int version_id
)
347 struct s3c2440_nand_s
*s
= (struct s3c2440_nand_s
*) opaque
;
348 qemu_get_be16s(f
, &s
->nfconf
);
349 qemu_get_be16s(f
, &s
->nfcont
);
350 qemu_get_8s(f
, &s
->nfcmd
);
351 qemu_get_be32s(f
, &s
->nfaddr
);
352 s
->nfwp
= qemu_get_be32(f
);
353 ecc_get(f
, &s
->nfecc
);
357 static const struct s3c_nand_driver_s s3c2440_nand_driver
= {
358 .reset
= s3c2440_nand_reset
,
359 .setwp
= s3c2440_nand_setwp
,
360 .reg
= s3c2440_nand_register
363 struct s3c_nand_driver_s
* s3c2440_nand_init(void)
366 struct s3c2440_nand_s
*nand
= (struct s3c2440_nand_s
*)
367 qemu_mallocz(sizeof(struct s3c2440_nand_s
));
368 nand
->driver
= s3c2440_nand_driver
;
369 nand
->nand_base
= 0x4e000000;
370 nand
->driver
.reset(nand
);
371 iomemtype
= cpu_register_io_memory(0, s3c2440_nand_readfn
, s3c2440_nand_writefn
, nand
);
372 cpu_register_physical_memory(nand
->nand_base
, 0xffffff, iomemtype
);
373 register_savevm("s3c2440_nand", 0, 0, s3c2440_nand_save
, s3c2440_nand_load
, nand
);
374 return &nand
->driver
;