int early_board_init(void) { const int cpu_ref = DEFAULT_CPU_REF_FREQUENCY_MHZ; int i; gd->arch.ddr_ref_hertz = 50000000; gd->arch.tlv_addr = CONFIG_SYS_I2C_EEPROM_ADDR; for (i = 0; i < ARRAY_SIZE(tuple_addresses); i++) { gd->arch.tlv_addr = tuple_addresses[i]; debug("%s: Trying TLV EEPROM at address 0x%x\n", __func__, tuple_addresses[i]); octeon_board_get_descriptor(CVMX_BOARD_TYPE_GENERIC, 1, 0); if (gd->board_type != CVMX_BOARD_TYPE_GENERIC) break; } if (i == ARRAY_SIZE(tuple_addresses) || gd->board_type == CVMX_BOARD_TYPE_GENERIC) { puts("ERROR: Board TLV descriptor not found! Cannot continue!\n" "Use the tlv_eeprom command to program the board type.\n"); return -1; } octeon_board_get_clock_info(DEFAULT_DDR3_CLOCK_FREQ_MHZ); printf("Board type: %s\n", cvmx_board_type_to_string(gd->board_type)); /* Read CPU clock multiplier */ gd->cpu_clk = octeon_get_cpu_multiplier() * cpu_ref * 1000000; return 0; }
void __octeon_board_get_descriptor(enum cvmx_board_types_enum type, int rev_major, int rev_minor) { struct bootloader_header *header; gd->arch.board_desc.board_type = CVMX_BOARD_TYPE_NULL; debug("%s(%s, %d, %d)\n", __func__, cvmx_board_type_to_string(type), rev_major, rev_minor); if (type == CVMX_BOARD_TYPE_NULL) { debug("NULL board type passed, extracting board type from header\n"); header = CASTPTR(struct bootloader_header, CONFIG_SYS_TEXT_BASE); if (validate_bootloader_header(header)) { gd->flags |= GD_FLG_BOARD_DESC_MISSING; gd->arch.board_desc.rev_major = header->maj_rev; gd->arch.board_desc.rev_minor = header->min_rev; gd->arch.board_desc.board_type = header->board_type; strncpy((char *)(gd->arch.board_desc.serial_str), "unknown", SERIAL_LEN); debug("header board type: %s, revision %d.%d\n", cvmx_board_type_to_string(header->board_type), header->maj_rev, header->min_rev); } }
/** * Return the board name as a constant string * * @return board name */ const char *octeon_board_type_string(void) { return cvmx_board_type_to_string(octeon_bootinfo->board_type); }
void platform_start(__register_t a0, __register_t a1, __register_t a2 __unused, __register_t a3) { const struct octeon_feature_description *ofd; uint64_t platform_counter_freq; int rv; mips_postboot_fixup(); /* * Initialize boot parameters so that we can determine things like * which console we shoud use, etc. */ octeon_boot_params_init(a3); /* Initialize pcpu stuff */ mips_pcpu0_init(); mips_timer_early_init(cvmx_sysinfo_get()->cpu_clock_hz); /* Initialize console. */ cninit(); /* * Display information about the CPU. */ #if !defined(OCTEON_MODEL) printf("Using runtime CPU model checks.\n"); #else printf("Compiled for CPU model: " __XSTRING(OCTEON_MODEL) "\n"); #endif strcpy(cpu_model, octeon_model_get_string(cvmx_get_proc_id())); printf("CPU Model: %s\n", cpu_model); printf("CPU clock: %uMHz Core Mask: %#x\n", cvmx_sysinfo_get()->cpu_clock_hz / 1000000, cvmx_sysinfo_get()->core_mask); rv = octeon_model_version_check(cvmx_get_proc_id()); if (rv == -1) panic("%s: kernel not compatible with this processor.", __func__); /* * Display information about the board. */ #if defined(OCTEON_BOARD_CAPK_0100ND) strcpy(cpu_board, "CAPK-0100ND"); if (cvmx_sysinfo_get()->board_type != CVMX_BOARD_TYPE_CN3010_EVB_HS5) { panic("Compiled for %s, but board type is %s.", cpu_board, cvmx_board_type_to_string(cvmx_sysinfo_get()->board_type)); } #else strcpy(cpu_board, cvmx_board_type_to_string(cvmx_sysinfo_get()->board_type)); #endif printf("Board: %s\n", cpu_board); printf("Board Type: %u Revision: %u/%u\n", cvmx_sysinfo_get()->board_type, cvmx_sysinfo_get()->board_rev_major, cvmx_sysinfo_get()->board_rev_minor); printf("Serial number: %s\n", cvmx_sysinfo_get()->board_serial_number); /* * Additional on-chip hardware/settings. * * XXX Display PCI host/target? What else? */ printf("MAC address base: %6D (%u configured)\n", cvmx_sysinfo_get()->mac_addr_base, ":", cvmx_sysinfo_get()->mac_addr_count); octeon_ciu_reset(); /* * Convert U-Boot 'bootoctlinux' loader command line arguments into * boot flags and kernel environment variables. */ bootverbose = 1; octeon_init_kenv(a3); /* * For some reason on the cn38xx simulator ebase register is set to * 0x80001000 at bootup time. Move it back to the default, but * when we move to having support for multiple executives, we need * to rethink this. */ mips_wr_ebase(0x80000000); octeon_memory_init(); init_param1(); init_param2(physmem); mips_cpu_init(); pmap_bootstrap(); mips_proc0_init(); mutex_init(); kdb_init(); #ifdef KDB if (boothowto & RB_KDB) kdb_enter(KDB_WHY_BOOTFLAGS, "Boot flags requested debugger"); #endif cpu_clock = cvmx_sysinfo_get()->cpu_clock_hz; platform_counter_freq = cpu_clock; octeon_timecounter.tc_frequency = cpu_clock; platform_timecounter = &octeon_timecounter; mips_timer_init_params(platform_counter_freq, 0); set_cputicker(octeon_get_ticks, cpu_clock, 0); #ifdef SMP /* * Clear any pending IPIs. */ cvmx_write_csr(CVMX_CIU_MBOX_CLRX(0), 0xffffffff); #endif printf("Octeon SDK: %s\n", OCTEON_SDK_VERSION_STRING); printf("Available Octeon features:"); for (ofd = octeon_feature_descriptions; ofd->ofd_string != NULL; ofd++) if (octeon_has_feature(ofd->ofd_feature)) printf(" %s", ofd->ofd_string); printf("\n"); }
int cvmx_llm_initialize_desc(llm_descriptor_t *llm_desc_ptr) { cvmx_sysinfo_t *sys_ptr; sys_ptr = cvmx_sysinfo_get(); llm_descriptor_t default_llm_desc; memset(&default_llm_desc, 0, sizeof(default_llm_desc)); if (sys_ptr->board_type == CVMX_BOARD_TYPE_SIM) { cvmx_dprintf("Skipping llm configuration for simulator.\n"); return 0; } if (sys_ptr->board_type == CVMX_BOARD_TYPE_EBH3100) { /* CN31xx DFA memory is DDR based, so it is completely different from the CN38XX DFA memory ** config descriptors are not supported yet.*/ cvmx_dprintf("Warning: preliminary DFA memory configuration\n"); cn31xx_dfa_memory_init(); return(256*1024*1024); } /* If no descriptor passed, generate default descriptor based on board type. ** Fail if no default available for given board type */ if (!llm_desc_ptr) { /* Get default descriptor */ if (0 > cvmx_llm_get_default_descriptor(&default_llm_desc)) return -1; /* Disable second port depending on CVMX config */ if (CVMX_LLM_NUM_PORTS == 1) default_llm_desc.rld0_bunks = 0; // For single port: Force RLD0(P1) to appear EMPTY cvmx_dprintf("Using default LLM configuration for board %s (%d)\n", cvmx_board_type_to_string(sys_ptr->board_type), sys_ptr->board_type); llm_desc_ptr = &default_llm_desc; } rldram_csr_config_t ebt3000_rld_cfg; if (!rld_csr_config_generate(llm_desc_ptr, &ebt3000_rld_cfg)) { cvmx_dprintf("Configuring %d llm port(s).\n", !!llm_desc_ptr->rld0_bunks + !!llm_desc_ptr->rld1_bunks); write_rld_cfg(&ebt3000_rld_cfg); } else { cvmx_dprintf("Error creating rldram configuration\n"); return(-1); } /* Compute how much memory is configured ** Memory is interleaved, so if one port has more than the other some memory is not usable */ /* If both ports are enabled, handle the case where one port has more than the other. ** This is an unusual and not recommended configuration that exists on the ebt3000 board */ if (!!llm_desc_ptr->rld0_bunks && !!llm_desc_ptr->rld1_bunks) llm_desc_ptr->rld0_mbytes = llm_desc_ptr->rld1_mbytes = MIN(llm_desc_ptr->rld0_mbytes, llm_desc_ptr->rld1_mbytes); return(((!!llm_desc_ptr->rld0_bunks) * llm_desc_ptr->rld0_mbytes + (!!llm_desc_ptr->rld1_bunks) * llm_desc_ptr->rld1_mbytes) * 1024*1024); }
int cvmx_llm_get_default_descriptor(llm_descriptor_t *llm_desc_ptr) { cvmx_sysinfo_t *sys_ptr; sys_ptr = cvmx_sysinfo_get(); if (!llm_desc_ptr) return -1; memset(llm_desc_ptr, 0, sizeof(llm_descriptor_t)); llm_desc_ptr->cpu_hz = cvmx_clock_get_rate(CVMX_CLOCK_CORE); if (sys_ptr->board_type == CVMX_BOARD_TYPE_EBT3000) { // N3K->RLD0 Address Swizzle strcpy(llm_desc_ptr->addr_rld0_fb_str, "22 21 20 19 18 17 16 15 14 13 12 11 10 09 08 07 06 05 04 03 02 01 00"); strcpy(llm_desc_ptr->addr_rld0_bb_str, "22 21 19 20 08 07 06 05 04 03 02 01 00 09 18 17 16 15 14 13 12 11 10"); // N3K->RLD1 Address Swizzle strcpy(llm_desc_ptr->addr_rld1_fb_str, "22 21 20 19 18 17 16 15 14 13 12 11 10 09 08 07 06 05 04 03 02 01 00"); strcpy(llm_desc_ptr->addr_rld1_bb_str, "22 21 20 00 08 07 06 05 04 13 02 01 03 09 18 17 16 15 14 10 12 11 19"); /* NOTE: The ebt3000 has a strange RLDRAM configuration for validation purposes. It is not recommended to have ** different amounts of memory on different ports as that renders some memory unusable */ llm_desc_ptr->rld0_bunks = 2; llm_desc_ptr->rld1_bunks = 2; llm_desc_ptr->rld0_mbytes = 128; // RLD0: 4x 32Mx9 llm_desc_ptr->rld1_mbytes = 64; // RLD1: 2x 16Mx18 } else if (sys_ptr->board_type == CVMX_BOARD_TYPE_EBT5800) { strcpy(llm_desc_ptr->addr_rld0_fb_str, "22 21 20 19 18 17 16 15 14 13 12 11 10 09 08 07 06 05 04 03 02 01 00"); strcpy(llm_desc_ptr->addr_rld0_bb_str, "22 21 20 00 08 07 06 05 04 13 02 01 03 09 18 17 16 15 14 10 12 11 19"); strcpy(llm_desc_ptr->addr_rld1_fb_str, "22 21 20 19 18 17 16 15 14 13 12 11 10 09 08 07 06 05 04 03 02 01 00"); strcpy(llm_desc_ptr->addr_rld1_bb_str, "22 21 20 00 08 07 06 05 04 13 02 01 03 09 18 17 16 15 14 10 12 11 19"); llm_desc_ptr->rld0_bunks = 2; llm_desc_ptr->rld1_bunks = 2; llm_desc_ptr->rld0_mbytes = 128; llm_desc_ptr->rld1_mbytes = 128; llm_desc_ptr->max_rld_clock_mhz = 400; /* CN58XX needs a max clock speed for selecting optimal divisor */ } else if (sys_ptr->board_type == CVMX_BOARD_TYPE_EBH3000) { strcpy(llm_desc_ptr->addr_rld0_fb_str, "22 21 20 19 18 17 16 15 14 13 12 11 10 09 08 07 06 05 04 03 02 01 00"); strcpy(llm_desc_ptr->addr_rld0_bb_str, "22 21 19 20 08 07 06 05 04 03 02 01 00 09 18 17 16 15 14 13 12 11 10"); strcpy(llm_desc_ptr->addr_rld1_fb_str, "22 21 20 19 18 17 16 15 14 13 12 11 10 09 08 07 06 05 04 03 02 01 00"); strcpy(llm_desc_ptr->addr_rld1_bb_str, "22 21 19 20 08 07 06 05 04 03 02 01 00 09 18 17 16 15 14 13 12 11 10"); llm_desc_ptr->rld0_bunks = 2; llm_desc_ptr->rld1_bunks = 2; llm_desc_ptr->rld0_mbytes = 128; llm_desc_ptr->rld1_mbytes = 128; } else if (sys_ptr->board_type == CVMX_BOARD_TYPE_THUNDER) { if (sys_ptr->board_rev_major >= 4) { strcpy(llm_desc_ptr->addr_rld0_fb_str, "22 21 13 11 01 02 07 19 03 18 10 12 20 06 04 08 17 05 14 16 00 09 15"); strcpy(llm_desc_ptr->addr_rld0_bb_str, "22 21 11 13 04 08 17 05 14 16 00 09 15 06 01 02 07 19 03 18 10 12 20"); strcpy(llm_desc_ptr->addr_rld1_fb_str, "22 21 02 19 18 17 16 09 14 13 20 11 10 01 08 03 06 15 04 07 05 12 00"); strcpy(llm_desc_ptr->addr_rld1_bb_str, "22 21 19 02 08 03 06 15 04 07 05 12 00 01 18 17 16 09 14 13 20 11 10"); } else { strcpy(llm_desc_ptr->addr_rld0_fb_str, "22 21 20 19 18 17 16 15 14 13 12 11 10 09 08 07 06 05 04 03 02 01 00"); strcpy(llm_desc_ptr->addr_rld0_bb_str, "22 21 20 19 18 17 16 15 14 13 12 11 10 09 08 07 06 05 04 03 02 01 00"); strcpy(llm_desc_ptr->addr_rld1_fb_str, "22 21 20 19 18 17 16 15 14 13 12 11 10 09 08 07 06 05 04 03 02 01 00"); strcpy(llm_desc_ptr->addr_rld1_bb_str, "22 21 20 19 18 17 16 15 14 13 12 11 10 09 08 07 06 05 04 03 02 01 00"); } llm_desc_ptr->rld0_bunks = 2; llm_desc_ptr->rld1_bunks = 2; llm_desc_ptr->rld0_mbytes = 128; llm_desc_ptr->rld1_mbytes = 128; } else if (sys_ptr->board_type == CVMX_BOARD_TYPE_NICPRO2) { strcpy(llm_desc_ptr->addr_rld0_fb_str, "22 21 20 19 18 17 16 15 14 13 12 11 10 09 08 07 06 05 04 03 02 01 00"); strcpy(llm_desc_ptr->addr_rld0_bb_str, "22 21 19 20 08 07 06 05 04 03 02 01 00 09 18 17 16 15 14 13 12 11 10"); strcpy(llm_desc_ptr->addr_rld1_fb_str, "22 21 20 19 18 17 16 15 14 13 12 11 10 09 08 07 06 05 04 03 02 01 00"); strcpy(llm_desc_ptr->addr_rld1_bb_str, "22 21 19 20 08 07 06 05 04 03 02 01 00 09 18 17 16 15 14 13 12 11 10"); llm_desc_ptr->rld0_bunks = 2; llm_desc_ptr->rld1_bunks = 2; llm_desc_ptr->rld0_mbytes = 256; llm_desc_ptr->rld1_mbytes = 256; llm_desc_ptr->max_rld_clock_mhz = 400; /* CN58XX needs a max clock speed for selecting optimal divisor */ } else if (sys_ptr->board_type == CVMX_BOARD_TYPE_EBH3100) { /* CN31xx DFA memory is DDR based, so it is completely different from the CN38XX DFA memory */ llm_desc_ptr->rld0_bunks = 1; llm_desc_ptr->rld0_mbytes = 256; } else if (sys_ptr->board_type == CVMX_BOARD_TYPE_KBP) { strcpy(llm_desc_ptr->addr_rld0_fb_str, ""); strcpy(llm_desc_ptr->addr_rld0_bb_str, ""); llm_desc_ptr->rld0_bunks = 0; llm_desc_ptr->rld0_mbytes = 0; strcpy(llm_desc_ptr->addr_rld1_fb_str, "22 21 20 19 18 17 16 15 14 13 12 11 10 09 08 07 06 05 04 03 02 01 00"); strcpy(llm_desc_ptr->addr_rld1_bb_str, "22 21 20 19 18 17 16 15 14 13 12 11 10 09 08 07 06 05 04 03 02 01 00"); llm_desc_ptr->rld1_bunks = 2; llm_desc_ptr->rld1_mbytes = 64; } else { cvmx_dprintf("No default LLM configuration available for board %s (%d)\n", cvmx_board_type_to_string(sys_ptr->board_type), sys_ptr->board_type); return -1; } return(0); }
/** * Command for updating a bootloader image in flash. This function * parses the arguments, and validates the header (if header exists.) * Actual flash updating is done by flash type specific functions. * * @return 0 on success * 1 on failure */ int do_bootloader_update(cmd_tbl_t * cmdtp, int flag, int argc, char *const argv[]) { uint32_t image_addr = 0; uint32_t image_len = 0; uint32_t burn_addr = 0; int failsafe = 0; const bootloader_header_t *header; int force_nand = 0; int force_spi = 0; if (argc >= 2) { if (!strcmp(argv[1], "nand")) { debug("Forced NAND bootloader update\n"); force_nand = 1; argc--; argv++; } else if (!strcmp(argv[1], "spi")) { debug("Forced SPI bootloader update\n"); force_spi = 1; argc--; argv++; if (!strcmp(argv[1], "failsafe")) { failsafe = 1; argc--; argv++; } } } if (argc >= 2) image_addr = simple_strtoul(argv[1], NULL, 16); if (argc >= 3) image_len = simple_strtoul(argv[2], NULL, 16); if (argc >= 4) { if (force_spi || force_nand) { if (!strcmp("failsafe", argv[3])) failsafe = 1; } else { burn_addr = simple_strtoul(argv[3], NULL, 16); } } if ((argc >= 5) && (strcmp("failsafe", argv[4]) == 0)) failsafe = 1; /* If we don't support failsafe images, we need to put the image at the * base of flash, so we treat all images like failsafe image in this * case. */ #ifdef CONFIG_OCTEON_NO_FAILSAFE failsafe = 1; burn_addr = 0x1fc00000; #endif if (!burn_addr) burn_addr = getenv_hex("burnaddr", 0); if (!image_addr) { image_addr = getenv_hex("fileaddr", load_addr); if (!image_addr) { puts("ERROR: Unable to get image address from " "'fileaddr' environment variable\n"); return 1; } } DBGUPD("%s: burn address: 0x%x, image address: 0x%x\n", __func__, burn_addr, image_addr); /* Figure out what kind of flash we are going to update. This will * typically come from the bootloader header. If the bootloader does * not have a header, then it is assumed to be a legacy NOR image, and * a destination NOR flash address must be supplied. NAND images * _must_ have a header. */ header = (void *)image_addr; if (header->magic != BOOTLOADER_HEADER_MAGIC) { /* No header, assume headerless NOR bootloader image */ puts("No valid bootloader header found. Assuming old headerless image\n" "Image checks cannot be performed\n"); if (!burn_addr) { burn_addr = CONFIG_SYS_NORMAL_BOOTLOADER_BASE; DBGUPD("Unable to get burn address from 'burnaddr' environment variable,\n"); DBGUPD(" using default 0x%x\n", burn_addr); } /* We only need image length for the headerless case */ if (!image_len) { image_len = getenv_hex("filesize", 0); if (!image_len) { puts("ERROR: Unable to get image size from " "'filesize' environment variable\n"); return 1; } } return do_bootloader_update_nor(image_addr, image_len, burn_addr, failsafe); } /* We have a header, so validate image */ if (validate_header(header)) { puts("ERROR: Image header has invalid CRC.\n"); return 1; } if (validate_data(header)) /* Errors printed */ return 1; /* We now have a valid image, so determine what to do with it. */ puts("Valid bootloader image found.\n"); /* Check to see that it is for the board we are running on */ if (header->board_type != cvmx_sysinfo_get()->board_type) { printf("Current board type: %s (%d), image board type: %s (%d)\n", cvmx_board_type_to_string(cvmx_sysinfo_get()->board_type), cvmx_sysinfo_get()->board_type, cvmx_board_type_to_string(header->board_type), header->board_type); if (cvmx_sysinfo_get()->board_type != CVMX_BOARD_TYPE_GENERIC && header->board_type != CVMX_BOARD_TYPE_GENERIC) { puts("ERROR: Bootloader image is not for this board type.\n"); return 1; } else { puts("Loading mismatched image since current " "or new bootloader is generic.\n"); } } /* SDK 1.9.0 NOR images have rev of 1.1 and unkown image_type */ if (((header->image_type == BL_HEADER_IMAGE_NOR) || (header->image_type == BL_HEADER_IMAGE_UNKNOWN && header->maj_rev == 1 && header->min_rev == 1)) && !force_nand && !force_spi) { debug("Updating NOR bootloader\n"); return do_bootloader_update_nor(image_addr, 0, burn_addr, failsafe); #if defined(CONFIG_CMD_OCTEON_NAND) || defined(CONFIG_OCTEON_NAND_BOOT_END) } else if (!force_spi && (header->image_type == BL_HEADER_IMAGE_STAGE2 || header->image_type == BL_HEADER_IMAGE_STAGE3 || force_nand)) { debug("Updating NAND bootloader\n"); return (do_bootloader_update_nand(image_addr)); #endif #if defined(CONFIG_OCTEON_SPI_BOOT_END) } else if (!force_nand && (header->image_type == BL_HEADER_IMAGE_STAGE2 || header->image_type == BL_HEADER_IMAGE_STAGE3 || force_spi)) { debug("Updating SPI bootloader\n"); return do_bootloader_update_spi(image_addr, header->dlen + header->hlen, failsafe, false); #else } else { puts("ERROR: This bootloader not compiled for this medium\n"); return 1; #endif } return 1; }
int do_oct_nand(cmd_tbl_t * cmdtp, int flag, int argc, char * const argv[]) { u64 buf_storage[CVMX_NAND_MAX_PAGE_AND_OOB_SIZE / 8]; unsigned char *buf = (unsigned char *)buf_storage; int rc; const char *cmd; if (argc < 2) goto usage; cmd = argv[1]; if (!strcmp("probe", cmd)) { rc = oct_nand_probe(); if (rc < 0) { printf("NAND flash not found\n"); } else { printf("probe found NAND flash on boot bus %d.\n", rc); } return 0; } else if (!strcmp("dump", cmd)) { /* Only dump full pages.... */ u64 page, end; u64 start_addr, len; int page_size = cvmx_nand_get_page_size(chip); int oob_size = cvmx_nand_get_oob_size(chip); if (argc < 3) goto usage; start_addr = simple_strtoull(argv[2], NULL, 16); if (argc == 4) len = simple_strtoull(argv[3], NULL, 16); else len = 0; if (!page_size) { printf("ERROR: No NAND configured (run probe)\n"); return 1; } /* Convert from addresses to pages */ page = start_addr / page_size; if (!len) end = page; else end = (start_addr + len) / page_size; while (page <= end) { /* Read the next block */ int i; int read_size = page_size + oob_size; /* Read more than required in cases when total read size * is not a multiple of 8. */ read_size = (read_size + 7) & ~0x7; int bytes = cvmx_nand_page_read(chip, page_size * page, cvmx_ptr_to_phys(buf), read_size); int pages_per_block = cvmx_nand_get_pages_per_block(chip); if (bytes != read_size) { printf("Error reading page %llu, " "bytes read: %d\n", page, bytes); return 1; } /* Dump page data */ printf("Address 0x%llx, (Block 0x%llx, page 0x%llx) " "data:\n", page * page_size, page / pages_per_block, page % pages_per_block); for (i = 0; i < page_size; i++) { if (i % 16 == 0) printf("0x%04llx:", page * page_size + i); printf(" %02x", buf[i]); if (i % 16 == 15) printf("\n"); } /* Dump OOB data */ printf("Address 0x%llx, (Block 0x%llx, page 0x%llx) OOB:\n", page * page_size, page / pages_per_block, page % pages_per_block); for (i = 0; i < oob_size; i++) { if (i % 16 == 0) printf("0x%04x:", i); printf(" %02x", buf[page_size + i]); if (i % 16 == 15) printf("\n"); } printf("\n"); page++; } return 0; } else if (!strcmp("erase", cmd)) { u64 start_addr, length; int force = 0; u64 i; int page_size = cvmx_nand_get_page_size(chip); int pages_per_block = cvmx_nand_get_pages_per_block(chip); if (argc < 3 || argc > 5) goto usage; start_addr = simple_strtoull(argv[2], NULL, 16); if (argc >= 4) length = simple_strtoull(argv[3], NULL, 16); else length = page_size * pages_per_block; if (argc == 5) { if (strcmp(argv[4], "force")) goto usage; printf("WARNING: Forced erase: erasing bad blocks.\n"); force = 1; } if (!page_size) { printf("ERROR: No NAND configured (run probe)\n"); return 1; } if (start_addr & ((u64) page_size * pages_per_block - 1)) { printf("ERROR: erase start not at block boundary " "(maybe want: 0x%llx or 0x%llx)\n", start_addr & ~((u64) page_size * pages_per_block - 1), (start_addr + page_size * pages_per_block) & ~((u64) page_size * pages_per_block - 1)); return 1; } if ((length & (page_size * pages_per_block - 1))) { printf ("ERROR: erase length not multiple of block size " "(maybe want: 0x%llx)\n", (length + (page_size * pages_per_block - 1)) & ~(page_size * pages_per_block - 1)); return 1; } for (i = start_addr; i < start_addr + length; i += page_size * pages_per_block) { if (oct_nand_block_is_bad(chip, i, 1)) { if (force) { printf("WARNING: Erasing bad bock at " "address 0x%llx\n", i); } else { printf("Not erasing bad block at " "address 0x%llx\n", i); continue; } } if (cvmx_nand_block_erase(chip, i)) { printf("cvmx_nand_block_erase() failed, " "addr 0x%08llx\n", i); return 1; } } return 0; } else if (!strcmp("write", cmd)) { u64 nand_addr; u32 dram_addr, length; int page_size = cvmx_nand_get_page_size(chip); if (!page_size) { printf("ERROR: No NAND configured (run probe)\n"); return 1; } if (argc != 3 && argc != 5) goto usage; nand_addr = simple_strtoull(argv[2], NULL, 16); if (argc == 5) { dram_addr = simple_strtoul(argv[3], NULL, 16); length = simple_strtoul(argv[4], NULL, 16); } else { /* We look up the length/address from environment * variables.... * filesize=3410 * fileaddr=20000000 */ if (getenv("filesize") && getenv("fileaddr")) { dram_addr = simple_strtoul(getenv("fileaddr"), NULL, 16); length = simple_strtoul(getenv("filesize"), NULL, 16); /* Round length up to pagesize */ length = (length + page_size - 1) & ~(page_size - 1); } else { printf("ERROR: filesize and fileaddr " "environment variables\n" "must be set if address and size " "are not given\n"); goto usage; } } unsigned char *data_ptr = (void *)dram_addr; if (nand_addr & (page_size - 1)) { printf("ERROR: write address not at page boundary " "(maybe want: 0x%08llx)\n", nand_addr & ~(page_size - 1)); return 1; } return oct_nand_boot_write(nand_addr, data_ptr, length, 0); } else if (!strcmp("scan", cmd)) { u64 nand_addr; bootloader_header_t header_copy; int page_size = cvmx_nand_get_page_size(chip); int pages_per_block = cvmx_nand_get_pages_per_block(chip); int block_size = page_size * pages_per_block; int errors = 0; int read_size = CVMX_NAND_BOOT_ECC_BLOCK_SIZE + CVMX_NAND_BOOT_ECC_ECC_SIZE; if (!page_size) { printf("ERROR: No NAND configured (run probe)\n"); return 1; } printf("Scanning NAND for recognized images\n"); for (nand_addr = 0; nand_addr < MAX_NAND_SEARCH_ADDR; nand_addr += page_size) { /* Adjust the read address based on location in page */ int offset = ((nand_addr & (page_size - 1)) / CVMX_NAND_BOOT_ECC_BLOCK_SIZE) * CVMX_NAND_BOOT_ECC_ECC_SIZE; int bytes = cvmx_nand_page_read(chip, nand_addr + offset, cvmx_ptr_to_phys(buf), read_size); if (bytes != read_size) { printf("Error reading NAND addr 0x%08llx " "(bytes_read: %d, expected: %d)\n", nand_addr, bytes, read_size); return 1; } errors = cvmx_nand_correct_boot_ecc(buf); if (errors <= 1) { const bootloader_header_t *header = (const bootloader_header_t *)buf; /* Block is good, see if it contains a * bootloader image header */ if ((header->magic == BOOTLOADER_HEADER_MAGIC)) { int rval; /* Check the CRC of the header */ u32 crc; crc = crc32(0, (void *)header, 12); crc = crc32(crc, (void *)header + 16, header->hlen - 16); if (crc != header->hcrc) { printf("ERROR: 0x%llx Header " "CRC mismatch, expected:" " 0x%08x, found:" " 0x%08x\n", nand_addr, header->hcrc, crc); } else { header_copy = *header; printf("\nImage header at addr:" "0x%llx, size: 0x%x, " "crc: 0x%x ", nand_addr, header->dlen, header->dcrc); if ((rval = oct_nand_validate_image(nand_addr)) < 0) { printf("Invalid image(%d).\n", rval); } else { printf ("Valid image.\n"); if (header_copy.flags & BL_HEADER_FLAG_FAILSAFE) printf("Failsafe"); else printf("Normal"); if (header_copy.image_type == BL_HEADER_IMAGE_STAGE2) printf("NAND stage2 "); else if (header_copy.image_type == BL_HEADER_IMAGE_STAGE3) printf("NAND stage3 "); else if (header_copy.image_type == BL_HEADER_IMAGE_UBOOT_ENV) printf("U-boot environment (version: %d)\n", (int)header_copy.address); if (header_copy.board_type) printf("image for %s board\n", cvmx_board_type_to_string(header_copy.board_type)); else printf("\n"); /* Skip the image, as we * have validated it */ nand_addr += ((header->dlen + page_size - 1) & ~(page_size - 1)) - page_size; } } } } else { /* Uncorrectable errors detected. */ /* Check to see if we have a bad block */ if (oct_nand_block_is_bad(chip, nand_addr, 1)) { printf ("Bad NAND block at addr: 0x%llx\n", nand_addr & ~(block_size - 1)); /* Skip rest of block */ nand_addr += block_size - (nand_addr & (block_size - 1)) - page_size; } } } printf("\n"); return 0; } usage: printf("Usage:\n%s\n", cmdtp->usage); return 1; }