[S3C2440] NAND support
[qemu/mini2440.git] / hw / s3c2440_nand.c
blob12c44e4d554ab207a1d8e2368c0d1391a965912f
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 uint32_t nfconf;
21 uint16_t nfcont;
22 uint8_t nfcmd;
23 uint32_t nfaddr;
24 struct ecc_state_s nfecc;
25 int nfwp;
28 /* NAND Flash controller */
29 #define S3C_NFCONF 0x00 /* NAND Flash Configuration register */
31 #define S3C_NFCONT 0x04 /* NAND Flash Configuration register */
32 #define S3C_NFCMD 0x08 /* NAND Flash Command Set register */
33 #define S3C_NFADDR 0x0c /* NAND Flash Address Set register */
34 #define S3C_NFDATA 0x10 /* NAND Flash Data register */
35 #define S3C_NFSTAT 0x20 /* NAND Flash Operation Status register */
36 #define S3C_NFSTAT0 0x24 /* NAND Flash Operation Status register */
37 #define S3C_NFSTAT1 0x28 /* NAND Flash Operation Status register */
39 #define S3C_NFECC 0x14 /* NAND Flash ECC register */
40 #define S3C_NFECCD0 0x14 /* NAND Flash ECC register */
41 #define S3C_NFECCD1 0x18 /* NAND Flash ECC register */
42 #define S3C_NFECCD 0x1c /* NAND Flash ECC register */
44 static void s3c2440_nand_reset(void * opaque)
46 struct s3c2440_nand_s *s = (struct s3c2440_nand_s *)opaque;
47 s->nfconf = 0x00001000;
48 s->nfcont = 0x0384;
49 s->nfcmd = 0;
50 s->nfaddr = 0;
51 ecc_reset(&s->nfecc);
54 static uint32_t s3c2440_nand_read(void *opaque, target_phys_addr_t addr)
56 struct s3c2440_nand_s *s = (struct s3c2440_nand_s *) opaque;
57 int rb, shr = 0;
58 if (!s->nand)
59 return 0;
61 switch (addr) {
62 case S3C_NFCONF:
63 return s->nfconf;
64 case S3C_NFCONT:
65 return s->nfcont;
66 case S3C_NFCMD:
67 return s->nfcmd;
68 case S3C_NFADDR:
69 return s->nfaddr;
70 case S3C_NFDATA:
71 if (s->nfcont & (1 << 0))
72 return ecc_digest(&s->nfecc, nand_getio(s->nand));
73 break;
74 case S3C_NFSTAT:
75 nand_getpins(s->nand, &rb);
76 return rb;
77 case S3C_NFECC + 2: shr += 8;
78 case S3C_NFECC + 1: shr += 8;
79 case S3C_NFECC:
80 #define ECC(shr, b, shl) ((s->nfecc.lp[b] << (shl - shr)) & (1 << shl))
81 return (~(
82 ECC(0, 1, 0) | ECC(0, 0, 1) | ECC(1, 1, 2) | ECC(1, 0, 3) |
83 ECC(2, 1, 4) | ECC(2, 0, 5) | ECC(3, 1, 6) | ECC(3, 0, 7) |
84 ECC(4, 1, 8) | ECC(4, 0, 9) | ECC(5, 1, 10) | ECC(5, 0, 11) |
85 ECC(6, 1, 12) | ECC(6, 0, 13) | ECC(7, 1, 14) | ECC(7, 0, 15) |
86 ECC(8, 1, 16) | ECC(8, 0, 17) | (s->nfecc.cp << 18)) >> shr) &
87 0xff;
88 #undef ECC
89 default:
90 printf("%s: Bad register 0x%lx\n", __FUNCTION__, (unsigned long)addr);
91 break;
93 return 0;
96 static void s3c2440_nand_write(void *opaque, target_phys_addr_t addr,
97 uint32_t value)
99 struct s3c2440_nand_s *s = (struct s3c2440_nand_s *) opaque;
100 if (!s->nand)
101 return;
103 switch (addr) {
104 case S3C_NFCONF:
105 s->nfconf = value & 0xffff;
106 break;
107 case S3C_NFCONT:
108 s->nfcont = value & 0xffff;
109 if (value & (1 << 4))
110 ecc_reset(&s->nfecc);
111 break;
112 case S3C_NFCMD:
113 s->nfcmd = value & 0xff;
114 if (s->nfcont & (1 << 0)) {
115 nand_setpins(s->nand, 1, 0, (s->nfconf >> 11) & 1, s->nfwp, 0);
116 nand_setio(s->nand, s->nfcmd);
117 nand_setpins(s->nand, 0, 0, (s->nfconf >> 11) & 1, s->nfwp, 0);
119 break;
120 case S3C_NFADDR:
121 s->nfaddr = value & 0xff;
122 if (s->nfcont & (1 << 0)) {
123 nand_setpins(s->nand, 0, 1, (s->nfconf >> 11) & 1, s->nfwp, 0);
124 nand_setio(s->nand, s->nfaddr);
125 nand_setpins(s->nand, 0, 0, (s->nfconf >> 11) & 1, s->nfwp, 0);
127 break;
128 case S3C_NFDATA:
129 if (s->nfcont & (1 << 0))
130 nand_setio(s->nand, ecc_digest(&s->nfecc, value & 0xff));
131 break;
132 default:
133 printf("%s: Bad register 0x%lx\n", __FUNCTION__, (unsigned long)addr);
137 static void s3c2440_nand_register(void * opaque, struct nand_flash_s *chip)
139 struct s3c2440_nand_s *s = (struct s3c2440_nand_s *) opaque;
140 s->nand = chip;
143 static void s3c2440_nand_setwp(void * opaque, int wp)
145 struct s3c2440_nand_s *s = (struct s3c2440_nand_s *) opaque;
146 s->nfwp = wp;
149 static CPUReadMemoryFunc *s3c2440_nand_readfn[] = {
150 s3c2440_nand_read,
151 s3c2440_nand_read,
152 s3c2440_nand_read,
155 static CPUWriteMemoryFunc *s3c2440_nand_writefn[] = {
156 s3c2440_nand_write,
157 s3c2440_nand_write,
158 s3c2440_nand_write,
161 static void s3c2440_nand_save(QEMUFile *f, void *opaque)
163 struct s3c2440_nand_s *s = (struct s3c2440_nand_s *) opaque;
164 qemu_put_be16s(f, &s->nfconf);
165 qemu_put_8s(f, &s->nfcmd);
166 qemu_put_8s(f, &s->nfaddr);
167 qemu_put_be32(f, s->nfwp);
168 ecc_put(f, &s->nfecc);
171 static int s3c2440_nand_load(QEMUFile *f, void *opaque, int version_id)
173 struct s3c2440_nand_s *s = (struct s3c2440_nand_s *) opaque;
174 qemu_get_be16s(f, &s->nfconf);
175 qemu_get_8s(f, &s->nfcmd);
176 qemu_get_8s(f, &s->nfaddr);
177 s->nfwp = qemu_get_be32(f);
178 ecc_get(f, &s->nfecc);
179 return 0;
182 static const struct s3c_nand_driver_s s3c2440_nand_driver = {
183 .reset = s3c2440_nand_reset,
184 .setwp = s3c2440_nand_setwp,
185 .reg = s3c2440_nand_register
188 struct s3c_nand_driver_s * s3c2440_nand_init(void)
190 int iomemtype;
191 struct s3c2440_nand_s *nand = (struct s3c2440_nand_s *)
192 qemu_mallocz(sizeof(struct s3c2440_nand_s));
193 nand->driver = s3c2440_nand_driver;
194 nand->nand_base = 0x4e000000;
195 nand->driver.reset(nand);
196 iomemtype = cpu_register_io_memory(0, s3c2440_nand_readfn, s3c2440_nand_writefn, nand);
197 cpu_register_physical_memory(nand->nand_base, 0xffffff, iomemtype);
198 register_savevm("s3c2440_nand", 0, 0, s3c2440_nand_save, s3c2440_nand_load, nand);
199 return &nand->driver;