/* * arch/arm/cpu/armv8/common/firmware/plat/gxb/storage.c * * Copyright (C) 2015 Amlogic, Inc. All rights reserved. * * 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., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include void dump_ddr_data(void) { #ifdef CONFIG_SPL_DDR_DUMP if (CONFIG_SPL_DDR_DUMP_FLAG == readl(P_PREG_STICKY_REG0)) { serial_puts("Dump ddr[0x"); serial_put_hex(CONFIG_SPL_DDR_DUMP_ADDR, 32); serial_puts("-0x"); serial_put_hex((CONFIG_SPL_DDR_DUMP_ADDR+CONFIG_SPL_DDR_DUMP_SIZE), 32); serial_puts("] to "); serial_puts((CONFIG_SPL_DDR_DUMP_DEV_TYPE==BOOT_DEVICE_SD)?"External":"Internal"); serial_puts(" device[0x"); serial_put_hex(CONFIG_SPL_DDR_DUMP_DEV_OFFSET, 32); serial_puts("-0x"); serial_put_hex((CONFIG_SPL_DDR_DUMP_DEV_OFFSET+CONFIG_SPL_DDR_DUMP_SIZE), 32); serial_puts("]\n\n"); writel(0, P_PREG_STICKY_REG0); watchdog_disable(); sdio_write_data(CONFIG_SPL_DDR_DUMP_DEV_TYPE, CONFIG_SPL_DDR_DUMP_ADDR, \ CONFIG_SPL_DDR_DUMP_DEV_OFFSET, CONFIG_SPL_DDR_DUMP_SIZE); reset_system(); } #endif return; } uint64_t storage_init(void) { uint64_t boot_device = 0; boot_device = get_boot_device(); switch (boot_device) { #if defined(CONFIG_AML_NAND) case BOOT_DEVICE_NAND: serial_puts( "NAND init\n"); nfio_init(); break; #endif //CONFIG_AML_NAND default: //serial_puts("do nothing!\n"); break; } return 0; } uint64_t storage_load(uint64_t src, uint64_t des, uint64_t size, const char * image_name) { char * device_name = "UNKNOWN"; uint64_t boot_device = 0; boot_device = get_boot_device(); //boot_device = BOOT_DEVICE_SPI; switch (boot_device) { case BOOT_DEVICE_RESERVED: device_name = "Rsv"; break; case BOOT_DEVICE_EMMC: device_name = "eMMC"; break; #if defined(CONFIG_AML_NAND) case BOOT_DEVICE_NAND: device_name = "NAND"; break; #endif //CONFIG_AML_NAND case BOOT_DEVICE_SPI: device_name = "SPI"; break; case BOOT_DEVICE_SD: device_name = "SD"; src += 512; //sd boot must add 512 offset break; case BOOT_DEVICE_USB: case BOOT_DEVICE_USB_FORCEMODE: device_name = "USB"; break; default: break; } //printf("Load %s from %s, src: 0x%x, dst: 0x%x, size: 0x%x\n", // image_name, device_name, src, des, size); serial_puts("Load "); serial_puts(image_name); serial_puts(" from "); serial_puts(device_name); serial_puts(", src: 0x"); serial_put_hex(src, 32); serial_puts(", des: 0x"); serial_put_hex(des, 32); serial_puts(", size: 0x"); serial_put_hex(size, 32); serial_puts("\n"); switch (boot_device) { case BOOT_DEVICE_RESERVED: break; case BOOT_DEVICE_EMMC: sdio_read_data(boot_device,src, des, size); break; #if defined(CONFIG_AML_NAND) case BOOT_DEVICE_NAND: nf_read(boot_device, src, des, size); break; #endif //CONFIG_AML_NAND case BOOT_DEVICE_SPI: spi_read(src, des, size); break; case BOOT_DEVICE_SD: sdio_read_data(boot_device, src, des, size); break; case BOOT_DEVICE_USB: case BOOT_DEVICE_USB_FORCEMODE: usb_boot(src, des, size); break; default: break; } #ifdef BL2_ENABLE_MMU inv_dcache_range(des, size); #endif return 0; } uint64_t spi_read(uint64_t src, uint64_t des, uint64_t size) { /*spi pin mux*/ *P_PAD_PULL_UP_EN_REG2 = 0xffff87ff; *P_PAD_PULL_UP_REG2 = 0xffff8700; // deselect nand/emmc, select spi. *P_PERIPHS_PIN_MUX_4 &= ~((1<<31) | (7<<22) | (1<<20)); *P_PERIPHS_PIN_MUX_5 |= 0xf; /*spi init*/ /* use sys_clock_freq: 0x002ab000 //24:0x002ab313 * use sys_clock_freq/2: 0x002aa101 * use sys_clock_freq/4: 0x002aa313 * use sys_clock_freq/8: 0x002aa737 * use sys_clock_freq/10: 0x002aa949 * use sys_clock_freq/16: 0x002aaf7f */ #ifndef CONFIG_PXP_EMULATOR writel(0x2aa949,P_SPI_FLASH_CTRL); #else writel(0x002ab000,P_SPI_FLASH_CTRL); #endif /*load data*/ uint64_t des64, src64; des64 = des; src64 = src; memcpy((void *)des64, (void *)(src64 | (uint64_t)P_SPI_START_ADDR), size); return 0; } uint64_t sdio_read_blocks(struct sd_emmc_global_regs *sd_emmc_regs, uint64_t src, uint64_t des, uint64_t size, uint64_t mode) { unsigned ret = 0; unsigned read_start; unsigned vstart = 0; unsigned status_irq = 0; unsigned response[4]; struct cmd_cfg *des_cmd_cur = NULL; struct sd_emmc_desc_info desc[MAX_DESC_NUM]; struct sd_emmc_desc_info *desc_cur; struct sd_emmc_start *desc_start = (struct sd_emmc_start*)&vstart; struct sd_emmc_status *status_irq_reg = (void *)&status_irq; memset(desc,0,MAX_DESC_NUM*sizeof(struct sd_emmc_desc_info)); desc_cur = desc; if (mode) read_start = src>>9; else read_start = src; des_cmd_cur = (struct cmd_cfg *)&(desc_cur->cmd_info); //starting reading...... des_cmd_cur->cmd_index = 18; //read data command if (mode) { des_cmd_cur->block_mode = 1; des_cmd_cur->length = size; }else{ des_cmd_cur->block_mode = 0; des_cmd_cur->length = size; } des_cmd_cur->data_io = 1; des_cmd_cur->data_wr = 0; des_cmd_cur->data_num = 0; des_cmd_cur->no_resp = 0; des_cmd_cur->resp_num = 0; des_cmd_cur->timeout = 7; des_cmd_cur->owner = 1; des_cmd_cur->end_of_chain = 1; desc_cur->cmd_arg = read_start; desc_cur->data_addr = des; desc_cur->data_addr &= ~(1<<0); //DDR desc_cur->resp_addr = (unsigned long)response; desc_start->init = 0; desc_start->busy = 1; desc_start->addr = (unsigned long)desc >> 2; sd_emmc_regs->gstatus = 0x3fff; //sd_emmc_regs->gstart = vstart; sd_emmc_regs->gcmd_cfg = desc_cur->cmd_info; sd_emmc_regs->gcmd_dat = desc_cur->data_addr; sd_emmc_regs->gcmd_arg = desc_cur->cmd_arg; while (1) { status_irq = sd_emmc_regs->gstatus; if (status_irq_reg->end_of_chain) break; } //send stop cmd desc_cur = &desc[1]; des_cmd_cur = (struct cmd_cfg *)&(desc_cur->cmd_info); des_cmd_cur->cmd_index = 12; des_cmd_cur->data_io = 0; des_cmd_cur->no_resp = 0; des_cmd_cur->r1b = 1; des_cmd_cur->owner = 1; des_cmd_cur->end_of_chain = 1; desc_start->init = 0; desc_start->busy = 1; desc_start->addr = (unsigned long)desc_cur >> 2; sd_emmc_regs->gstatus = 0x3fff; //sd_emmc_regs->gstart = vstart; sd_emmc_regs->gcmd_cfg = desc_cur->cmd_info; sd_emmc_regs->gcmd_dat = desc_cur->data_addr; sd_emmc_regs->gcmd_arg = desc_cur->cmd_arg; while (1) { status_irq = sd_emmc_regs->gstatus; //printf("status_irq=0x%x\n",status_irq); if (status_irq_reg->end_of_chain) break; } if (status_irq_reg->rxd_err) ret |= SD_EMMC_RXD_ERROR; if (status_irq_reg->txd_err) ret |= SD_EMMC_TXD_ERROR; if (status_irq_reg->desc_err) ret |= SD_EMMC_DESC_ERROR; if (status_irq_reg->resp_err) ret |= SD_EMMC_RESP_CRC_ERROR; if (status_irq_reg->resp_timeout) ret |= SD_EMMC_RESP_TIMEOUT_ERROR; if (status_irq_reg->desc_timeout) ret |= SD_EMMC_DESC_TIMEOUT_ERROR; if (ret) { serial_puts("sd/emmc read data error: ret="); serial_put_dec(ret); serial_puts("\n"); } //else //serial_puts("read data success!\n"); return ret; } #ifdef CONFIG_SPL_DDR_DUMP uint64_t sdio_write_blocks(struct sd_emmc_global_regs *sd_emmc_regs, uint64_t src, uint64_t des, uint64_t size, uint64_t mode) { unsigned ret = 0; unsigned write_start; unsigned vstart = 0; unsigned status_irq = 0; unsigned response[4]; struct cmd_cfg *des_cmd_cur = NULL; struct sd_emmc_desc_info desc[MAX_DESC_NUM]; struct sd_emmc_desc_info *desc_cur; struct sd_emmc_start *desc_start = (struct sd_emmc_start*)&vstart; struct sd_emmc_status *status_irq_reg = (void *)&status_irq; memset(desc,0,MAX_DESC_NUM*sizeof(struct sd_emmc_desc_info)); desc_cur = desc; if (mode) write_start = des>>9; else write_start = des; des_cmd_cur = (struct cmd_cfg *)&(desc_cur->cmd_info); //starting reading...... des_cmd_cur->cmd_index = 25; //muti write data command if (mode) { des_cmd_cur->block_mode = 1; des_cmd_cur->length = size; }else{ des_cmd_cur->block_mode = 0; des_cmd_cur->length = size; } des_cmd_cur->data_io = 1; des_cmd_cur->data_wr = 1; des_cmd_cur->data_num = 0; des_cmd_cur->no_resp = 0; des_cmd_cur->resp_num = 0; des_cmd_cur->timeout = 7; des_cmd_cur->owner = 1; des_cmd_cur->end_of_chain = 1; desc_cur->cmd_arg = write_start; desc_cur->data_addr = src; desc_cur->data_addr &= ~(1<<0); //DDR desc_cur->resp_addr = (unsigned long)response; desc_start->init = 0; desc_start->busy = 1; desc_start->addr = (unsigned long)desc >> 2; sd_emmc_regs->gstatus = 0x3fff; //sd_emmc_regs->gstart = vstart; sd_emmc_regs->gcmd_cfg = desc_cur->cmd_info; sd_emmc_regs->gcmd_dat = desc_cur->data_addr; sd_emmc_regs->gcmd_arg = desc_cur->cmd_arg; while (1) { status_irq = sd_emmc_regs->gstatus; if (status_irq_reg->end_of_chain) break; } //send stop cmd desc_cur = &desc[1]; des_cmd_cur = (struct cmd_cfg *)&(desc_cur->cmd_info); des_cmd_cur->cmd_index = 12; des_cmd_cur->data_io = 0; des_cmd_cur->no_resp = 0; des_cmd_cur->r1b = 1; des_cmd_cur->owner = 1; des_cmd_cur->end_of_chain = 1; desc_start->init = 0; desc_start->busy = 1; desc_start->addr = (unsigned long)desc_cur >> 2; sd_emmc_regs->gstatus = 0x3fff; //sd_emmc_regs->gstart = vstart; sd_emmc_regs->gcmd_cfg = desc_cur->cmd_info; sd_emmc_regs->gcmd_dat = desc_cur->data_addr; sd_emmc_regs->gcmd_arg = desc_cur->cmd_arg; while (1) { status_irq = sd_emmc_regs->gstatus; //printf("status_irq=0x%x\n",status_irq); if (status_irq_reg->end_of_chain) break; } if (status_irq_reg->rxd_err) ret |= SD_EMMC_RXD_ERROR; if (status_irq_reg->txd_err) ret |= SD_EMMC_TXD_ERROR; if (status_irq_reg->desc_err) ret |= SD_EMMC_DESC_ERROR; if (status_irq_reg->resp_err) ret |= SD_EMMC_RESP_CRC_ERROR; if (status_irq_reg->resp_timeout) ret |= SD_EMMC_RESP_TIMEOUT_ERROR; if (status_irq_reg->desc_timeout) ret |= SD_EMMC_DESC_TIMEOUT_ERROR; if (ret) { serial_puts("sd/emmc write data error: ret="); serial_put_dec(ret); serial_puts("\n"); } //else //serial_puts("write data success!\n"); return ret; } #endif // #ifdef CONFIG_SPL_DDR_DUMP uint64_t sdio_read_data(uint64_t boot_device, uint64_t src, uint64_t des, uint64_t size) { unsigned mode,blk_cnt,ret; struct sd_emmc_global_regs *sd_emmc_regs=0; union sd_emmc_setup *s_setup = (union sd_emmc_setup *)SEC_AO_SEC_GP_CFG1; if (boot_device == BOOT_DEVICE_EMMC) sd_emmc_regs = (struct sd_emmc_global_regs *)SD_EMMC_BASE_C; else if(boot_device == BOOT_DEVICE_SD) sd_emmc_regs = (struct sd_emmc_global_regs *)SD_EMMC_BASE_B; else serial_puts("sd/emmc boot device error\n"); mode = s_setup->b.sdhc | s_setup->b.hcs ? 1 : 0; #if 0 if (mode) serial_puts("sd/emmc is lba mode\n"); else serial_puts("sd/emmc is byte mode\n"); #endif blk_cnt = ((size+511)&(~(511)))>>9; do { ret = sdio_read_blocks(sd_emmc_regs,src,des,(blk_cnt>MAX_BLOCK_COUNTS)?MAX_BLOCK_COUNTS:blk_cnt,mode); if (ret) return ret; if (blk_cnt>MAX_BLOCK_COUNTS) { src += MAX_BLOCK_COUNTS<<9; des += MAX_BLOCK_COUNTS<<9; blk_cnt -= MAX_BLOCK_COUNTS; }else break; }while(1); return ret; } #ifdef CONFIG_SPL_DDR_DUMP uint64_t sdio_write_data(uint64_t boot_device, uint64_t src, uint64_t des, uint64_t size) { unsigned mode,blk_cnt,ret; struct sd_emmc_global_regs *sd_emmc_regs=0; union sd_emmc_setup *s_setup = (union sd_emmc_setup *)SEC_AO_SEC_GP_CFG1; if (boot_device == BOOT_DEVICE_EMMC) sd_emmc_regs = (struct sd_emmc_global_regs *)SD_EMMC_BASE_C; else if(boot_device == BOOT_DEVICE_SD) sd_emmc_regs = (struct sd_emmc_global_regs *)SD_EMMC_BASE_B; else serial_puts("sd/emmc boot device error\n"); mode = s_setup->b.sdhc | s_setup->b.hcs ? 1 : 0; #if 0 if (mode) serial_puts("sd/emmc is lba mode\n"); else serial_puts("sd/emmc is byte mode\n"); #endif blk_cnt = ((size+511)&(~(511)))>>9; do { ret = sdio_write_blocks(sd_emmc_regs,src,des,(blk_cnt>MAX_BLOCK_COUNTS)?MAX_BLOCK_COUNTS:blk_cnt,mode); if (ret) return ret; if (blk_cnt>MAX_BLOCK_COUNTS) { src += MAX_BLOCK_COUNTS<<9; des += MAX_BLOCK_COUNTS<<9; blk_cnt -= MAX_BLOCK_COUNTS; }else break; }while(1); return ret; } #endif // #ifdef CONFIG_SPL_DDR_DUMP uint64_t get_boot_device(void) { const unsigned forceUsbRegVal = readl(SEC_AO_RTI_STATUS_REG3); const unsigned forceUsbBootFlag = ( forceUsbRegVal>>12 ) & 0xF; if ( 2 == forceUsbBootFlag) return BOOT_DEVICE_USB_FORCEMODE; return (readl(SEC_AO_SEC_GP_CFG0) & 0xf); } uint64_t get_ddr_size(void) { return ((readl(SEC_AO_SEC_GP_CFG0) >> 16) & 0xffff); }