| 1 | #include <stdlib.h> |
| 2 | #include <stdio.h> |
| 3 | #include <fcntl.h> |
| 4 | #include <unistd.h> |
| 5 | #include <errno.h> |
| 6 | #include <sys/stat.h> |
| 7 | #include <string.h> |
| 8 | |
| 9 | #include "nand.h" |
| 10 | #include "ingenic_usb.h" |
| 11 | #include "usb_boot_defines.h" |
| 12 | |
| 13 | #define NAND_OP(idx, op, mode) (((mode << 12) & 0xf000) | ((idx << 4) & 0xff0) | op) |
| 14 | |
| 15 | #define DIV_ROUND_UP(n,d) (((n) + (d) - 1) / (d)) |
| 16 | |
| 17 | static const char IMAGE_TYPE[][30] = { |
| 18 | "with oob and ecc", |
| 19 | "with oob and without ecc", |
| 20 | "without oob", |
| 21 | }; |
| 22 | |
| 23 | static int error_check(const char *a, const char *b, unsigned int size) |
| 24 | { |
| 25 | unsigned int i; |
| 26 | const unsigned char *org = (const unsigned char *)a; |
| 27 | const unsigned char *obj= (const unsigned char *)b; |
| 28 | |
| 29 | |
| 30 | printf("Comparing %d bytes - ", size); |
| 31 | for (i = 0; i < size; i++) { |
| 32 | if (org[i] != obj[i]) { |
| 33 | unsigned int s = (i < 8) ? i : i - 8; // start_dump |
| 34 | printf("FAIL at off %d, wrote 0x%x, read 0x%x\n", i, org[i], obj[i]); |
| 35 | printf(" off %d write: %02x %02x %02x %02x %02x %02x %02x %02x" |
| 36 | " %02x %02x %02x %02x %02x %02x %02x %02x\n", s, |
| 37 | org[s], org[s+1], org[s+2], org[s+3], org[s+4], org[s+5], org[s+6], org[s+7], |
| 38 | org[s+8], org[s+9], org[s+10], org[s+11], org[s+12], org[s+13], org[s+14], org[s+15]); |
| 39 | printf(" off %d read: %02x %02x %02x %02x %02x %02x %02x %02x" |
| 40 | " %02x %02x %02x %02x %02x %02x %02x %02x\n", s, |
| 41 | obj[s], obj[s+1], obj[s+2], obj[s+3], obj[s+4], obj[s+5], obj[s+6], obj[s+7], |
| 42 | obj[s+8], obj[s+9], obj[s+10], obj[s+11], obj[s+12], obj[s+13], obj[s+14], obj[s+15]); |
| 43 | return 0; |
| 44 | } |
| 45 | } |
| 46 | printf("SUCCESS\n"); |
| 47 | return 1; |
| 48 | } |
| 49 | |
| 50 | static int nand_read_pages(struct ingenic_dev *dev, unsigned int start_page, int num_pages, char *buf, |
| 51 | int length, uint16_t op, char *ret) |
| 52 | { |
| 53 | usb_send_data_address_to_ingenic(dev, start_page); |
| 54 | usb_send_data_length_to_ingenic(dev, num_pages); |
| 55 | |
| 56 | usb_ingenic_nand_ops(dev, op); |
| 57 | |
| 58 | usb_read_data_from_ingenic(dev, buf, length); |
| 59 | |
| 60 | usb_read_data_from_ingenic(dev, ret, 8); |
| 61 | |
| 62 | return 0; |
| 63 | } |
| 64 | int nand_markbad(struct ingenic_dev *dev, uint8_t nand_idx, uint32_t block) |
| 65 | { |
| 66 | char ret[8]; |
| 67 | (void)nand_idx; |
| 68 | |
| 69 | if (usb_get_ingenic_cpu(dev) < 3) { |
| 70 | fprintf(stderr, "Device unboot! Boot it first!\n"); |
| 71 | return -ENODEV; |
| 72 | } |
| 73 | printf("Mark bad block : %d\n", block); |
| 74 | usb_send_data_address_to_ingenic(dev, block); |
| 75 | usb_ingenic_nand_ops(dev, NAND_MARK_BAD); |
| 76 | usb_read_data_from_ingenic(dev, ret, ARRAY_SIZE(ret)); |
| 77 | printf("Mark bad block at %d\n",((ret[3] << 24) | |
| 78 | (ret[2] << 16) | |
| 79 | (ret[1] << 8) | |
| 80 | (ret[0] << 0)) / dev->config.nand_ppb); |
| 81 | |
| 82 | return 0; |
| 83 | } |
| 84 | |
| 85 | int nand_program_check(struct ingenic_dev *dev, uint8_t nand_idx, |
| 86 | unsigned int start_page, const char *data, |
| 87 | uint32_t length, unsigned int mode) |
| 88 | { |
| 89 | unsigned int page_num, cur_page = -1; |
| 90 | unsigned short op; |
| 91 | static char read_back_buf[MAX_TRANSFER_SIZE]; |
| 92 | char ret[8]; |
| 93 | |
| 94 | printf("Writing NAND page %u len %u...\n", start_page, length); |
| 95 | if (length > (unsigned int)MAX_TRANSFER_SIZE) { |
| 96 | fprintf(stderr, "Buffer size too long!\n"); |
| 97 | return -EINVAL; |
| 98 | } |
| 99 | |
| 100 | if (usb_get_ingenic_cpu(dev) < 3) { |
| 101 | fprintf(stderr, "Device unboot! Boot it first!\n"); |
| 102 | return -ENODEV; |
| 103 | } |
| 104 | usb_send_data_to_ingenic(dev, data, length); |
| 105 | |
| 106 | if (mode == NO_OOB) |
| 107 | page_num = DIV_ROUND_UP(length, dev->config.nand_ps); |
| 108 | else |
| 109 | page_num = DIV_ROUND_UP(length, dev->config.nand_ps + dev->config.nand_os); |
| 110 | |
| 111 | op = NAND_OP(nand_idx, NAND_PROGRAM, mode); |
| 112 | |
| 113 | usb_send_data_address_to_ingenic(dev, start_page); |
| 114 | usb_send_data_length_to_ingenic(dev, page_num); |
| 115 | usb_ingenic_nand_ops(dev, op); |
| 116 | |
| 117 | usb_read_data_from_ingenic(dev, ret, 8); |
| 118 | printf("Finish! (len %d start_page %d page_num %d)\n", |
| 119 | length, start_page, page_num); |
| 120 | |
| 121 | switch(mode) { |
| 122 | case NO_OOB: |
| 123 | op = NAND_OP(nand_idx, NAND_READ, NO_OOB); |
| 124 | break; |
| 125 | default: |
| 126 | op = NAND_OP(nand_idx, NAND_READ_RAW, OOB_ECC); |
| 127 | break; |
| 128 | } |
| 129 | |
| 130 | nand_read_pages(dev, start_page, page_num, read_back_buf, length, op, ret); |
| 131 | printf("Checking %d bytes...", length); |
| 132 | |
| 133 | cur_page = (ret[3] << 24) | (ret[2] << 16) | (ret[1] << 8) | |
| 134 | (ret[0] << 0); |
| 135 | |
| 136 | if (start_page < 1 && |
| 137 | dev->config.nand_ps == 4096 && |
| 138 | dev->config.fw_args.cpu_id == 0x4740) { |
| 139 | printf("no check! End at Page: %d\n", cur_page); |
| 140 | } |
| 141 | |
| 142 | if (!error_check(data, read_back_buf, length)) { |
| 143 | // tbd: doesn't the other side skip bad blocks too? Can we just deduct 1 from cur_page? |
| 144 | // tbd: why do we only mark a block as bad if the last page in the block was written? |
| 145 | if (cur_page % dev->config.nand_ppb == 0) |
| 146 | nand_markbad(dev, nand_idx, (cur_page - 1) / dev->config.nand_ppb); |
| 147 | } |
| 148 | |
| 149 | printf("End at Page: %d\n", cur_page); |
| 150 | |
| 151 | /* *start_page = cur_page;*/ |
| 152 | return 0; |
| 153 | } |
| 154 | |
| 155 | int nand_erase(struct ingenic_dev *dev, uint8_t nand_idx, uint32_t start_block, |
| 156 | uint32_t num_blocks) |
| 157 | { |
| 158 | uint32_t end_block; |
| 159 | uint16_t op; |
| 160 | static char ret[8]; |
| 161 | |
| 162 | if (start_block > (unsigned int)NAND_MAX_BLK_NUM) { |
| 163 | fprintf(stderr, "Start block number overflow!\n"); |
| 164 | return -EINVAL; |
| 165 | } |
| 166 | if (num_blocks > (unsigned int)NAND_MAX_BLK_NUM) { |
| 167 | fprintf(stderr, "Length block number overflow!\n"); |
| 168 | return -EINVAL; |
| 169 | } |
| 170 | |
| 171 | if (usb_get_ingenic_cpu(dev) < 3) { |
| 172 | fprintf(stderr, "Device unboot! Boot it first!\n"); |
| 173 | return -ENODEV; |
| 174 | } |
| 175 | |
| 176 | printf("Erasing No.%d device No.%d flash (start_blk %u blk_num %u)......\n", |
| 177 | 0, nand_idx, start_block, num_blocks); |
| 178 | |
| 179 | usb_send_data_address_to_ingenic(dev, start_block); |
| 180 | usb_send_data_length_to_ingenic(dev, num_blocks); |
| 181 | |
| 182 | op = NAND_OP(nand_idx, NAND_ERASE, 0); |
| 183 | usb_ingenic_nand_ops(dev, op); |
| 184 | |
| 185 | usb_read_data_from_ingenic(dev, ret, 8); |
| 186 | printf("Finish!"); |
| 187 | |
| 188 | end_block = ((ret[3] << 24) | (ret[2] << 16) | |
| 189 | (ret[1] << 8) | (ret[0] << 0)) / dev->config.nand_ppb; |
| 190 | printf("Return: %02x %02x %02x %02x %02x %02x %02x %02x (position %d)\n", |
| 191 | ret[0], ret[1], ret[2], ret[3], ret[4], ret[5], ret[6], ret[7], end_block); |
| 192 | if (!dev->config.nand_force_erase) { |
| 193 | /* not force erase, show bad block infomation */ |
| 194 | printf("There are marked bad blocks: %d\n", |
| 195 | end_block - start_block - num_blocks ); |
| 196 | } else { |
| 197 | /* force erase, no bad block infomation can show */ |
| 198 | printf("Force erase, no bad block infomation!\n" ); |
| 199 | } |
| 200 | |
| 201 | return 0; |
| 202 | } |
| 203 | |
| 204 | int nand_program_file(struct ingenic_dev *dev, uint8_t nand_idx, |
| 205 | uint32_t start_page, const char *filename, int mode) |
| 206 | { |
| 207 | uint32_t start_block, num_blocks; |
| 208 | int flen, m, j, k; |
| 209 | unsigned int page_num, code_len, offset, transfer_size; |
| 210 | int fd, status; |
| 211 | struct stat fstat; |
| 212 | static char code_buf[MAX_TRANSFER_SIZE]; |
| 213 | |
| 214 | status = stat(filename, &fstat); |
| 215 | |
| 216 | if (status < 0) { |
| 217 | fprintf(stderr, "Error - can't get file size from '%s': %s\n", |
| 218 | filename, strerror(errno)); |
| 219 | return -EEXIST; |
| 220 | } |
| 221 | flen = fstat.st_size; |
| 222 | |
| 223 | fd = open(filename, O_RDONLY); |
| 224 | if (fd < 0) { |
| 225 | fprintf(stderr, "Error - can't open file '%s': %s\n", |
| 226 | filename, strerror(errno)); |
| 227 | return errno; |
| 228 | } |
| 229 | |
| 230 | printf("Programing No.%d device, flen %d, start page %d...\n", 0, |
| 231 | flen, start_page); |
| 232 | |
| 233 | /* printf("length %d flen %d\n", n_in.length, flen); */ |
| 234 | if (mode == NO_OOB) |
| 235 | transfer_size = (dev->config.nand_ppb * dev->config.nand_ps); |
| 236 | else |
| 237 | transfer_size = (dev->config.nand_ppb * (dev->config.nand_ps + |
| 238 | dev->config.nand_os)); |
| 239 | |
| 240 | start_block = start_page / dev->config.nand_ppb; |
| 241 | num_blocks = flen / (transfer_size - 1) + 1; |
| 242 | |
| 243 | if (nand_erase(dev, nand_idx, start_block, num_blocks)) |
| 244 | return -1; |
| 245 | |
| 246 | m = flen / transfer_size; |
| 247 | j = flen % transfer_size; |
| 248 | |
| 249 | printf("Size to send %d, transfer_size %d\n", flen, transfer_size); |
| 250 | /* printf("Image type : %s\n", IMAGE_TYPE[mode]);*/ |
| 251 | printf("It will cause %d times buffer transfer.\n", j == 0 ? m : m + 1); |
| 252 | |
| 253 | if (mode == NO_OOB) |
| 254 | page_num = transfer_size / dev->config.nand_ps; |
| 255 | else |
| 256 | page_num = transfer_size / (dev->config.nand_ps + dev->config.nand_os); |
| 257 | |
| 258 | |
| 259 | offset = 0; |
| 260 | for (k = 0; k < m; k++) { |
| 261 | code_len = transfer_size; |
| 262 | status = read(fd, code_buf, code_len); |
| 263 | if (status < (int)code_len) { |
| 264 | fprintf(stderr, "Error - can't read file '%s': %s\n", |
| 265 | filename, strerror(errno)); |
| 266 | goto close; |
| 267 | } |
| 268 | |
| 269 | if (nand_program_check(dev, nand_idx, start_page, code_buf, code_len, mode) == -1) |
| 270 | goto close; |
| 271 | |
| 272 | /* if (start_page - nand_in->start > dev->config.nand_ppb) |
| 273 | printf("Skip a old bad block !\n");*/ |
| 274 | |
| 275 | offset += code_len ; |
| 276 | } |
| 277 | |
| 278 | if (j) { |
| 279 | code_len = j; |
| 280 | if (j % dev->config.nand_ps) |
| 281 | j += dev->config.nand_ps - (j % dev->config.nand_ps); |
| 282 | memset(code_buf, 0, j); /* set all to null */ |
| 283 | |
| 284 | status = read(fd, code_buf, code_len); |
| 285 | |
| 286 | if (status < (int)code_len) { |
| 287 | fprintf(stderr, "Error - can't read file '%s': %s\n", |
| 288 | filename, strerror(errno)); |
| 289 | goto close; |
| 290 | } |
| 291 | |
| 292 | if (nand_program_check(dev, nand_idx, start_page, code_buf, j, mode) == -1) |
| 293 | goto close; |
| 294 | |
| 295 | /* |
| 296 | if (start_page - nand_in->start > dev->config.nand_ppb) |
| 297 | printf("Skip a old bad block !"); |
| 298 | */ |
| 299 | } |
| 300 | close: |
| 301 | close(fd); |
| 302 | return 0; |
| 303 | } |
| 304 | |
| 305 | int nand_prog(struct ingenic_dev *dev, uint8_t nand_idx, uint32_t start_page, |
| 306 | const char *filename, int mode) |
| 307 | { |
| 308 | if (dev->config.nand_plane > 1) |
| 309 | printf("ERROR"); |
| 310 | else |
| 311 | nand_program_file(dev, nand_idx, start_page, filename, mode); |
| 312 | |
| 313 | return 0; |
| 314 | } |
| 315 | |
| 316 | int nand_query(struct ingenic_dev *dev, uint8_t nand_idx) |
| 317 | { |
| 318 | uint16_t op; |
| 319 | char ret[8] = {0,0,0,0,0,0,0,0}; |
| 320 | int ret2; |
| 321 | |
| 322 | if (usb_get_ingenic_cpu(dev) < 3) { |
| 323 | fprintf(stderr, "Device unboot! Boot it first!\n"); |
| 324 | return -ENODEV; |
| 325 | } |
| 326 | |
| 327 | printf("ID of No.%u device No.%u flash: \n", 0, nand_idx); |
| 328 | |
| 329 | op = NAND_OP(nand_idx, NAND_QUERY, 0); |
| 330 | |
| 331 | usb_ingenic_nand_ops(dev, op); |
| 332 | ret2 = usb_read_data_from_ingenic(dev, ret, ARRAY_SIZE(ret)); |
| 333 | |
| 334 | if (ret2 < 0) |
| 335 | return ret2; |
| 336 | |
| 337 | printf("Vendor ID :0x%x \n", (unsigned char)ret[0]); |
| 338 | printf("Product ID :0x%x \n", (unsigned char)ret[1]); |
| 339 | printf("Chip ID :0x%x \n", (unsigned char)ret[2]); |
| 340 | printf("Page ID :0x%x \n", (unsigned char)ret[3]); |
| 341 | printf("Plane ID :0x%x \n", (unsigned char)ret[4]); |
| 342 | |
| 343 | usb_read_data_from_ingenic(dev, ret, ARRAY_SIZE(ret)); |
| 344 | printf("Operation status: Success!\n"); |
| 345 | |
| 346 | return 0; |
| 347 | } |
| 348 | |
| 349 | int nand_read(struct ingenic_dev *dev, uint8_t nand_idx, int mode, |
| 350 | uint32_t start_page, uint32_t length, uint32_t ram_addr, |
| 351 | nand_read_cb_t callback, void *userdata) |
| 352 | { |
| 353 | uint16_t op; |
| 354 | uint32_t page; |
| 355 | uint32_t request_length; |
| 356 | uint32_t pages_per_request; |
| 357 | char ret[8]; |
| 358 | char *buf; |
| 359 | int ret2; |
| 360 | |
| 361 | if (start_page > NAND_MAX_PAGE_NUM) { |
| 362 | fprintf(stderr, "Page number overflow!\n"); |
| 363 | return -EINVAL; |
| 364 | } |
| 365 | if (usb_get_ingenic_cpu(dev) < 3) { |
| 366 | fprintf(stderr, "Device unboot! Boot it first!\n"); |
| 367 | return -EINVAL; |
| 368 | } |
| 369 | if (nand_idx >= 16) |
| 370 | return -EINVAL; |
| 371 | |
| 372 | printf("Reading from No.%u device No.%u flash....\n", 0, nand_idx); |
| 373 | |
| 374 | |
| 375 | switch(mode) { |
| 376 | case NAND_READ: |
| 377 | op = NAND_OP(nand_idx, NAND_READ, NO_OOB); |
| 378 | break; |
| 379 | case NAND_READ_OOB: |
| 380 | op = NAND_OP(nand_idx, NAND_READ_OOB, 0); |
| 381 | break; |
| 382 | case NAND_READ_RAW: |
| 383 | op = NAND_OP(nand_idx, NAND_READ_RAW, OOB_ECC); |
| 384 | break; |
| 385 | case NAND_READ_TO_RAM: |
| 386 | op = NAND_OP(nand_idx, NAND_READ_TO_RAM, NO_OOB); |
| 387 | usb_ingenic_start(dev, VR_PROGRAM_START1, ram_addr); |
| 388 | break; |
| 389 | default: |
| 390 | return -EINVAL; |
| 391 | } |
| 392 | |
| 393 | pages_per_request = 1; |
| 394 | if (mode == NAND_READ_OOB || mode == NAND_READ_RAW) |
| 395 | request_length = (dev->config.nand_ps + dev->config.nand_os) * pages_per_request; |
| 396 | else |
| 397 | request_length = dev->config.nand_ps * pages_per_request; |
| 398 | |
| 399 | page = start_page; |
| 400 | |
| 401 | buf = malloc(request_length); |
| 402 | |
| 403 | while (length > 0) { |
| 404 | if (request_length > length) |
| 405 | request_length = length; |
| 406 | |
| 407 | nand_read_pages(dev, page, pages_per_request, buf, request_length, op, ret); |
| 408 | |
| 409 | ret2 = callback(userdata, buf, request_length); |
| 410 | if (ret2) |
| 411 | return ret2; |
| 412 | |
| 413 | length -= request_length; |
| 414 | page += pages_per_request; |
| 415 | } |
| 416 | |
| 417 | printf("Operation end position : %u \n", |
| 418 | (ret[3]<<24)|(ret[2]<<16)|(ret[1]<<8)|(ret[0]<<0)); |
| 419 | free(buf); |
| 420 | |
| 421 | return 0; |
| 422 | } |
| 423 | |