diff options
Diffstat (limited to 'firmware/target/mips/ingenic_x1000/nand-x1000.c')
-rw-r--r-- | firmware/target/mips/ingenic_x1000/nand-x1000.c | 665 |
1 files changed, 321 insertions, 344 deletions
diff --git a/firmware/target/mips/ingenic_x1000/nand-x1000.c b/firmware/target/mips/ingenic_x1000/nand-x1000.c index fc8d471559..1770324fb3 100644 --- a/firmware/target/mips/ingenic_x1000/nand-x1000.c +++ b/firmware/target/mips/ingenic_x1000/nand-x1000.c | |||
@@ -20,253 +20,350 @@ | |||
20 | ****************************************************************************/ | 20 | ****************************************************************************/ |
21 | 21 | ||
22 | #include "nand-x1000.h" | 22 | #include "nand-x1000.h" |
23 | #include "nand-target.h" | ||
24 | #include "sfc-x1000.h" | 23 | #include "sfc-x1000.h" |
25 | #include "system.h" | 24 | #include "system.h" |
26 | #include <string.h> | 25 | #include <stddef.h> |
27 | 26 | ||
28 | #if !defined(NAND_MAX_PAGE_SIZE) || \ | 27 | /* NAND command numbers */ |
29 | !defined(NAND_INIT_SFC_DEV_CONF) || \ | 28 | #define NAND_CMD_READ_ID 0x9f |
30 | !defined(NAND_INIT_CLOCK_SPEED) | 29 | #define NAND_CMD_WRITE_ENABLE 0x06 |
31 | # error "Target needs to specify NAND driver parameters" | 30 | #define NAND_CMD_GET_FEATURE 0x0f |
32 | #endif | 31 | #define NAND_CMD_SET_FEATURE 0x1f |
33 | 32 | #define NAND_CMD_PAGE_READ_TO_CACHE 0x13 | |
34 | /* Must be at least as big as a cacheline */ | 33 | #define NAND_CMD_READ_FROM_CACHE 0x0b |
35 | #define NAND_AUX_BUFFER_SIZE CACHEALIGN_SIZE | 34 | #define NAND_CMD_READ_FROM_CACHEx4 0x6b |
36 | 35 | #define NAND_CMD_PROGRAM_LOAD 0x02 | |
37 | /* Writes have been enabled */ | 36 | #define NAND_CMD_PROGRAM_LOADx4 0x32 |
38 | #define NAND_DRV_FLAG_WRITEABLE 0x01 | 37 | #define NAND_CMD_PROGRAM_EXECUTE 0x10 |
39 | 38 | #define NAND_CMD_BLOCK_ERASE 0xd8 | |
40 | /* Defined by target */ | 39 | |
41 | extern const nand_chip_desc target_nand_chip_descs[]; | 40 | /* NAND device register addresses for GET_FEATURE / SET_FEATURE */ |
42 | 41 | #define NAND_FREG_PROTECTION 0xa0 | |
43 | #ifdef BOOTLOADER_SPL | 42 | #define NAND_FREG_FEATURE 0xb0 |
44 | # define NANDBUFFER_ATTR __attribute__((section(".sdram"))) CACHEALIGN_ATTR | 43 | #define NAND_FREG_STATUS 0xc0 |
44 | |||
45 | /* Protection register bits */ | ||
46 | #define NAND_FREG_PROTECTION_BRWD 0x80 | ||
47 | #define NAND_FREG_PROTECTION_BP2 0x20 | ||
48 | #define NAND_FREG_PROTECTION_BP1 0x10 | ||
49 | #define NAND_FREG_PROTECTION_BP0 0x80 | ||
50 | /* Mask of BP bits 0-2 */ | ||
51 | #define NAND_FREG_PROTECTION_ALLBP (0x38) | ||
52 | |||
53 | /* Feature register bits */ | ||
54 | #define NAND_FREG_FEATURE_QE 0x01 | ||
55 | |||
56 | /* Status register bits */ | ||
57 | #define NAND_FREG_STATUS_OIP 0x01 | ||
58 | #define NAND_FREG_STATUS_WEL 0x02 | ||
59 | #define NAND_FREG_STATUS_E_FAIL 0x04 | ||
60 | #define NAND_FREG_STATUS_P_FAIL 0x08 | ||
61 | |||
62 | /* NAND chip config */ | ||
63 | const nand_chip_data target_nand_chip_data[] = { | ||
64 | #ifdef FIIO_M3K | ||
65 | { | ||
66 | /* ATO25D1GA */ | ||
67 | .mf_id = 0x9b, | ||
68 | .dev_id = 0x12, | ||
69 | .dev_conf = jz_orf(SFC_DEV_CONF, CE_DL(1), HOLD_DL(1), WP_DL(1), | ||
70 | CPHA(0), CPOL(0), TSH(7), TSETUP(0), THOLD(0), | ||
71 | STA_TYPE_V(1BYTE), CMD_TYPE_V(8BITS), SMP_DELAY(1)), | ||
72 | .clock_freq = 150000000, | ||
73 | .log2_page_size = 11, /* = 2048 bytes */ | ||
74 | .log2_block_size = 6, /* = 64 pages */ | ||
75 | .rowaddr_width = 3, | ||
76 | .coladdr_width = 2, | ||
77 | .flags = NANDCHIP_FLAG_QUAD, | ||
78 | } | ||
45 | #else | 79 | #else |
46 | # define NANDBUFFER_ATTR CACHEALIGN_ATTR | 80 | /* Nobody will use this anyway if the device has no NAND flash */ |
81 | { 0 }, | ||
47 | #endif | 82 | #endif |
83 | }; | ||
84 | |||
85 | const size_t target_nand_chip_count = | ||
86 | sizeof(target_nand_chip_data) / sizeof(nand_chip_data); | ||
87 | |||
88 | /* NAND ops -- high level primitives used by the driver */ | ||
89 | static int nandop_wait_status(int errbit); | ||
90 | static int nandop_read_page(uint32_t row_addr, uint8_t* buf); | ||
91 | static int nandop_write_page(uint32_t row_addr, const uint8_t* buf); | ||
92 | static int nandop_erase_block(uint32_t block_addr); | ||
93 | static int nandop_set_write_protect(bool en); | ||
94 | |||
95 | /* NAND commands -- 1-to-1 mapping between chip commands and functions */ | ||
96 | static int nandcmd_read_id(int* mf_id, int* dev_id); | ||
97 | static int nandcmd_write_enable(void); | ||
98 | static int nandcmd_get_feature(uint8_t reg); | ||
99 | static int nandcmd_set_feature(uint8_t reg, uint8_t val); | ||
100 | static int nandcmd_page_read_to_cache(uint32_t row_addr); | ||
101 | static int nandcmd_read_from_cache(uint8_t* buf); | ||
102 | static int nandcmd_program_load(const uint8_t* buf); | ||
103 | static int nandcmd_program_execute(uint32_t row_addr); | ||
104 | static int nandcmd_block_erase(uint32_t block_addr); | ||
105 | |||
106 | struct nand_drv { | ||
107 | const nand_chip_data* chip_data; | ||
108 | bool write_enabled; | ||
109 | }; | ||
48 | 110 | ||
49 | /* Globals for the driver */ | 111 | static struct nand_drv nand_drv; |
50 | static unsigned char pagebuffer[NAND_MAX_PAGE_SIZE] NANDBUFFER_ATTR; | 112 | static uint8_t nand_auxbuf[32] CACHEALIGN_ATTR; |
51 | static unsigned char auxbuffer[NAND_AUX_BUFFER_SIZE] NANDBUFFER_ATTR; | ||
52 | static nand_drv nand_driver; | ||
53 | 113 | ||
54 | static void nand_drv_reset(nand_drv* d) | 114 | static void nand_drv_reset(void) |
55 | { | 115 | { |
56 | d->chip_ops = NULL; | 116 | nand_drv.chip_data = NULL; |
57 | d->chip_data = NULL; | 117 | nand_drv.write_enabled = false; |
58 | d->pagebuf = &pagebuffer[0]; | ||
59 | d->auxbuf = &auxbuffer[0]; | ||
60 | d->raw_page_size = 0; | ||
61 | d->flags = 0; | ||
62 | } | 118 | } |
63 | 119 | ||
64 | /* Driver code */ | ||
65 | int nand_open(void) | 120 | int nand_open(void) |
66 | { | 121 | { |
67 | sfc_init(); | 122 | sfc_init(); |
68 | sfc_lock(); | 123 | sfc_lock(); |
69 | 124 | ||
70 | /* Reset driver state */ | 125 | nand_drv_reset(); |
71 | nand_drv_reset(&nand_driver); | ||
72 | |||
73 | /* Init hardware */ | ||
74 | sfc_open(); | 126 | sfc_open(); |
75 | sfc_set_dev_conf(NAND_INIT_SFC_DEV_CONF); | ||
76 | sfc_set_clock(NAND_INIT_CLOCK_SPEED); | ||
77 | |||
78 | /* Identify NAND chip */ | ||
79 | int status = 0; | ||
80 | int nandid = nandcmd_read_id(&nand_driver); | ||
81 | if(nandid < 0) { | ||
82 | status = NANDERR_CHIP_UNSUPPORTED; | ||
83 | goto _err; | ||
84 | } | ||
85 | 127 | ||
86 | unsigned char mf_id = nandid >> 8; | 128 | const nand_chip_data* chip_data = &target_nand_chip_data[0]; |
87 | unsigned char dev_id = nandid & 0xff; | 129 | sfc_set_dev_conf(chip_data->dev_conf); |
88 | const nand_chip_desc* desc = &target_nand_chip_descs[0]; | 130 | sfc_set_clock(chip_data->clock_freq); |
89 | while(1) { | ||
90 | if(desc->data == NULL || desc->ops == NULL) { | ||
91 | status = NANDERR_CHIP_UNSUPPORTED; | ||
92 | goto _err; | ||
93 | } | ||
94 | |||
95 | if(desc->data->mf_id == mf_id && desc->data->dev_id == dev_id) | ||
96 | break; | ||
97 | } | ||
98 | |||
99 | /* Fill driver parameters */ | ||
100 | nand_driver.chip_ops = desc->ops; | ||
101 | nand_driver.chip_data = desc->data; | ||
102 | nand_driver.raw_page_size = desc->data->page_size + desc->data->spare_size; | ||
103 | |||
104 | /* Configure hardware and run init op */ | ||
105 | sfc_set_dev_conf(desc->data->dev_conf); | ||
106 | sfc_set_clock(desc->data->clock_freq); | ||
107 | 131 | ||
108 | if((status = desc->ops->open(&nand_driver)) < 0) | 132 | return NAND_SUCCESS; |
109 | goto _err; | ||
110 | |||
111 | _exit: | ||
112 | sfc_unlock(); | ||
113 | return status; | ||
114 | _err: | ||
115 | nand_drv_reset(&nand_driver); | ||
116 | sfc_close(); | ||
117 | goto _exit; | ||
118 | } | 133 | } |
119 | 134 | ||
120 | void nand_close(void) | 135 | void nand_close(void) |
121 | { | 136 | { |
122 | sfc_lock(); | 137 | sfc_lock(); |
123 | nand_driver.chip_ops->close(&nand_driver); | ||
124 | nand_drv_reset(&nand_driver); | ||
125 | sfc_close(); | 138 | sfc_close(); |
139 | nand_drv_reset(); | ||
126 | sfc_unlock(); | 140 | sfc_unlock(); |
127 | } | 141 | } |
128 | 142 | ||
129 | int nand_enable_writes(bool en) | 143 | int nand_identify(int* mf_id, int* dev_id) |
130 | { | 144 | { |
131 | sfc_lock(); | 145 | sfc_lock(); |
132 | 146 | ||
133 | int st = nand_driver.chip_ops->set_wp_enable(&nand_driver, en); | 147 | int status = nandcmd_read_id(mf_id, dev_id); |
134 | if(st >= 0) { | 148 | if(status < 0) |
135 | if(en) | 149 | goto error; |
136 | nand_driver.flags |= NAND_DRV_FLAG_WRITEABLE; | 150 | |
137 | else | 151 | for(size_t i = 0; i < target_nand_chip_count; ++i) { |
138 | nand_driver.flags &= ~NAND_DRV_FLAG_WRITEABLE; | 152 | const nand_chip_data* data = &target_nand_chip_data[i]; |
153 | if(data->mf_id == *mf_id && data->dev_id == *dev_id) { | ||
154 | nand_drv.chip_data = data; | ||
155 | break; | ||
156 | } | ||
157 | } | ||
158 | |||
159 | if(!nand_drv.chip_data) { | ||
160 | status = NAND_ERR_UNKNOWN_CHIP; | ||
161 | goto error; | ||
139 | } | 162 | } |
140 | 163 | ||
164 | /* Set parameters according to new chip data */ | ||
165 | sfc_set_dev_conf(nand_drv.chip_data->dev_conf); | ||
166 | sfc_set_clock(nand_drv.chip_data->clock_freq); | ||
167 | status = NAND_SUCCESS; | ||
168 | |||
169 | error: | ||
141 | sfc_unlock(); | 170 | sfc_unlock(); |
142 | return st; | 171 | return status; |
143 | } | 172 | } |
144 | 173 | ||
145 | extern int nand_read_bytes(uint32_t byteaddr, int count, void* buf) | 174 | const nand_chip_data* nand_get_chip_data(void) |
146 | { | 175 | { |
147 | if(count <= 0) | 176 | return nand_drv.chip_data; |
148 | return 0; | 177 | } |
149 | 178 | ||
150 | nand_drv* d = &nand_driver; | 179 | extern int nand_enable_writes(bool en) |
151 | uint32_t rowaddr = byteaddr / d->chip_data->page_size; | 180 | { |
152 | uint32_t coladdr = byteaddr % d->chip_data->page_size; | 181 | if(en == nand_drv.write_enabled) |
153 | unsigned char* dstbuf = (unsigned char*)buf; | 182 | return NAND_SUCCESS; |
154 | int status = 0; | ||
155 | 183 | ||
156 | sfc_lock(); | 184 | int rc = nandop_set_write_protect(!en); |
157 | do { | 185 | if(rc == NAND_SUCCESS) |
158 | if(d->chip_ops->read_page(d, rowaddr, d->pagebuf) < 0) { | 186 | nand_drv.write_enabled = en; |
159 | status = NANDERR_READ_FAILED; | ||
160 | goto _end; | ||
161 | } | ||
162 | 187 | ||
163 | if(d->chip_ops->ecc_read(d, d->pagebuf) < 0) { | 188 | return rc; |
164 | status = NANDERR_ECC_FAILED; | 189 | } |
165 | goto _end; | ||
166 | } | ||
167 | 190 | ||
168 | int amount = d->chip_data->page_size - coladdr; | 191 | static int nand_rdwr(bool write, uint32_t addr, uint32_t size, uint8_t* buf) |
169 | if(amount > count) | 192 | { |
170 | amount = count; | 193 | const uint32_t page_size = (1 << nand_drv.chip_data->log2_page_size); |
194 | |||
195 | if(addr & (page_size - 1)) | ||
196 | return NAND_ERR_UNALIGNED; | ||
197 | if(size & (page_size - 1)) | ||
198 | return NAND_ERR_UNALIGNED; | ||
199 | if(size <= 0) | ||
200 | return NAND_SUCCESS; | ||
201 | if(write && !nand_drv.write_enabled) | ||
202 | return NAND_ERR_WRITE_PROTECT; | ||
203 | /* FIXME: re-enable this check after merging new SPL+bootloader. | ||
204 | * It's only necessary for DMA, which is currently not used, but it's a | ||
205 | * good practice anyway. Disable for now due to SPL complications. */ | ||
206 | /*if((uint32_t)buf & (CACHEALIGN_SIZE - 1)) | ||
207 | return NAND_ERR_UNALIGNED;*/ | ||
208 | |||
209 | addr >>= nand_drv.chip_data->log2_page_size; | ||
210 | size >>= nand_drv.chip_data->log2_page_size; | ||
211 | |||
212 | int rc = NAND_SUCCESS; | ||
213 | sfc_lock(); | ||
171 | 214 | ||
172 | memcpy(dstbuf, d->pagebuf, amount); | 215 | for(; size > 0; --size, ++addr, buf += page_size) { |
173 | dstbuf += amount; | 216 | if(write) |
174 | count -= amount; | 217 | rc = nandop_write_page(addr, buf); |
175 | rowaddr += 1; | 218 | else |
176 | coladdr = 0; | 219 | rc = nandop_read_page(addr, buf); |
177 | } while(count > 0); | 220 | |
221 | if(rc) | ||
222 | break; | ||
223 | } | ||
178 | 224 | ||
179 | _end: | ||
180 | sfc_unlock(); | 225 | sfc_unlock(); |
181 | return status; | 226 | return rc; |
182 | } | 227 | } |
183 | 228 | ||
184 | int nand_write_bytes(uint32_t byteaddr, int count, const void* buf) | 229 | int nand_read(uint32_t addr, uint32_t size, uint8_t* buf) |
185 | { | 230 | { |
186 | nand_drv* d = &nand_driver; | 231 | return nand_rdwr(false, addr, size, buf); |
232 | } | ||
187 | 233 | ||
188 | if((d->flags & NAND_DRV_FLAG_WRITEABLE) == 0) | 234 | int nand_write(uint32_t addr, uint32_t size, const uint8_t* buf) |
189 | return NANDERR_WRITE_PROTECTED; | 235 | { |
236 | return nand_rdwr(true, addr, size, (uint8_t*)buf); | ||
237 | } | ||
190 | 238 | ||
191 | if(count <= 0) | 239 | int nand_erase(uint32_t addr, uint32_t size) |
192 | return 0; | 240 | { |
241 | const uint32_t page_size = 1 << nand_drv.chip_data->log2_page_size; | ||
242 | const uint32_t block_size = page_size << nand_drv.chip_data->log2_block_size; | ||
243 | const uint32_t pages_per_block = 1 << nand_drv.chip_data->log2_page_size; | ||
244 | |||
245 | if(addr & (block_size - 1)) | ||
246 | return NAND_ERR_UNALIGNED; | ||
247 | if(size & (block_size - 1)) | ||
248 | return NAND_ERR_UNALIGNED; | ||
249 | if(size <= 0) | ||
250 | return NAND_SUCCESS; | ||
251 | if(!nand_drv.write_enabled) | ||
252 | return NAND_ERR_WRITE_PROTECT; | ||
253 | |||
254 | addr >>= nand_drv.chip_data->log2_page_size; | ||
255 | size >>= nand_drv.chip_data->log2_page_size; | ||
256 | size >>= nand_drv.chip_data->log2_block_size; | ||
257 | |||
258 | int rc = NAND_SUCCESS; | ||
259 | sfc_lock(); | ||
193 | 260 | ||
194 | uint32_t rowaddr = byteaddr / d->chip_data->page_size; | 261 | for(; size > 0; --size, addr += pages_per_block) |
195 | uint32_t coladdr = byteaddr % d->chip_data->page_size; | 262 | if((rc = nandop_erase_block(addr))) |
263 | break; | ||
196 | 264 | ||
197 | /* Only support whole page writes right now */ | 265 | sfc_unlock(); |
198 | if(coladdr != 0) | 266 | return rc; |
199 | return NANDERR_UNALIGNED_ADDRESS; | 267 | } |
200 | if(count % d->chip_data->page_size) | ||
201 | return NANDERR_UNALIGNED_LENGTH; | ||
202 | 268 | ||
203 | const unsigned char* srcbuf = (const unsigned char*)buf; | 269 | /* |
204 | int status = 0; | 270 | * NAND ops |
271 | */ | ||
205 | 272 | ||
206 | sfc_lock(); | 273 | static int nandop_wait_status(int errbit) |
274 | { | ||
275 | int reg; | ||
207 | do { | 276 | do { |
208 | memcpy(d->pagebuf, srcbuf, d->chip_data->page_size); | 277 | reg = nandcmd_get_feature(NAND_FREG_STATUS); |
209 | d->chip_ops->ecc_write(d, d->pagebuf); | 278 | if(reg < 0) |
210 | 279 | return reg; | |
211 | if(d->chip_ops->write_page(d, rowaddr, d->pagebuf) < 0) { | 280 | } while(reg & NAND_FREG_STATUS_OIP); |
212 | status = NANDERR_PROGRAM_FAILED; | ||
213 | goto _end; | ||
214 | } | ||
215 | 281 | ||
216 | rowaddr += 1; | 282 | if(reg & errbit) |
217 | srcbuf += d->chip_data->page_size; | 283 | return NAND_ERR_COMMAND; |
218 | count -= d->chip_data->page_size; | ||
219 | } while(count > 0); | ||
220 | 284 | ||
221 | _end: | 285 | return reg; |
222 | sfc_unlock(); | ||
223 | return status; | ||
224 | } | 286 | } |
225 | 287 | ||
226 | int nand_erase_bytes(uint32_t byteaddr, int count) | 288 | static int nandop_read_page(uint32_t row_addr, uint8_t* buf) |
227 | { | 289 | { |
228 | nand_drv* d = &nand_driver; | 290 | int status; |
229 | 291 | ||
230 | if((d->flags & NAND_DRV_FLAG_WRITEABLE) == 0) | 292 | if((status = nandcmd_page_read_to_cache(row_addr)) < 0) |
231 | return NANDERR_WRITE_PROTECTED; | 293 | return status; |
294 | if((status = nandop_wait_status(0)) < 0) | ||
295 | return status; | ||
296 | if((status = nandcmd_read_from_cache(buf)) < 0) | ||
297 | return status; | ||
232 | 298 | ||
233 | /* Ensure address is aligned to a block boundary */ | 299 | return NAND_SUCCESS; |
234 | if(byteaddr % d->chip_data->page_size) | 300 | } |
235 | return NANDERR_UNALIGNED_ADDRESS; | ||
236 | 301 | ||
237 | uint32_t blockaddr = byteaddr / d->chip_data->page_size; | 302 | static int nandop_write_page(uint32_t row_addr, const uint8_t* buf) |
238 | if(blockaddr % d->chip_data->block_size) | 303 | { |
239 | return NANDERR_UNALIGNED_ADDRESS; | 304 | int status; |
240 | 305 | ||
241 | /* Ensure length is also aligned to the size of a block */ | 306 | if((status = nandcmd_write_enable()) < 0) |
242 | if(count % d->chip_data->page_size) | 307 | return status; |
243 | return NANDERR_UNALIGNED_LENGTH; | 308 | if((status = nandcmd_program_load(buf)) < 0) |
309 | return status; | ||
310 | if((status = nandcmd_program_execute(row_addr)) < 0) | ||
311 | return status; | ||
312 | if((status = nandop_wait_status(NAND_FREG_STATUS_P_FAIL)) < 0) | ||
313 | return status; | ||
244 | 314 | ||
245 | count /= d->chip_data->page_size; | 315 | return NAND_SUCCESS; |
246 | if(count % d->chip_data->block_size) | 316 | } |
247 | return NANDERR_UNALIGNED_LENGTH; | 317 | |
318 | static int nandop_erase_block(uint32_t block_addr) | ||
319 | { | ||
320 | int status; | ||
248 | 321 | ||
249 | count /= d->chip_data->block_size; | 322 | if((status = nandcmd_write_enable()) < 0) |
323 | return status; | ||
324 | if((status = nandcmd_block_erase(block_addr)) < 0) | ||
325 | return status; | ||
326 | if((status = nandop_wait_status(NAND_FREG_STATUS_E_FAIL)) < 0) | ||
327 | return status; | ||
250 | 328 | ||
251 | int status = 0; | 329 | return NAND_SUCCESS; |
252 | sfc_lock(); | 330 | } |
253 | 331 | ||
254 | for(int i = 0; i < count; ++i) { | 332 | static int nandop_set_write_protect(bool en) |
255 | if(d->chip_ops->erase_block(d, blockaddr)) { | 333 | { |
256 | status = NANDERR_ERASE_FAILED; | 334 | int val = nandcmd_get_feature(NAND_FREG_PROTECTION); |
257 | goto _end; | 335 | if(val < 0) |
258 | } | 336 | return val; |
259 | 337 | ||
260 | /* Advance to next block */ | 338 | if(en) { |
261 | blockaddr += d->chip_data->block_size; | 339 | val &= ~NAND_FREG_PROTECTION_ALLBP; |
340 | if(nand_drv.chip_data->flags & NANDCHIP_FLAG_USE_BRWD) | ||
341 | val &= ~NAND_FREG_PROTECTION_BRWD; | ||
342 | } else { | ||
343 | val |= NAND_FREG_PROTECTION_ALLBP; | ||
344 | if(nand_drv.chip_data->flags & NANDCHIP_FLAG_USE_BRWD) | ||
345 | val |= NAND_FREG_PROTECTION_BRWD; | ||
262 | } | 346 | } |
263 | 347 | ||
264 | _end: | 348 | /* NOTE: The WP pin typically only protects changes to the protection |
265 | sfc_unlock(); | 349 | * register -- it doesn't actually prevent writing to the chip. That's |
266 | return status; | 350 | * why it should be re-enabled after setting the new protection status. |
351 | */ | ||
352 | sfc_set_wp_enable(false); | ||
353 | int status = nandcmd_set_feature(NAND_FREG_PROTECTION, val); | ||
354 | sfc_set_wp_enable(true); | ||
355 | |||
356 | if(status < 0) | ||
357 | return status; | ||
358 | |||
359 | return NAND_SUCCESS; | ||
267 | } | 360 | } |
268 | 361 | ||
269 | int nandcmd_read_id(nand_drv* d) | 362 | /* |
363 | * Low-level NAND commands | ||
364 | */ | ||
365 | |||
366 | static int nandcmd_read_id(int* mf_id, int* dev_id) | ||
270 | { | 367 | { |
271 | sfc_op op = {0}; | 368 | sfc_op op = {0}; |
272 | op.command = NAND_CMD_READ_ID; | 369 | op.command = NAND_CMD_READ_ID; |
@@ -274,72 +371,72 @@ int nandcmd_read_id(nand_drv* d) | |||
274 | op.addr_bytes = 1; | 371 | op.addr_bytes = 1; |
275 | op.addr_lo = 0; | 372 | op.addr_lo = 0; |
276 | op.data_bytes = 2; | 373 | op.data_bytes = 2; |
277 | op.buffer = d->auxbuf; | 374 | op.buffer = nand_auxbuf; |
278 | if(sfc_exec(&op)) | 375 | if(sfc_exec(&op)) |
279 | return NANDERR_COMMAND_FAILED; | 376 | return NAND_ERR_CONTROLLER; |
280 | 377 | ||
281 | return (d->auxbuf[0] << 8) | d->auxbuf[1]; | 378 | *mf_id = nand_auxbuf[0]; |
379 | *dev_id = nand_auxbuf[1]; | ||
380 | return NAND_SUCCESS; | ||
282 | } | 381 | } |
283 | 382 | ||
284 | int nandcmd_write_enable(nand_drv* d) | 383 | static int nandcmd_write_enable(void) |
285 | { | 384 | { |
286 | (void)d; | ||
287 | |||
288 | sfc_op op = {0}; | 385 | sfc_op op = {0}; |
289 | op.command = NAND_CMD_WRITE_ENABLE; | 386 | op.command = NAND_CMD_WRITE_ENABLE; |
290 | if(sfc_exec(&op)) | 387 | if(sfc_exec(&op)) |
291 | return NANDERR_COMMAND_FAILED; | 388 | return NAND_ERR_CONTROLLER; |
292 | 389 | ||
293 | return 0; | 390 | return NAND_SUCCESS; |
294 | } | 391 | } |
295 | 392 | ||
296 | int nandcmd_get_feature(nand_drv* d, int reg) | 393 | static int nandcmd_get_feature(uint8_t reg) |
297 | { | 394 | { |
298 | sfc_op op = {0}; | 395 | sfc_op op = {0}; |
299 | op.command = NAND_CMD_GET_FEATURE; | 396 | op.command = NAND_CMD_GET_FEATURE; |
300 | op.flags = SFC_FLAG_READ; | 397 | op.flags = SFC_FLAG_READ; |
301 | op.addr_bytes = 1; | 398 | op.addr_bytes = 1; |
302 | op.addr_lo = reg & 0xff; | 399 | op.addr_lo = reg; |
303 | op.data_bytes = 1; | 400 | op.data_bytes = 1; |
304 | op.buffer = d->auxbuf; | 401 | op.buffer = nand_auxbuf; |
305 | if(sfc_exec(&op)) | 402 | if(sfc_exec(&op)) |
306 | return NANDERR_COMMAND_FAILED; | 403 | return NAND_ERR_CONTROLLER; |
307 | 404 | ||
308 | return d->auxbuf[0]; | 405 | return nand_auxbuf[0]; |
309 | } | 406 | } |
310 | 407 | ||
311 | int nandcmd_set_feature(nand_drv* d, int reg, int val) | 408 | static int nandcmd_set_feature(uint8_t reg, uint8_t val) |
312 | { | 409 | { |
313 | sfc_op op = {0}; | 410 | sfc_op op = {0}; |
314 | op.command = NAND_CMD_SET_FEATURE; | 411 | op.command = NAND_CMD_SET_FEATURE; |
315 | op.flags = SFC_FLAG_READ; | 412 | op.flags = SFC_FLAG_READ; |
316 | op.addr_bytes = 1; | 413 | op.addr_bytes = 1; |
317 | op.addr_lo = reg & 0xff; | 414 | op.addr_lo = reg; |
318 | op.data_bytes = 1; | 415 | op.data_bytes = 1; |
319 | op.buffer = d->auxbuf; | 416 | op.buffer = nand_auxbuf; |
320 | d->auxbuf[0] = val & 0xff; | 417 | nand_auxbuf[0] = val; |
321 | if(sfc_exec(&op)) | 418 | if(sfc_exec(&op)) |
322 | return NANDERR_COMMAND_FAILED; | 419 | return NAND_ERR_CONTROLLER; |
323 | 420 | ||
324 | return 0; | 421 | return NAND_SUCCESS; |
325 | } | 422 | } |
326 | 423 | ||
327 | int nandcmd_page_read_to_cache(nand_drv* d, uint32_t rowaddr) | 424 | static int nandcmd_page_read_to_cache(uint32_t row_addr) |
328 | { | 425 | { |
329 | sfc_op op = {0}; | 426 | sfc_op op = {0}; |
330 | op.command = NAND_CMD_PAGE_READ_TO_CACHE; | 427 | op.command = NAND_CMD_PAGE_READ_TO_CACHE; |
331 | op.addr_bytes = d->chip_data->rowaddr_width; | 428 | op.addr_bytes = nand_drv.chip_data->rowaddr_width; |
332 | op.addr_lo = rowaddr; | 429 | op.addr_lo = row_addr; |
333 | if(sfc_exec(&op)) | 430 | if(sfc_exec(&op)) |
334 | return NANDERR_COMMAND_FAILED; | 431 | return NAND_ERR_CONTROLLER; |
335 | 432 | ||
336 | return 0; | 433 | return NAND_SUCCESS; |
337 | } | 434 | } |
338 | 435 | ||
339 | int nandcmd_read_from_cache(nand_drv* d, unsigned char* buf) | 436 | static int nandcmd_read_from_cache(uint8_t* buf) |
340 | { | 437 | { |
341 | sfc_op op = {0}; | 438 | sfc_op op = {0}; |
342 | if(d->chip_data->flags & NANDCHIP_FLAG_QUAD) { | 439 | if(nand_drv.chip_data->flags & NANDCHIP_FLAG_QUAD) { |
343 | op.command = NAND_CMD_READ_FROM_CACHEx4; | 440 | op.command = NAND_CMD_READ_FROM_CACHEx4; |
344 | op.mode = SFC_MODE_QUAD_IO; | 441 | op.mode = SFC_MODE_QUAD_IO; |
345 | } else { | 442 | } else { |
@@ -348,21 +445,21 @@ int nandcmd_read_from_cache(nand_drv* d, unsigned char* buf) | |||
348 | } | 445 | } |
349 | 446 | ||
350 | op.flags = SFC_FLAG_READ; | 447 | op.flags = SFC_FLAG_READ; |
351 | op.addr_bytes = d->chip_data->coladdr_width; | 448 | op.addr_bytes = nand_drv.chip_data->coladdr_width; |
352 | op.addr_lo = 0; | 449 | op.addr_lo = 0; |
353 | op.dummy_bits = 8; | 450 | op.dummy_bits = 8; // NOTE: this may need a chip_data parameter |
354 | op.data_bytes = d->raw_page_size; | 451 | op.data_bytes = (1 << nand_drv.chip_data->log2_page_size); |
355 | op.buffer = buf; | 452 | op.buffer = buf; |
356 | if(sfc_exec(&op)) | 453 | if(sfc_exec(&op)) |
357 | return NANDERR_COMMAND_FAILED; | 454 | return NAND_ERR_CONTROLLER; |
358 | 455 | ||
359 | return 0; | 456 | return NAND_SUCCESS; |
360 | } | 457 | } |
361 | 458 | ||
362 | int nandcmd_program_load(nand_drv* d, const unsigned char* buf) | 459 | static int nandcmd_program_load(const uint8_t* buf) |
363 | { | 460 | { |
364 | sfc_op op = {0}; | 461 | sfc_op op = {0}; |
365 | if(d->chip_data->flags & NANDCHIP_FLAG_QUAD) { | 462 | if(nand_drv.chip_data->flags & NANDCHIP_FLAG_QUAD) { |
366 | op.command = NAND_CMD_PROGRAM_LOADx4; | 463 | op.command = NAND_CMD_PROGRAM_LOADx4; |
367 | op.mode = SFC_MODE_QUAD_IO; | 464 | op.mode = SFC_MODE_QUAD_IO; |
368 | } else { | 465 | } else { |
@@ -371,156 +468,36 @@ int nandcmd_program_load(nand_drv* d, const unsigned char* buf) | |||
371 | } | 468 | } |
372 | 469 | ||
373 | op.flags = SFC_FLAG_WRITE; | 470 | op.flags = SFC_FLAG_WRITE; |
374 | op.addr_bytes = d->chip_data->coladdr_width; | 471 | op.addr_bytes = nand_drv.chip_data->coladdr_width; |
375 | op.addr_lo = 0; | 472 | op.addr_lo = 0; |
376 | op.data_bytes = d->raw_page_size; | 473 | op.data_bytes = (1 << nand_drv.chip_data->log2_page_size); |
377 | op.buffer = (void*)buf; | 474 | op.buffer = (void*)buf; |
378 | if(sfc_exec(&op)) | 475 | if(sfc_exec(&op)) |
379 | return NANDERR_COMMAND_FAILED; | 476 | return NAND_ERR_CONTROLLER; |
380 | 477 | ||
381 | return 0; | 478 | return NAND_SUCCESS; |
382 | } | 479 | } |
383 | 480 | ||
384 | int nandcmd_program_execute(nand_drv* d, uint32_t rowaddr) | 481 | static int nandcmd_program_execute(uint32_t row_addr) |
385 | { | 482 | { |
386 | sfc_op op = {0}; | 483 | sfc_op op = {0}; |
387 | op.command = NAND_CMD_PROGRAM_EXECUTE; | 484 | op.command = NAND_CMD_PROGRAM_EXECUTE; |
388 | op.addr_bytes = d->chip_data->rowaddr_width; | 485 | op.addr_bytes = nand_drv.chip_data->rowaddr_width; |
389 | op.addr_lo = rowaddr; | 486 | op.addr_lo = row_addr; |
390 | if(sfc_exec(&op)) | 487 | if(sfc_exec(&op)) |
391 | return NANDERR_COMMAND_FAILED; | 488 | return NAND_ERR_CONTROLLER; |
392 | 489 | ||
393 | return 0; | 490 | return NAND_SUCCESS; |
394 | } | 491 | } |
395 | 492 | ||
396 | int nandcmd_block_erase(nand_drv* d, uint32_t blockaddr) | 493 | static int nandcmd_block_erase(uint32_t block_addr) |
397 | { | 494 | { |
398 | sfc_op op = {0}; | 495 | sfc_op op = {0}; |
399 | op.command = NAND_CMD_BLOCK_ERASE; | 496 | op.command = NAND_CMD_BLOCK_ERASE; |
400 | op.addr_bytes = d->chip_data->rowaddr_width; | 497 | op.addr_bytes = nand_drv.chip_data->rowaddr_width; |
401 | op.addr_lo = blockaddr; | 498 | op.addr_lo = block_addr; |
402 | if(sfc_exec(&op)) | 499 | if(sfc_exec(&op)) |
403 | return NANDERR_COMMAND_FAILED; | 500 | return NAND_ERR_CONTROLLER; |
404 | 501 | ||
405 | return 0; | 502 | return NAND_SUCCESS; |
406 | } | ||
407 | |||
408 | const nand_chip_ops nand_chip_ops_std = { | ||
409 | .open = nandop_std_open, | ||
410 | .close = nandop_std_close, | ||
411 | .read_page = nandop_std_read_page, | ||
412 | .write_page = nandop_std_write_page, | ||
413 | .erase_block = nandop_std_erase_block, | ||
414 | .set_wp_enable = nandop_std_set_wp_enable, | ||
415 | .ecc_read = nandop_ecc_none_read, | ||
416 | .ecc_write = nandop_ecc_none_write, | ||
417 | }; | ||
418 | |||
419 | /* Helper needed by other ops */ | ||
420 | static int nandop_std_wait_status(nand_drv* d, int errbit) | ||
421 | { | ||
422 | int reg; | ||
423 | do { | ||
424 | reg = nandcmd_get_feature(d, NAND_FREG_STATUS); | ||
425 | if(reg < 0) | ||
426 | return reg; | ||
427 | } while(reg & NAND_FREG_STATUS_OIP); | ||
428 | |||
429 | if(reg & errbit) | ||
430 | return NANDERR_OTHER; | ||
431 | |||
432 | return reg; | ||
433 | } | ||
434 | |||
435 | int nandop_std_open(nand_drv* d) | ||
436 | { | ||
437 | (void)d; | ||
438 | return 0; | ||
439 | } | ||
440 | |||
441 | void nandop_std_close(nand_drv* d) | ||
442 | { | ||
443 | (void)d; | ||
444 | } | ||
445 | |||
446 | int nandop_std_read_page(nand_drv* d, uint32_t rowaddr, unsigned char* buf) | ||
447 | { | ||
448 | int status; | ||
449 | |||
450 | if((status = nandcmd_page_read_to_cache(d, rowaddr)) < 0) | ||
451 | return status; | ||
452 | if((status = nandop_std_wait_status(d, 0)) < 0) | ||
453 | return status; | ||
454 | if((status = nandcmd_read_from_cache(d, buf)) < 0) | ||
455 | return status; | ||
456 | |||
457 | return 0; | ||
458 | } | ||
459 | |||
460 | int nandop_std_write_page(nand_drv* d, uint32_t rowaddr, const unsigned char* buf) | ||
461 | { | ||
462 | int status; | ||
463 | |||
464 | if((status = nandcmd_write_enable(d)) < 0) | ||
465 | return status; | ||
466 | if((status = nandcmd_program_load(d, buf)) < 0) | ||
467 | return status; | ||
468 | if((status = nandcmd_program_execute(d, rowaddr)) < 0) | ||
469 | return status; | ||
470 | if((status = nandop_std_wait_status(d, NAND_FREG_STATUS_P_FAIL)) < 0) | ||
471 | return status; | ||
472 | |||
473 | return 0; | ||
474 | } | ||
475 | |||
476 | int nandop_std_erase_block(nand_drv* d, uint32_t blockaddr) | ||
477 | { | ||
478 | int status; | ||
479 | |||
480 | if((status = nandcmd_write_enable(d)) < 0) | ||
481 | return status; | ||
482 | if((status = nandcmd_block_erase(d, blockaddr)) < 0) | ||
483 | return status; | ||
484 | if((status = nandop_std_wait_status(d, NAND_FREG_STATUS_E_FAIL) < 0)) | ||
485 | return status; | ||
486 | |||
487 | return 0; | ||
488 | } | ||
489 | |||
490 | int nandop_std_set_wp_enable(nand_drv* d, bool en) | ||
491 | { | ||
492 | int val = nandcmd_get_feature(d, NAND_FREG_PROTECTION); | ||
493 | if(val < 0) | ||
494 | return val; | ||
495 | |||
496 | if(en) { | ||
497 | val &= ~NAND_FREG_PROTECTION_ALLBP; | ||
498 | if(d->chip_data->flags & NANDCHIP_FLAG_USE_BRWD) | ||
499 | val &= ~NAND_FREG_PROTECTION_BRWD; | ||
500 | } else { | ||
501 | val |= NAND_FREG_PROTECTION_ALLBP; | ||
502 | if(d->chip_data->flags & NANDCHIP_FLAG_USE_BRWD) | ||
503 | val |= NAND_FREG_PROTECTION_BRWD; | ||
504 | } | ||
505 | |||
506 | sfc_set_wp_enable(false); | ||
507 | int status = nandcmd_set_feature(d, NAND_FREG_PROTECTION, val); | ||
508 | sfc_set_wp_enable(true); | ||
509 | |||
510 | if(status < 0) | ||
511 | return status; | ||
512 | |||
513 | return 0; | ||
514 | } | ||
515 | |||
516 | int nandop_ecc_none_read(nand_drv* d, unsigned char* buf) | ||
517 | { | ||
518 | (void)d; | ||
519 | (void)buf; | ||
520 | return 0; | ||
521 | } | ||
522 | |||
523 | void nandop_ecc_none_write(nand_drv* d, unsigned char* buf) | ||
524 | { | ||
525 | memset(&buf[d->chip_data->page_size], 0xff, d->chip_data->spare_size); | ||
526 | } | 503 | } |