/* * common/cmd_spidboot.c * * Copyright (C) 2009 SPiDCOM Technologies * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, * MA 02111-1307 USA * * Author(s): * May 2009 Drasko DRASKOVIC */ /* * Boot support */ #include #include #include #include #include #include #include #include #if defined (CONFIG_CHIP_MSE500) #include #include #include #include #include #endif /* CONFIG_CHIP_MSE500 */ DECLARE_GLOBAL_DATA_PTR; extern int do_spidupd (void); extern flash_info_t flash_info[]; /* info for SPI FLASH chip */ extern void flush_dcache(void); /* * Some systems (for example LWMON) have very short watchdog periods; * we must make sure to split long operations like memmove() or * md5() into reasonable chunks. */ #if defined(CONFIG_HW_WATCHDOG) || defined(CONFIG_WATCHDOG) # define CHUNKSZ (64 * 1024) #endif #if defined (CONFIG_CHIP_FEATURE_IOMUX) /* PLC200 GPO field mask in IOMUX. Used for the hack in fixup_iomux. This * define is local because we are not supposed to access IOMUX directly but * let the NVRAM configuration do the job. */ # define IOMUX_PLC200_GPO_MASK 0xF #endif #define MAX_IMG 10 /* supported num of images */ static unsigned long img_addr[MAX_IMG] = {0}; static spidcom_image_desc_generic_t img_desc[MAX_IMG]; static uint32_t autoswitch_en = 0; #define LINUX_LOAD_ADDR (char *)0x40008000 #define BAD_MAGIC_NUMBER 1 #define INDEX_NOT_VALID 2 #define IMAGE_NOT_VALID 3 #define WRONG_IMAGE_ARCH 4 #define WRONG_IMAGE_TYPE 5 #define UPD_NOT_FINISHED 6 #define IMAGE_NOT_SUCCESS 7 #define IMAGE_OK 8 #define IMAGE_OK_ORIGIN 9 /* Util functions */ #if defined (CONFIG_SETUP_MEMORY_TAGS) || \ defined (CONFIG_CMDLINE_TAG) || \ defined (CONFIG_SERIAL_TAG) || \ defined (CONFIG_REVISION_TAG) static struct tag *params; /* * Get the DSP mode by combining image header information (mode), and NVRAM. * If the value in header is undefined, we will use the NVRAM value. Otherwise * image header value prevails. */ static spidcom_image_desc_dsp_mode_t compute_dsp_mode (spidcom_image_desc_dsp_mode_t mode, spidcom_nvram_t *nvram) { if (mode == SPIDCOM_IMG_DESC_DSP_MODE_2X || (mode != SPIDCOM_IMG_DESC_DSP_MODE_1X && (NVRAM_BFEXT (MSE500_MODE, nvram->pkg_cfg) == NVRAM_MSE500_MODE_500))) return SPIDCOM_IMG_DESC_DSP_MODE_2X; return SPIDCOM_IMG_DESC_DSP_MODE_1X; } static void setup_start_tag (bd_t *bd) { params = (struct tag *) bd->bi_boot_params; params->hdr.tag = ATAG_CORE; params->hdr.size = tag_size (tag_core); params->u.core.flags = 0; params->u.core.pagesize = 0; params->u.core.rootdev = 0; params = tag_next (params); } #ifdef CONFIG_SETUP_MEMORY_TAGS static void setup_memory_tags (bd_t *bd) { int i; for (i = 0; i < CONFIG_NR_DRAM_BANKS; i++) { params->hdr.tag = ATAG_MEM; params->hdr.size = tag_size (tag_mem32); params->u.mem.start = bd->bi_dram[i].start; params->u.mem.size = bd->bi_dram[i].size; params = tag_next (params); } } #endif /* CONFIG_SETUP_MEMORY_TAGS */ static void setup_commandline_tag (bd_t *bd, char *commandline) { char *p; if (!commandline) return; /* eat leading white space */ for (p = commandline; *p == ' '; p++); /* skip non-existent command lines so the kernel will still * use its default command line. */ if (*p == '\0') return; params->hdr.tag = ATAG_CMDLINE; params->hdr.size = (sizeof (struct tag_header) + strlen (p) + 1 + 4) >> 2; strcpy (params->u.cmdline.cmdline, p); params = tag_next (params); } #ifdef CONFIG_SPC300_TAG void setup_spc300_tag (bd_t *bd, spidcom_image_desc_generic_t *hdr, struct tag **tmp, int img_slot) { struct tag *params = *tmp; params->hdr.tag = ATAG_SPC300; params->hdr.size = tag_size(tag_spc300); params->u.spc300.nvram_offset = bd->bi_nvram_addr - PHYS_FLASH_SPI_1; if (hdr->type == SPIDCOM_IMG_DESC_IMAGE_TYPE_300) { params->u.spc300.plc_mem_size = hdr->img_300.plc_ram; if (compute_dsp_mode (hdr->header->dsp_mode, (spidcom_nvram_t *)(gd->bd->bi_nvram_addr)) == SPIDCOM_IMG_DESC_DSP_MODE_2X) { params->u.spc300.dsp_mode = SPC300_ATAG_DSP_MODE_2X; } else params->u.spc300.dsp_mode = SPC300_ATAG_DSP_MODE_1X; } params->u.spc300.cur_img_slot = img_slot; params->u.spc300.autoswitch_en = autoswitch_en; params = tag_next (params); *tmp = params; } #endif /* CONFIG_SPC300_TAG */ #ifdef CONFIG_SERIAL_TAG void setup_serial_tag (struct tag **tmp) { struct tag *params = *tmp; struct tag_serialnr serialnr; void get_board_serial(struct tag_serialnr *serialnr); get_board_serial(&serialnr); params->hdr.tag = ATAG_SERIAL; params->hdr.size = tag_size (tag_serialnr); params->u.serialnr.low = serialnr.low; params->u.serialnr.high= serialnr.high; params = tag_next (params); *tmp = params; } #endif #ifdef CONFIG_REVISION_TAG void setup_revision_tag(struct tag **in_params) { u32 rev = 0; u32 get_board_rev(void); rev = get_board_rev(); params->hdr.tag = ATAG_REVISION; params->hdr.size = tag_size (tag_revision); params->u.revision.rev = rev; params = tag_next (params); } #endif /* CONFIG_REVISION_TAG */ static void setup_end_tag (bd_t *bd) { params->hdr.tag = ATAG_NONE; params->hdr.size = 0; } #endif /* CONFIG_SETUP_MEMORY_TAGS || CONFIG_CMDLINE_TAG */ #if defined (CONFIG_CHIP_MSE500) static void setup_uart (uint32_t speed) { int baudrate; uint32_t baud_divisor; if ((baudrate = gd->baudrate) <= 0) baudrate = CONFIG_BAUDRATE; baud_divisor = ((speed * 1000000) + 8*baudrate) / (16 * baudrate); serial_setdivisor (baud_divisor); } /* * This function sets the System PLL speed. It assumes that the given value is * correct, otherwise it has no effect. */ static void setup_system_pll (uint32_t speed, uint32_t type) { /* Make sure the value is in the bounds. */ if (speed < 96 || speed > 276) { if (speed) { printf ("System PLL speed (%dMHz) not supported, the image is probably corrupted!\n", speed); printf ("Booting with default value\n"); return; } /* Check image type. If we find a 200 image, we set SPLL to 96MHz. * Otherwise we keep the default, which is the value that should work * in all cases (MSE500 with ARM CLK at 444 or 492MHz). */ if (type == SPIDCOM_IMG_DESC_IMAGE_TYPE_200) speed = 96; else return; } if ((speed >= 192 && (speed % 6)) || (speed < 192 && (speed % 3))) { printf ("System PLL speed (%dMHz) not supported, the image is probably corrupted!\n", speed); printf ("Booting with default value\n"); return; } *((volatile uint32_t *) RB_SPLL_BYPASS) = PLL_CMD_BYPASS; while (*((volatile uint32_t *) RB_SPLL_BYPASS_STAT) != PLL_IS_BYPASS) ; *((volatile uint32_t *)(MSEPLL_SPLL_BASE + MSEPLL_SPPLL_CTRL_OFFSET)) |= MSEPLL_BIT(SPPLL_CTRL_PD); if (speed >= 192) { *((volatile uint32_t *)(MSEPLL_SPLL_BASE + MSEPLL_SPPLL_CTRL_OFFSET)) |= MSEPLL_BIT(SPPLL_CTRL_VCO_DIV2_DIS); *((volatile uint32_t *)(MSEPLL_SPLL_BASE + MSEPLL_SPPLL_CONF_OFFSET)) = MSEPLL_BFINS(SPPLL_CONF_LOOP_DIV_SECOND, speed / 6, *((volatile uint32_t *)(MSEPLL_SPLL_BASE + MSEPLL_SPPLL_CONF_OFFSET))); } else { *((volatile uint32_t *)(MSEPLL_SPLL_BASE + MSEPLL_SPPLL_CTRL_OFFSET)) &= ~MSEPLL_BIT(SPPLL_CTRL_VCO_DIV2_DIS); *((volatile uint32_t *)(MSEPLL_SPLL_BASE + MSEPLL_SPPLL_CONF_OFFSET)) = MSEPLL_BFINS(SPPLL_CONF_LOOP_DIV_SECOND, speed / 3, *((volatile uint32_t *)(MSEPLL_SPLL_BASE + MSEPLL_SPPLL_CONF_OFFSET))); } *((volatile uint32_t *)(MSEPLL_SPLL_BASE + MSEPLL_SPPLL_CTRL_OFFSET)) &= ~MSEPLL_BIT(SPPLL_CTRL_PD); *((volatile uint32_t *) RB_SPLL_BYPASS) = PLL_CMD_PLL; while (*((volatile uint32_t *) RB_SPLL_BYPASS_STAT) != PLL_IS_PLL) ; setup_uart (speed); } /* * This function sets the DSP PLL speed. Value is determined depending on * the image type, the DSP mode and the NVRAM. */ static void setup_dsp_pll (uint32_t type, uint32_t mode) { int adc_div, dsp_div, dac_div, loop_div, icp; if (type == SPIDCOM_IMG_DESC_IMAGE_TYPE_200) { /* Config for an output clock at 256 MHz. * DSP clock is 128 MHz due to a divisor after the PLL output. */ adc_div = MSEAFE_DPLL_CTRL_ADC_DIV_6; dsp_div = MSEAFE_DPLL_CONF_DSP_DIV_3; dac_div = MSEAFE_DPLL_CONF_DAC_DIV_3; loop_div = 32; icp = 3; } else { /* Config for an output clock at 300 MHz. * DSP clock is 150 MHz due to a divisor after the PLL output in * 300 mode. In 500 mode, divisor is bypassed to get a DSP clock at * 300 MHz. */ adc_div = MSEAFE_DPLL_CTRL_ADC_DIV_4; dsp_div = MSEAFE_DPLL_CONF_DSP_DIV_2; dac_div = MSEAFE_DPLL_CONF_DAC_DIV_2; loop_div = 25; icp = 2; if (compute_dsp_mode (mode, (spidcom_nvram_t *)(gd->bd->bi_nvram_addr)) == SPIDCOM_IMG_DESC_DSP_MODE_2X) { *((volatile uint32_t *)(MARIA_REGBANK_BASE + RB_CLK_DIV_DSP_OFFSET)) = CLK_DIV_DSP_1; } } *((volatile uint32_t *)RB_DPLL_BYPASS) = PLL_CMD_BYPASS; while (*((volatile uint32_t *)RB_DPLL_BYPASS_STAT) != PLL_IS_BYPASS) ; *((volatile uint32_t *)(MSEAFE_BASE + MSEAFE_DPLL_CONF_OFFSET)) |= MSEAFE_BIT (DPLL_CONF_PD); *((volatile uint32_t *)(MSEAFE_BASE + MSEAFE_DPLL_CTRL_OFFSET)) |= MSEAFE_BIT (DPLL_CTRL_PD_CLK); *((volatile uint32_t *)(MSEAFE_BASE + MSEAFE_DPLL_CTRL_OFFSET)) |= MSEAFE_BIT (DPLL_CTRL_PD_DAC_CLK_OUT); *((volatile uint32_t *)(MSEAFE_BASE + MSEAFE_DPLL_CTRL_OFFSET)) |= MSEAFE_BIT (DPLL_CTRL_PD_DAC_CLK); *((volatile uint32_t *)(MSEAFE_BASE + MSEAFE_DPLL_CTRL_OFFSET)) |= MSEAFE_BIT (DPLL_CTRL_PD_ADC_CLK); *((volatile uint32_t *)(MSEAFE_BASE + MSEAFE_DPLL_CTRL_OFFSET)) = MSEAFE_BFINS (DPLL_CTRL_REF_DIV, MSEAFE_DPLL_CTRL_REF_DIV_2, *((volatile uint32_t *)(MSEAFE_BASE + MSEAFE_DPLL_CTRL_OFFSET))); *((volatile uint32_t *)(MSEAFE_BASE + MSEAFE_DPLL_CTRL_OFFSET)) = MSEAFE_BFINS (DPLL_CTRL_ADC_DIV, adc_div, *((volatile uint32_t *)(MSEAFE_BASE + MSEAFE_DPLL_CTRL_OFFSET))); *((volatile uint32_t *)(MSEAFE_BASE + MSEAFE_DPLL_LOOP_DIV_OFFSET)) = MSEAFE_BFINS (DPLL_LOOP_DIV_FIRST, MSEAFE_DPLL_LOOP_DIV_FIRST_2, *((volatile uint32_t *)(MSEAFE_BASE + MSEAFE_DPLL_LOOP_DIV_OFFSET))); *((volatile uint32_t *)(MSEAFE_BASE + MSEAFE_DPLL_LOOP_DIV_OFFSET)) = MSEAFE_BFINS (DPLL_LOOP_DIV_SECOND, loop_div, *((volatile uint32_t *)(MSEAFE_BASE + MSEAFE_DPLL_LOOP_DIV_OFFSET))); *((volatile uint32_t *)(MSEAFE_BASE + MSEAFE_DPLL_CONF_OFFSET)) = MSEAFE_BFINS (DPLL_CONF_DSP_DIV, dsp_div, *((volatile uint32_t *)(MSEAFE_BASE + MSEAFE_DPLL_CONF_OFFSET))); *((volatile uint32_t *)(MSEAFE_BASE + MSEAFE_DPLL_CONF_OFFSET)) = MSEAFE_BFINS (DPLL_CONF_DAC_DIV, dac_div, *((volatile uint32_t *)(MSEAFE_BASE + MSEAFE_DPLL_CONF_OFFSET))); *((volatile uint32_t *)(MSEAFE_BASE + MSEAFE_DPLL_CONF_OFFSET)) = MSEAFE_BFINS (DPLL_CONF_ICP, icp, *((volatile uint32_t *)(MSEAFE_BASE + MSEAFE_DPLL_CONF_OFFSET))); *((volatile uint32_t *)(MSEAFE_BASE + MSEAFE_DPLL_CTRL_OFFSET)) &= ~MSEAFE_BIT (DPLL_CTRL_PD_ADC_CLK); *((volatile uint32_t *)(MSEAFE_BASE + MSEAFE_DPLL_CTRL_OFFSET)) &= ~MSEAFE_BIT (DPLL_CTRL_PD_DAC_CLK); *((volatile uint32_t *)(MSEAFE_BASE + MSEAFE_DPLL_CTRL_OFFSET)) &= ~MSEAFE_BIT (DPLL_CTRL_PD_DAC_CLK_OUT); *((volatile uint32_t *)(MSEAFE_BASE + MSEAFE_DPLL_CTRL_OFFSET)) &= ~MSEAFE_BIT (DPLL_CTRL_PD_CLK); *((volatile uint32_t *)(MSEAFE_BASE + MSEAFE_DPLL_CONF_OFFSET)) &= ~MSEAFE_BIT (DPLL_CONF_PD); *((volatile uint32_t *)RB_DPLL_BYPASS) = PLL_CMD_PLL; while (*((volatile uint32_t *)RB_DPLL_BYPASS_STAT) != PLL_IS_PLL) ; } static void fixup_iomux (void) { /* This is a hack to be able to use a common IOMUX configuration in NVRAM: * by default, PLC200_GPO is set, what allows to give the corresponding * GPIO to PLC200 modem. If we are booting a 300 or 500 image, we erase * this field, what automatically gives this GPIO back to LEON. We assume * that IOMUX configuration in NVRAM has already properly set LEON GPIOs. */ *((volatile uint32_t *)(IOMUX_BASE + IOMUX_200_MODEM_OFFSET)) &= ~IOMUX_PLC200_GPO_MASK; } #endif /* CONFIG_CHIP_MSE500 */ /* --- SPiDBOOT --- */ static int load_header(spidcom_image_desc_generic_t *desc, char *addr) { if (IS_IN_SPI_RANGE(addr)) { /* first read image header, to see what is the len of the image */ if ( flash_read_spi ((unsigned char *)addr, (unsigned char *)&(desc->img_common), sizeof(spidcom_image_desc_common_200_300_t), 1, 0) != ERR_OK ) { printf("ERROR : loading image header from SPI flash failed.\n"); return -1; } } else /* we boot from SDRAM */ { memmove (&(desc->img_common), (char *)addr, sizeof(spidcom_image_desc_common_200_300_t)); } return spidcom_image_desc_load (desc); } static int check_img(spidcom_image_desc_generic_t *desc, char *addr) { int descriptor_changed = 0; int result = IMAGE_OK; /* First check image validity. If it was set as a non valid before, * we will not even bother to look further. */ if (desc->img_common.is_valid == 0) { return IMAGE_NOT_VALID; } /* Now check other flags... */ if ( memcmp(desc->img_common.magic, SPIDCOM_IMG_DESC_MAGIC, sizeof(desc->img_common.magic)) ) { desc->img_common.is_valid = 0; /* Mark descriptor has changed to write this new value under flash */ descriptor_changed = 1; result = BAD_MAGIC_NUMBER; goto outahere; } #if defined(__ARM__) if (((desc->type == SPIDCOM_IMG_DESC_IMAGE_TYPE_300) && (desc->img_300.arch != SPIDCOM_IMG_DESC_SPC300)) || ((desc->type == SPIDCOM_IMG_DESC_IMAGE_TYPE_200) && (desc->img_200.arch != SPIDCOM_IMG_DESC_SPC200C || desc->img_200.arch != SPIDCOM_IMG_DESC_SPC200E))) #else # error Unknown CPU type #endif { desc->img_common.is_valid = 0; /* Mark descriptor has changed to write this new value under flash */ descriptor_changed = 1; result = WRONG_IMAGE_ARCH; goto outahere; } if (desc->img_common.type != SPIDCOM_IMG_DESC_NORMAL_TYPE) { desc->img_common.is_valid = 0; /* Mark descriptor has changed to write this new value under flash */ descriptor_changed = 1; result = WRONG_IMAGE_TYPE; goto outahere; } if (desc->img_common.index == SPIDCOM_IMG_DESC_ORIGIN_INDEX) { /* image was downloaded by JTAG */ if (desc->type == SPIDCOM_IMG_DESC_IMAGE_TYPE_300) { desc->img_300.is_not_update = 0; /* Mark descriptor has changed to write this new value under * flash */ descriptor_changed = 1; } result = IMAGE_OK_ORIGIN; goto outahere; } if ((desc->type == SPIDCOM_IMG_DESC_IMAGE_TYPE_300) && (desc->img_300.is_not_update != 0)) { desc->img_300.is_valid = 0; /* Mark descriptor has changed to write this new value under flash */ descriptor_changed = 1; result = UPD_NOT_FINISHED; goto outahere; } if (desc->img_common.index == SPIDCOM_IMG_DESC_INVALID_INDEX) { desc->img_common.is_valid = 0; /* Mark descriptor has changed to write this new value under flash */ descriptor_changed = 1; result = INDEX_NOT_VALID; goto outahere; } /* fix image validity based on previous booting attempt */ if(!desc->img_common.is_1st_boot && desc->img_common.is_not_success) { /* image is corrupted : invalidate it !!! */ desc->img_common.is_valid = 0; /* Mark descriptor has changed to write this new value under flash */ descriptor_changed = 1; result = IMAGE_NOT_SUCCESS; goto outahere; } outahere: if(descriptor_changed) { /* Replacing 1s with 0s is allowed without erase. * We will write whole structure (it is allowed to write unchanged parts, * just transition 0->1 is impossible without erase */ flash_write( (char *)&desc->img_common, (ulong)addr, sizeof(spidcom_image_desc_common_200_300_t) ); } return result; } static void update_origin_index(spidcom_image_desc_generic_t *desc, char *addr, ulong index) { desc->img_common.index = index; /* Replacing 1s with 0s is allowed without erase. * We will write whole structure (it is allowed to write unchanged parts, * just transition 0->1 is impossible without erase */ flash_write( (char *)&desc->img_common, (ulong)addr, sizeof(spidcom_image_desc_common_200_300_t) ); } static int select_image(int img_nb) { int sel_img = -1; /* will hold the index of selected image */ spidcom_image_desc_generic_t *desc; int img_state[MAX_IMG] = {0}; ulong max_index = 0; char img_version[65]; uint version_len; spidcom_image_desc_image_type_t prev_img_type = SPIDCOM_IMG_DESC_IMAGE_TYPE_UNKNOWN; int i; #if defined (CONFIG_CHIP_MSE500) volatile sram_autoswitch_data_t *autoswitch_data = (volatile sram_autoswitch_data_t *) SRAM_DATA_PA_PTR (autoswitch); if (!strncmp ((const char *)autoswitch_data->magic, MSE500_AUTOSWITCH_MAGIC, MSE500_AUTOSWITCH_MAGIC_SIZE) && ((autoswitch_data->slot <= 1) && (autoswitch_data->slot >= 0))) { if (load_header (&img_desc[autoswitch_data->slot], (char *)img_addr[autoswitch_data->slot]) >= 0) { int state = check_img (&img_desc[autoswitch_data->slot], (char *)img_addr[autoswitch_data->slot]); if (state == IMAGE_OK || state == IMAGE_OK_ORIGIN) { if (state == IMAGE_OK_ORIGIN) { /* Update origin index. If we are here, we can be sure * that alternate image exists and is OK. That is why * we allow ourselves to use its index without checks. */ int new_index, alt_img_slot; if (autoswitch_data->slot == 0) alt_img_slot = 1; else alt_img_slot = 0; load_header (&img_desc[alt_img_slot], (char *)img_addr[alt_img_slot]); new_index = img_desc[alt_img_slot].img_common.index + 1; update_origin_index (&img_desc[autoswitch_data->slot], (char*) img_addr[autoswitch_data->slot], new_index); } autoswitch_en = 1; return autoswitch_data->slot; } } } #endif for (i=0; i= 0) { img_state[i] = check_img(desc, (char *)img_addr[i]); if(!memcmp(desc->img_common.magic, SPIDCOM_IMG_DESC_MAGIC, sizeof(desc->img_common.magic))) { version_len = spidcom_image_desc_read_version (desc, img_version); img_version[version_len] = '\0'; } else { strcpy(img_version, "Unknown"); } } else { strcpy(img_version, "Invalid"); img_state[i] = IMAGE_NOT_VALID; } switch(img_state[i]) { case IMAGE_OK: printf("IMG (%-16s) at address %#x is OK.\n", img_version, img_addr[i]); if (desc->img_common.index >= max_index) { max_index = desc->img_common.index; sel_img = i; } break; case IMAGE_OK_ORIGIN: printf("IMG (%-16s) at address %#x is OK.\n", img_version, img_addr[i]); break; case BAD_MAGIC_NUMBER: printf("IMG (%-16s) at address %#x is not OK (bad magic number).\n", img_version, img_addr[i]); break; case WRONG_IMAGE_ARCH: printf("IMG (%-16s) at address %#x is not OK (architecture error).\n", img_version, img_addr[i]); break; case WRONG_IMAGE_TYPE: printf("IMG (%-16s) at address %#x is not OK (type error).\n", img_version, img_addr[i]); break; case UPD_NOT_FINISHED: printf("IMG (%-16s) at address %#x is not OK (update error).\n", img_version, img_addr[i]); break; case INDEX_NOT_VALID: printf("IMG (%-16s) at address %#x is not OK (index error).\n", img_version, img_addr[i]); break; case IMAGE_NOT_SUCCESS: printf("IMG (%-16s) at address %#x is not OK (image error).\n", img_version, img_addr[i]); break; default: /* IMAGE_NOT_VALID */ printf("IMG (%-16s) at address %#x is not OK (valid error).\n", img_version, img_addr[i]); } } /* Check a JTAG image, cause it will be better to boot it */ for (i=0; iimage_type; continue; } if ((img_state[i] == IMAGE_OK || img_state[i] == IMAGE_OK_ORIGIN) && prev_img_type != img_desc[i].header->image_type) { /* We need to activate autoswitch. */ autoswitch_en = 1; break; } } return sel_img; } static ulong find_nb_images (void) { spidcom_nvram_t *nvram = (spidcom_nvram_t *)(gd->bd->bi_nvram_addr); /* We can use SPI Direct Access because nb_images is a uint32_t */ return nvram->nb_images; } static ulong find_image_start_addr (void) { spidcom_nvram_t *nvram = (spidcom_nvram_t *)(gd->bd->bi_nvram_addr); /* We can use SPI Direct Access because nb_images is a uint32_t */ return PHYS_FLASH_SPI_1 + nvram->img_0_offset; } static ulong find_image_max_size (void) { spidcom_nvram_t *nvram = (spidcom_nvram_t *)(gd->bd->bi_nvram_addr); /* We can use SPI Direct Access because nb_images is a uint32_t */ return nvram->img_max_size; } int do_spidboot (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) { ulong addr; ulong data, len; int verify; int i; char *s; void (*appl)(int zero, int arch, uint params); spidcom_image_desc_generic_t *hdr = NULL; int use_spi_flash = 0; char *img_load_addr = NULL; bd_t *bd = gd->bd; ulong img_nb = 0; ulong img_0_addr; ulong img_max_size; int sel_img = -1; /* image selected for boot after all the checks */ char img_version[65]; uint version_size; char *version_addr; #ifdef CONFIG_CMDLINE_TAG char *commandline = getenv ("bootargs"); #endif /* set load addr of the kernel */ img_load_addr = LINUX_LOAD_ADDR; /* A second argument overwrites the load address */ if (argc > 2) { img_load_addr = (char*)simple_strtoul(argv[2], NULL, 16); } s = getenv ("verify"); verify = (s && (*s == 'n')) ? 0 : 1; if (argc < 2) /* no address was given - normal multiboot */ { img_nb = find_nb_images(); img_max_size = find_image_max_size(); img_0_addr = find_image_start_addr(); if(img_max_size == 0xFFFFFFFF) { /* max_size unknown force mapping to one image */ img_nb = 1; img_max_size = flash_info[0].size + PHYS_FLASH_SPI_1 - img_0_addr; } for(i=0 ; i < img_nb ; i++) { img_addr[i] = (img_max_size * i) + img_0_addr; } while(sel_img < 0) { /* give a chance of 1sec timeout for user to initiate the update */ do_spidupd(); sel_img = select_image(img_nb); if ( ctrlc() ) { return -1; } } addr = img_addr[sel_img]; hdr = &img_desc[sel_img]; } else { /* address was given, so we want to boot this exact image => * we are in the single boot mode; * we are also in developer mode, trying to force the boot of * chosen image => we will not call do_update() here */ addr = simple_strtoul(argv[1], NULL, 16); hdr = &img_desc[0]; /* we will use img_0_desc structure for single boot */ load_header(hdr, (char *)addr); /* do no checking - we are in developer mode, trying to force boot of * selected image => we will not check it's validity */ } switch (hdr->type) { case SPIDCOM_IMG_DESC_IMAGE_TYPE_300: version_addr = hdr->img_300.version; version_size = sizeof (hdr->img_300.version); break; case SPIDCOM_IMG_DESC_IMAGE_TYPE_200: version_addr = hdr->img_200.version; version_size = sizeof (hdr->img_200.version); break; default: printf ("Unsupported image type\n"); return -1; } strncpy(img_version, version_addr, version_size); img_version[version_size] = '\0'; printf ("## Booting image \"%s\" at 0x%08lx ...\n", img_version, addr); /* do the image transfer */ data = (int)addr + sizeof(spidcom_image_desc_common_200_300_t); len = hdr->img_common.size; if ( IS_IN_SPI_RANGE(data) ) { use_spi_flash = 1; } #if defined(CONFIG_HW_WATCHDOG) || defined(CONFIG_WATCHDOG) { size_t l = len; void *to = (void *)(img_load_addr); void *from = (void *)data; while (l > 0) { size_t tail = (l > CHUNKSZ) ? CHUNKSZ : l; WATCHDOG_RESET(); if (use_spi_flash) { if ( flash_read_spi (from, to, tail, 1, 0) != ERR_OK ) { printf("ERROR : loading image from SPI flash failed.\n"); return -1; } } else /* we boot from SDRAM */ { memmove (to, from, tail); } to += tail; from += tail; l -= tail; } } #else /* !(CONFIG_HW_WATCHDOG || CONFIG_WATCHDOG) */ { if (use_spi_flash) { if ( flash_read_spi ( (uchar *)data, (void *)(img_load_addr), len, 1, 0 ) != ERR_OK ) { printf("ERROR : loading image from SPI flash failed.\n"); return -1; } } else /* we boot from SDRAM */ { memmove ((void *)(img_load_addr), (uchar *)data, len); } } #endif /* CONFIG_HW_WATCHDOG || CONFIG_WATCHDOG */ /* we transfered img from to SDRAM load addr - * data pointer to point to image in RAM from now on */ data = (ulong)(img_load_addr); /* we are positioned at image load addr */ len = hdr->img_common.size; /* * before starting Linux, set correct image descriptor flags */ /* we will boot - it will not be 1st boot any more */ if (hdr->img_common.is_1st_boot == 1) { hdr->img_common.is_1st_boot = 0; /* Replacing 1s with 0s is allowed without erase. * We will write whole structure (it is allowed to write unchanged parts, * just transition 0->1 is impossible without erase */ if(argc < 2) flash_write( (char *)&hdr->img_common, (ulong)img_addr[sel_img], sizeof(spidcom_image_desc_common_200_300_t) ); } if (hdr->type == SPIDCOM_IMG_DESC_IMAGE_TYPE_300) { /* Get PLC processor needed RAM space and reduce Linux one */ gd->bd->bi_dram[0].size -= hdr->img_300.plc_ram; } /* * We have reached the point of no return: we are going to * overwrite all exception vector code, so we cannot easily * recover from any failures any more... */ /* load (and uncompress), but don't start if "autostart" * is set to "no" */ if (((s = getenv("autostart")) != NULL) && (strcmp(s,"no") == 0)) { char buf[32]; sprintf(buf, "%lX", len); setenv("filesize", buf); return 0; } #if defined (CONFIG_CHIP_MSE500) /* Before booting Linux, we need to set System and DSP PLL. */ setup_system_pll (hdr->header->sysclk_speed, hdr->header->image_type); setup_dsp_pll (hdr->header->image_type, hdr->header->dsp_mode); if (hdr->header->image_type == SPIDCOM_IMG_DESC_IMAGE_TYPE_300) fixup_iomux (); #endif #if defined (CONFIG_SETUP_MEMORY_TAGS) || \ defined (CONFIG_CMDLINE_TAG) || \ defined (CONFIG_SERIAL_TAG) || \ defined (CONFIG_REVISION_TAG) setup_start_tag (bd); #ifdef CONFIG_SERIAL_TAG setup_serial_tag (¶ms); #endif #ifdef CONFIG_REVISION_TAG setup_revision_tag (¶ms); #endif #ifdef CONFIG_SETUP_MEMORY_TAGS setup_memory_tags (bd); #endif #ifdef CONFIG_CMDLINE_TAG setup_commandline_tag (bd, commandline); #endif #ifdef CONFIG_SPC300_TAG setup_spc300_tag (bd, hdr, ¶ms, sel_img); #endif setup_end_tag (bd); #endif /* disable D/I caches and flush them */ cleanup_before_linux (); /* Start Watchdog with timeout=2s */ spc300_wdt_start(2); /* GO */ appl = (void (*) (int, int, uint))(img_load_addr) ; appl(0, bd->bi_arch_number, bd->bi_boot_params); return 0; } U_BOOT_CMD( spidboot, 2, 1, do_spidboot, "spidboot - boot application image from memory\n", "[addr [arg ...]]\n - boot application image stored in memory\n" "\tpassing arguments 'arg ...'; to precise a Linux install address.\n" );