|
1 /* |
|
2 * Syborg NAND flash controller |
|
3 * |
|
4 * Copyright (c) 2009 CodeSourcery |
|
5 * |
|
6 * Permission is hereby granted, free of charge, to any person obtaining a copy |
|
7 * of this software and associated documentation files (the "Software"), to deal |
|
8 * in the Software without restriction, including without limitation the rights |
|
9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
|
10 * copies of the Software, and to permit persons to whom the Software is |
|
11 * furnished to do so, subject to the following conditions: |
|
12 * |
|
13 * The above copyright notice and this permission notice shall be included in |
|
14 * all copies or substantial portions of the Software. |
|
15 * |
|
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
|
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
|
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
|
19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
|
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
|
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
|
22 * THE SOFTWARE. |
|
23 */ |
|
24 |
|
25 #include "hw.h" |
|
26 #include "syborg.h" |
|
27 #include "devtree.h" |
|
28 #include "block.h" |
|
29 #include "flash.h" |
|
30 |
|
31 //#define DEBUG_SYBORG_NAND |
|
32 |
|
33 #ifdef DEBUG_SYBORG_NAND |
|
34 #define DPRINTF(fmt, args...) \ |
|
35 do { printf("syborg_nand: " fmt , ##args); } while (0) |
|
36 #define BADF(fmt, args...) \ |
|
37 do { fprintf(stderr, "syborg_nand: error: " fmt , ##args); exit(1);} while (0) |
|
38 #else |
|
39 #define DPRINTF(fmt, args...) do {} while(0) |
|
40 #define BADF(fmt, args...) \ |
|
41 do { fprintf(stderr, "syborg_nand: error: " fmt , ##args);} while (0) |
|
42 #endif |
|
43 |
|
44 enum { |
|
45 SNAND_ID = 0, |
|
46 SNAND_DATA = 1, |
|
47 SNAND_CTL = 2, |
|
48 SNAND_ECC_COUNT = 3, |
|
49 SNAND_ECC_CP = 4, |
|
50 SNAND_ECC_LP = 5 |
|
51 }; |
|
52 |
|
53 #define SNAND_CTL_CLE 0x01 |
|
54 #define SNAND_CTL_ALE 0x02 |
|
55 #define SNAND_CTL_CE 0x04 |
|
56 #define SNAND_CTL_WP 0x08 |
|
57 #define SNAND_CTL_RB 0x10 |
|
58 #define SNAND_CTL_IN_MASK \ |
|
59 (SNAND_CTL_CLE | SNAND_CTL_ALE | SNAND_CTL_CE | SNAND_CTL_WP) |
|
60 |
|
61 typedef struct { |
|
62 QEMUDevice *qdev; |
|
63 struct nand_flash_s *nand; |
|
64 struct ecc_state_s ecc; |
|
65 uint32_t ctl; |
|
66 } syborg_nand_state; |
|
67 |
|
68 static uint32_t syborg_nand_ecc_lp(syborg_nand_state *s) |
|
69 { |
|
70 uint32_t v0; |
|
71 uint32_t v1; |
|
72 |
|
73 v0 = s->ecc.lp[0]; |
|
74 v1 = s->ecc.lp[1]; |
|
75 |
|
76 #define ODD(n) ((v0 << n) & (1 << (2 * n))) |
|
77 #define EVEN(n) ((v1 << (n + 1)) & (1 << (2 * n + 1))) |
|
78 /* Interleave parity bits. */ |
|
79 return ODD( 0) | ODD( 1) | ODD( 2) | ODD( 3) |
|
80 | ODD( 4) | ODD( 5) | ODD( 6) | ODD( 7) |
|
81 | ODD( 8) | ODD( 9) | ODD(10) | ODD(11) |
|
82 | ODD(12) | ODD(13) | ODD(14) | ODD(15) |
|
83 | EVEN( 0) | EVEN( 1) | EVEN( 2) | EVEN( 3) |
|
84 | EVEN( 4) | EVEN( 5) | EVEN( 6) | EVEN( 7) |
|
85 | EVEN( 8) | EVEN( 9) | EVEN(10) | EVEN(11) |
|
86 | EVEN(12) | EVEN(13) | EVEN(14) | EVEN(15); |
|
87 #undef ODD |
|
88 #undef EVEN |
|
89 } |
|
90 |
|
91 static uint32_t syborg_nand_read(void *opaque, target_phys_addr_t offset) |
|
92 { |
|
93 syborg_nand_state *s = (syborg_nand_state *)opaque; |
|
94 |
|
95 offset &= 0xfff; |
|
96 DPRINTF("read 0x%x\n", (int)offset); |
|
97 switch(offset >> 2) { |
|
98 case SNAND_ID: |
|
99 return SYBORG_ID_NAND; |
|
100 case SNAND_DATA: |
|
101 return ecc_digest(&s->ecc, nand_getio(s->nand)); |
|
102 case SNAND_CTL: |
|
103 { |
|
104 int rb; |
|
105 nand_getpins(s->nand, &rb); |
|
106 return s->ctl | (rb ? SNAND_CTL_RB : 0); |
|
107 } |
|
108 case SNAND_ECC_COUNT: |
|
109 return s->ecc.count; |
|
110 case SNAND_ECC_CP: |
|
111 return s->ecc.cp; |
|
112 case SNAND_ECC_LP: |
|
113 return syborg_nand_ecc_lp(s); |
|
114 default: |
|
115 BADF("Bad read offset 0x%x\n", (int)offset); |
|
116 return 0; |
|
117 } |
|
118 } |
|
119 |
|
120 static void syborg_nand_write(void *opaque, target_phys_addr_t offset, |
|
121 uint32_t value) |
|
122 { |
|
123 syborg_nand_state *s = (syborg_nand_state *)opaque; |
|
124 |
|
125 offset &= 0xfff; |
|
126 DPRINTF("Write 0x%x=0x%x\n", (int)offset, value); |
|
127 switch (offset >> 2) { |
|
128 case SNAND_DATA: |
|
129 nand_setio(s->nand, ecc_digest(&s->ecc, value & 0xff)); |
|
130 break; |
|
131 case SNAND_CTL: |
|
132 s->ctl = value & SNAND_CTL_IN_MASK; |
|
133 nand_setpins(s->nand, |
|
134 (value & SNAND_CTL_CLE) != 0, |
|
135 (value & SNAND_CTL_ALE) != 0, |
|
136 (value & SNAND_CTL_CE) != 0, |
|
137 (value & SNAND_CTL_WP) != 0, |
|
138 0); |
|
139 break; |
|
140 case SNAND_ECC_COUNT: |
|
141 if (value != 0) { |
|
142 BADF("Bad write to ECC count\n"); |
|
143 } |
|
144 ecc_reset(&s->ecc); |
|
145 break; |
|
146 default: |
|
147 BADF("Bad write offset 0x%x\n", (int)offset); |
|
148 break; |
|
149 } |
|
150 } |
|
151 |
|
152 static CPUReadMemoryFunc *syborg_nand_readfn[] = { |
|
153 syborg_nand_read, |
|
154 syborg_nand_read, |
|
155 syborg_nand_read |
|
156 }; |
|
157 |
|
158 static CPUWriteMemoryFunc *syborg_nand_writefn[] = { |
|
159 syborg_nand_write, |
|
160 syborg_nand_write, |
|
161 syborg_nand_write |
|
162 }; |
|
163 |
|
164 static void syborg_nand_save(QEMUFile *f, void *opaque) |
|
165 { |
|
166 syborg_nand_state *s = opaque; |
|
167 |
|
168 qemu_put_be32(f, s->ctl); |
|
169 ecc_put(f, &s->ecc); |
|
170 } |
|
171 |
|
172 static int syborg_nand_load(QEMUFile *f, void *opaque, int version_id) |
|
173 { |
|
174 syborg_nand_state *s = opaque; |
|
175 |
|
176 if (version_id != 1) |
|
177 return -EINVAL; |
|
178 |
|
179 s->ctl = qemu_get_be32(f); |
|
180 ecc_get(f, &s->ecc); |
|
181 |
|
182 return 0; |
|
183 } |
|
184 |
|
185 static const struct |
|
186 { |
|
187 int size; |
|
188 int id; |
|
189 } nand_chips[] = |
|
190 { |
|
191 {1, 0x6e}, |
|
192 {2, 0x64}, |
|
193 {4, 0x6b}, |
|
194 {8, 0xd6}, |
|
195 {16, 0x33}, |
|
196 {32, 0x35}, |
|
197 {64, 0x36}, |
|
198 {128, 0x78}, |
|
199 {256, 0x71}, |
|
200 {512, 0xa2}, |
|
201 {1024, 0xa1}, |
|
202 {2048, 0xaa}, |
|
203 {4096, 0xac}, |
|
204 {8192, 0xa3}, |
|
205 {16384, 0xa5}, |
|
206 {0, 0} |
|
207 }; |
|
208 |
|
209 static void syborg_nand_create(QEMUDevice *dev) |
|
210 { |
|
211 int n; |
|
212 int size; |
|
213 syborg_nand_state *s; |
|
214 s = (syborg_nand_state *)qemu_mallocz(sizeof(syborg_nand_state)); |
|
215 s->qdev = dev; |
|
216 qdev_set_opaque(dev, s); |
|
217 |
|
218 size = qdev_get_property_int(dev, "size"); |
|
219 n = 0; |
|
220 while (nand_chips[n].size && nand_chips[n].size != size) { |
|
221 n++; |
|
222 } |
|
223 if (nand_chips[n].size == 0) { |
|
224 BADF("Unsuppored chip size: %d\n", size); |
|
225 exit(1); |
|
226 } |
|
227 s->nand = nand_init(NAND_MFR_SAMSUNG, nand_chips[n].id); |
|
228 ecc_reset(&s->ecc); |
|
229 } |
|
230 |
|
231 void syborg_nand_register(void) |
|
232 { |
|
233 QEMUDeviceClass *dc; |
|
234 dc = qdev_new("syborg,nand", syborg_nand_create, 0); |
|
235 qdev_add_registers(dc, syborg_nand_readfn, syborg_nand_writefn, 0x1000); |
|
236 qdev_add_property_int(dc, "size", 0); |
|
237 qdev_add_savevm(dc, 1, syborg_nand_save, syborg_nand_load); |
|
238 } |