[S3C_NAND] Cleaner NAND code, WIP
[qemu/mini2440.git] / hw / s3c2440_nand.c
blobb48fcd907976d29b2e975e50bceb81d1353715a4
1 /*
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.
9 */
11 #include "s3c.h"
12 #include "hw.h"
14 struct s3c2440_nand_s {
15 struct s3c_nand_driver_s driver;
17 /* NAND Flash controller */
18 target_phys_addr_t nand_base;
19 struct nand_flash_s *nand;
20 uint16_t nfconf;
21 uint16_t nfcont;
22 uint8_t nfcmd;
23 uint32_t nfaddr;
24 struct ecc_state_s nfecc;
25 int nfwp;
28 extern struct s3c_state_s *g_s3c;
30 /* NAND Flash controller */
31 #define S3C_NFCONF 0x00 /* NAND Flash Configuration register */
33 #define S3C_NFCONT 0x04 /* NAND Flash Configuration register */
34 #define S3C_NFCONT_MODE (1 << 0) /* NAND flash controller operating mode */
35 #define S3C_NFCONT_CE (1 << 1) /* NAND Flash Memory nFCE signal control 0: active) */
36 #define S3C_NFCONT_INITECC (1 << 4) /* Initialize ECC decoder/encoder(Write-only) */
38 #define S3C_NFCMD 0x08 /* NAND Flash Command Set register */
39 #define S3C_NFADDR 0x0c /* NAND Flash Address Set register */
40 #define S3C_NFDATA 0x10 /* NAND Flash Data register */
41 #define S3C_NFSTAT 0x20 /* NAND Flash Operation Status register */
42 #define S3C_NFSTAT0 0x24 /* NAND Flash Operation Status register */
43 #define S3C_NFSTAT1 0x28 /* NAND Flash Operation Status register */
45 #define S3C_NFECC 0x14 /* NAND Flash ECC register */
46 #define S3C_NFECCD0 0x14 /* NAND Flash ECC register */
47 #define S3C_NFECCD1 0x18 /* NAND Flash ECC register */
48 #define S3C_NFECCD 0x1c /* NAND Flash ECC register */
50 static void s3c2440_nand_reset(void * opaque)
52 struct s3c2440_nand_s *s = (struct s3c2440_nand_s *)opaque;
53 s->nfconf = 0x1000;
54 s->nfcont = 0x0384;
55 s->nfcmd = 0;
56 s->nfaddr = 0;
57 ecc_reset(&s->nfecc);
60 static uint32_t s3c2440_nand_read(void *opaque, target_phys_addr_t addr)
62 struct s3c2440_nand_s *s = (struct s3c2440_nand_s *) opaque;
63 int rb, shr = 0;
64 if (!s->nand)
65 return 0;
67 // printf("%08x - %s: %02x\n", g_s3c->env->regs[15], __FUNCTION__, (unsigned long)addr);fflush(stdout);
69 switch (addr) {
70 case S3C_NFCONF:
71 return s->nfconf;
72 case S3C_NFCONT:
73 return s->nfcont;
74 case S3C_NFCMD:
75 return s->nfcmd;
76 case S3C_NFADDR:
77 return s->nfaddr >> 24; // last 8 bits poked
78 case S3C_NFDATA:
79 if (s->nfcont & S3C_NFCONT_MODE) {
80 uint32_t r1 = nand_getio(s->nand);
81 uint32_t res = ecc_digest(&s->nfecc, r1);
82 return res;
84 break;
85 case S3C_NFSTAT:
86 nand_getpins(s->nand, &rb);
87 return rb;
88 case S3C_NFECC + 2: shr += 8;
89 case S3C_NFECC + 1: shr += 8;
90 case S3C_NFECC:
91 #define ECC(shr, b, shl) ((s->nfecc.lp[b] << (shl - shr)) & (1 << shl))
92 return (~(
93 ECC(0, 1, 0) | ECC(0, 0, 1) | ECC(1, 1, 2) | ECC(1, 0, 3) |
94 ECC(2, 1, 4) | ECC(2, 0, 5) | ECC(3, 1, 6) | ECC(3, 0, 7) |
95 ECC(4, 1, 8) | ECC(4, 0, 9) | ECC(5, 1, 10) | ECC(5, 0, 11) |
96 ECC(6, 1, 12) | ECC(6, 0, 13) | ECC(7, 1, 14) | ECC(7, 0, 15) |
97 ECC(8, 1, 16) | ECC(8, 0, 17) | (s->nfecc.cp << 18)) >> shr) &
98 0xff;
99 #undef ECC
100 default:
101 printf("%s: Bad register 0x%lx\n", __FUNCTION__, (unsigned long)addr);
102 break;
104 return 0;
107 static void s3c2440_nand_write(void *opaque, target_phys_addr_t addr,
108 uint32_t value)
110 struct s3c2440_nand_s *s = (struct s3c2440_nand_s *) opaque;
111 if (!s->nand)
112 return;
114 //printf("%08x - %s: %02x = %lx\n", g_s3c->env->regs[15], __FUNCTION__, (unsigned long)addr, value);fflush(stdout);
116 switch (addr) {
117 case S3C_NFCONF:
118 s->nfconf = value & 0xffff;
119 break;
120 case S3C_NFCONT:
121 if (value & S3C_NFCONT_MODE)
122 ecc_reset(&s->nfecc);
123 s->nfcont = value & (0xffff ^ S3C_NFCONT_INITECC);
124 break;
125 case S3C_NFCMD:
126 s->nfcmd = value & 0xff;
127 if (s->nfcont & S3C_NFCONT_MODE) {
128 nand_setpins(s->nand, 1, 0, s->nfcont & S3C_NFCONT_CE, s->nfwp, 0);
129 nand_setio(s->nand, s->nfcmd);
130 nand_setpins(s->nand, 0, 0, s->nfcont & S3C_NFCONT_CE, s->nfwp, 0);
132 break;
133 case S3C_NFSTAT: // it's OK to write to this on the 2440
134 break;
135 case S3C_NFADDR:
136 s->nfaddr = (s->nfaddr >> 8) | (value << 24);
137 if (s->nfcont & S3C_NFCONT_MODE) {
138 nand_setpins(s->nand, 0, 1, s->nfcont & S3C_NFCONT_CE, s->nfwp, 0);
139 nand_setio(s->nand, value & 0xff);
140 nand_setpins(s->nand, 0, 0, s->nfcont & S3C_NFCONT_CE, s->nfwp, 0);
142 break;
143 case S3C_NFDATA:
144 if (s->nfcont & S3C_NFCONT_MODE)
145 nand_setio(s->nand, ecc_digest(&s->nfecc, value & 0xff));
146 break;
147 default:
148 printf("%s: Bad register 0x%lx=%x\n", __FUNCTION__, (unsigned long)addr, value);
152 static void s3c2440_nand_register(void * opaque, struct nand_flash_s *chip)
154 struct s3c2440_nand_s *s = (struct s3c2440_nand_s *) opaque;
155 s->nand = chip;
158 static void s3c2440_nand_setwp(void * opaque, int wp)
160 struct s3c2440_nand_s *s = (struct s3c2440_nand_s *) opaque;
161 s->nfwp = wp;
164 static CPUReadMemoryFunc *s3c2440_nand_readfn[] = {
165 s3c2440_nand_read,
166 s3c2440_nand_read,
167 s3c2440_nand_read,
170 static CPUWriteMemoryFunc *s3c2440_nand_writefn[] = {
171 s3c2440_nand_write,
172 s3c2440_nand_write,
173 s3c2440_nand_write,
176 static void s3c2440_nand_save(QEMUFile *f, void *opaque)
178 struct s3c2440_nand_s *s = (struct s3c2440_nand_s *) opaque;
179 qemu_put_be16s(f, &s->nfconf);
180 qemu_put_be16s(f, &s->nfcont);
181 qemu_put_8s(f, &s->nfcmd);
182 qemu_put_be32s(f, &s->nfaddr);
183 qemu_put_be32(f, s->nfwp);
184 ecc_put(f, &s->nfecc);
187 static int s3c2440_nand_load(QEMUFile *f, void *opaque, int version_id)
189 struct s3c2440_nand_s *s = (struct s3c2440_nand_s *) opaque;
190 qemu_get_be16s(f, &s->nfconf);
191 qemu_get_be16s(f, &s->nfcont);
192 qemu_get_8s(f, &s->nfcmd);
193 qemu_get_be32s(f, &s->nfaddr);
194 s->nfwp = qemu_get_be32(f);
195 ecc_get(f, &s->nfecc);
196 return 0;
199 static const struct s3c_nand_driver_s s3c2440_nand_driver = {
200 .reset = s3c2440_nand_reset,
201 .setwp = s3c2440_nand_setwp,
202 .reg = s3c2440_nand_register
205 struct s3c_nand_driver_s * s3c2440_nand_init(void)
207 int iomemtype;
208 struct s3c2440_nand_s *nand = (struct s3c2440_nand_s *)
209 qemu_mallocz(sizeof(struct s3c2440_nand_s));
210 nand->driver = s3c2440_nand_driver;
211 nand->nand_base = 0x4e000000;
212 nand->driver.reset(nand);
213 iomemtype = cpu_register_io_memory(0, s3c2440_nand_readfn, s3c2440_nand_writefn, nand);
214 cpu_register_physical_memory(nand->nand_base, 0xffffff, iomemtype);
215 register_savevm("s3c2440_nand", 0, 0, s3c2440_nand_save, s3c2440_nand_load, nand);
216 return &nand->driver;