/*************************************************************** ** ** Broadcom Corp. Confidential ** Copyright 2007 Broadcom Corp. All Rights Reserved. ** ** THIS SOFTWARE MAY ONLY BE USED SUBJECT TO AN EXECUTED ** SOFTWARE LICENSE AGREEMENT BETWEEN THE USER AND BROADCOM. ** YOU HAVE NO RIGHT TO USE OR EXPLOIT THIS MATERIAL EXCEPT ** SUBJECT TO THE TERMS OF SUCH AN AGREEMENT. ** ** File: bspi_flash.c ** Description: Serial flash mgmt ** Created: Wed Mar 21 13:31:21 PDT 2007 - Jeffrey P. Fisher ** ****************************************************************/ #include "bspi_flash.h" #include "ministd.h" #include "bchp_hif_mspi.h" #include "bchp_bspi.h" #include "bcm_mips_defs.h" #include "gist.h" #include "bstd.h" BDBG_MODULE(bspi_flash); /* Register software module with debug interface */ #ifndef WriteReg32 #define WriteReg32(reg,val) BREG_Write32(GetREG(),reg,val) #endif #ifndef ReadReg32 #define ReadReg32(reg) BREG_Read32(GetREG(),reg) #endif /*************************************************************************** Summary: The SPI flash interface handle. ****************************************************************************/ struct bspi { bool initialized; unsigned char max_write; bspi_settings_t settings; }; /* * Map 700x and 354x MSPI register definitions to 75xx MSPI definitions */ #define BCHP_MSPI_SPCR0_LSB BCHP_HIF_MSPI_SPCR0_LSB #define BCHP_MSPI_SPCR0_MSB BCHP_HIF_MSPI_SPCR0_MSB #define BCHP_MSPI_SPCR1_LSB BCHP_HIF_MSPI_SPCR1_LSB #define BCHP_MSPI_SPCR1_MSB BCHP_HIF_MSPI_SPCR1_MSB #define BCHP_MSPI_NEWQP BCHP_HIF_MSPI_NEWQP #define BCHP_MSPI_ENDQP BCHP_HIF_MSPI_ENDQP #define BCHP_MSPI_SPCR2 BCHP_HIF_MSPI_SPCR2 #define BCHP_MSPI_MSPI_STATUS BCHP_HIF_MSPI_MSPI_STATUS #define BCHP_MSPI_CPTQP BCHP_HIF_MSPI_CPTQP #define BCHP_MSPI_TXRAM00 BCHP_HIF_MSPI_TXRAM00 #define BCHP_MSPI_RXRAM00 BCHP_HIF_MSPI_RXRAM00 #define BCHP_MSPI_RXRAM01 BCHP_HIF_MSPI_RXRAM01 #define BCHP_MSPI_CDRAM00 BCHP_HIF_MSPI_CDRAM00 #define BCHP_MSPI_MSPI_STATUS_reserved0_MASK BCHP_HIF_MSPI_MSPI_STATUS_reserved0_MASK #define BCHP_MSPI_MSPI_STATUS_reserved0_SHIFT BCHP_HIF_MSPI_MSPI_STATUS_reserved0_SHIFT #define BCHP_MSPI_MSPI_STATUS_HALTA_MASK BCHP_HIF_MSPI_MSPI_STATUS_HALTA_MASK #define BCHP_MSPI_MSPI_STATUS_HALTA_SHIFT BCHP_HIF_MSPI_MSPI_STATUS_HALTA_SHIFT #define BCHP_MSPI_MSPI_STATUS_SPIF_MASK BCHP_HIF_MSPI_MSPI_STATUS_SPIF_MASK #define BCHP_MSPI_MSPI_STATUS_SPIF_SHIFT BCHP_HIF_MSPI_MSPI_STATUS_SPIF_SHIFT #define BCHP_MSPI_SPCR0_MSB_CPOL_MASK BCHP_HIF_MSPI_SPCR0_MSB_CPOL_MASK #define BCHP_MSPI_SPCR0_MSB_CPHA_MASK BCHP_HIF_MSPI_SPCR0_MSB_CPHA_MASK #define BCHP_MSPI_SPCR0_MSB_MSTR_MASK BCHP_HIF_MSPI_SPCR0_MSB_MSTR_MASK #define BCHP_MSPI_SPCR2_spe_MASK BCHP_HIF_MSPI_SPCR2_spe_MASK #define SPI_WREN_CMD (0x06) #define SPI_WRDI_CMD (0x04) #define SPI_RDSR_CMD (0x05) #define SPI_READ_CMD (0x03) #define SPI_BE64_CMD (0xD8) #define SPI_SE4_CMD (0x20) #define SPI_PP_CMD (0x02) #define SPI_RDID_CMD (0x9f) #define SPI_POLLING_INTERVAL 10 /* in usecs */ #define BSPI_Pcs_eUpgSpiPcs0 0 #define SPI_CDRAM_CONT 0x80 #define SPI_CDRAM_PCS_PCS0 0x01 #define SPI_CDRAM_PCS_PCS1 0x02 #define SPI_CDRAM_PCS_PCS2 0x04 #define SPI_CDRAM_PCS_PCS3 0x08 #define SPI_CDRAM_PCS_DISABLE_ALL (SPI_CDRAM_PCS_PCS0 | SPI_CDRAM_PCS_PCS1 | SPI_CDRAM_PCS_PCS2 | SPI_CDRAM_PCS_PCS3) #define SPI_SYSTEM_CLK 27000000 /* 27 MHz */ #define MAX_SPI_BAUD 1687500 /* SPBR = 8, 27MHZ */ #if SUPPORT_DST_PLATFORM //BKTODO check #define BSPI_MAX_WRITE 12 //12byte -> 8byte ¼öÁ¤ #else #define BSPI_MAX_WRITE 12 #endif #define BSPI_SECTOR_64K 0x10000 #define BSPI_SECTOR_4K 0x1000 #define BSPI_PAGE_1 0x100 #define MSPI_CALC_TIMEOUT(bytes,baud) ((((bytes * 9000)/baud) * 110)/100 + 1) static struct bspi s_spi = { false, BSPI_MAX_WRITE, { SPI_SE4_CMD, BSPI_SECTOR_4K}}; /* Summary: Performs small delays in microseconds */ void bspi_udelay(int microseconds) { unsigned int mips_cycles,end_cycles; mips_cycles = bcm_read_cp0($9,0); end_cycles = mips_cycles + (g_ticks_per_second * g_cycles_per_tick * microseconds) /1000000; do{ __asm__ volatile ("mfc0 %0, $9":"=r"(mips_cycles)); }while(0 == ((end_cycles - mips_cycles) & 0x80000000)); } /* Summary: Disables BSPI (memory mapped SPI interface) temporarily. */ static unsigned int bspi_disable_bspi(void) { while (ReadReg32(BCHP_BSPI_BUSY_STATUS)) ; WriteReg32(BCHP_BSPI_MAST_N_BOOT_CTRL,1); return 0; } /* Summary: Enabled BSPI (memory mapped SPI interface), disabled by bspi_enter_critical. */ static void bspi_enable_bspi(unsigned int flags) { WriteReg32(BCHP_BSPI_MAST_N_BOOT_CTRL,0); bspi_udelay(100); } /* Summary: poll for completion. */ static int mspi_wait(unsigned int timeout_ms) { unsigned int loopCnt,lval; /* * Polling mode */ BDBG_MSG(("%s:%d(%d)\n",__FUNCTION__,__LINE__,timeout_ms)); loopCnt = ((timeout_ms * 1000) / SPI_POLLING_INTERVAL) + 1; while (1) { lval = ReadReg32(BCHP_MSPI_MSPI_STATUS); if (lval & BCHP_MSPI_MSPI_STATUS_SPIF_MASK) { BDBG_MSG(("%s:%d status done 0x%08x\n",__FUNCTION__,__LINE__,lval)); break; } if (loopCnt == 0) { /* Transfer finished, clear SPIF bit */ WriteReg32( BCHP_MSPI_MSPI_STATUS, 0); BDBG_MSG(("%s:%d timeout = %d\n",__FUNCTION__,__LINE__,timeout_ms)); return -1; } bspi_udelay(SPI_POLLING_INTERVAL); loopCnt--; } /* Transfer finished, clear SPIF bit */ WriteReg32( BCHP_MSPI_MSPI_STATUS, 0); return 0; } /* Summary: SPI transactionn. */ static bresult mspi_transaction( unsigned char *w_buf, unsigned char write_len, unsigned char *r_buf, unsigned char read_len) { unsigned int lval; unsigned char i, len; static int s_init = 0; /* set up clock once */ if (!s_init) { s_init = 1; lval = SPI_SYSTEM_CLK / (2 * MAX_SPI_BAUD); WriteReg32(BCHP_MSPI_SPCR0_LSB, lval ); /* Configure the clock */ lval = ReadReg32(BCHP_MSPI_SPCR0_MSB); lval &= ~(BCHP_MSPI_SPCR0_MSB_CPOL_MASK | BCHP_MSPI_SPCR0_MSB_CPHA_MASK); lval |= (BCHP_MSPI_SPCR0_MSB_MSTR_MASK | (BCHP_MSPI_SPCR0_MSB_CPOL_MASK | BCHP_MSPI_SPCR0_MSB_CPHA_MASK)); WriteReg32(BCHP_MSPI_SPCR0_MSB, lval ); } len = write_len + read_len; for (i = 0; i < len; ++i) { if (i < write_len) { WriteReg32( BCHP_MSPI_TXRAM00 + (i * 8), (unsigned int)w_buf[i] ); } lval = SPI_CDRAM_CONT | SPI_CDRAM_PCS_DISABLE_ALL; lval &= ~(1 << BSPI_Pcs_eUpgSpiPcs0); WriteReg32( BCHP_MSPI_CDRAM00 + (i * 4) , lval ); } lval = SPI_CDRAM_PCS_DISABLE_ALL; lval &= ~(1 << BSPI_Pcs_eUpgSpiPcs0); WriteReg32( BCHP_MSPI_CDRAM00 + ((len - 1) * 4), lval ); /* Set queue pointers */ WriteReg32( BCHP_MSPI_NEWQP, 0 ); WriteReg32( BCHP_MSPI_ENDQP, len - 1 ); /* Start SPI transfer */ lval = ReadReg32( BCHP_MSPI_SPCR2); lval |= BCHP_MSPI_SPCR2_spe_MASK; WriteReg32(BCHP_MSPI_SPCR2, lval); /* Wait for SPI to finish */ if (mspi_wait(MSPI_CALC_TIMEOUT(len,MAX_SPI_BAUD)) != 0) { return berr_timeout; } for (i = write_len; i < len; ++i) { r_buf[i-write_len] = (unsigned char)ReadReg32( BCHP_MSPI_RXRAM01 + (i * 8)); } return b_ok; } /*************************************************************************** Summary: Initialize and return SPI interface handle. Description: Initialize and return a SPI interface handle. This function expects the bspi_settings_t structure to be initialized. Returns: SPI interface handle, non-zero on failure. See Also: bspi_close(), bspi_identify(), ****************************************************************************/ bresult bspi_open( bspi_t *h_spi, /* [out] Return SPI handle */ bspi_settings_t *p_settings /* [in] SPI flash settings */ ) { *h_spi = NULL; if (s_spi.initialized) { return berr_invalid_parameter; } s_spi.settings = *p_settings; s_spi.initialized = true; *h_spi = &s_spi; return b_ok; } /*************************************************************************** Summary: Release SPI interface handle. Description: Release SPI interface handle and any other SPI flash resources. Returns: non-zero on failure. See Also: bspi_open(), ****************************************************************************/ bresult bspi_close( bspi_t h_spi /* [in] SPI handle */ ) { if (!s_spi.initialized) { return berr_invalid_parameter; } s_spi.initialized = false; return b_ok; } /*************************************************************************** Summary: Reads a single byte from flash using the SPI interface, not memory mapped spi. ****************************************************************************/ bresult bspi_read( bspi_t h_spi, /* [in] SPI handle */ unsigned int offset, /* [in] Offset in bytes */ unsigned char *data /* [out] buffer to receive data */ ) { unsigned char cmd[4]; bresult result; unsigned int flags; cmd[0] = SPI_READ_CMD; cmd[1] = ((unsigned char*)&offset)[2]; cmd[2] = ((unsigned char*)&offset)[1]; cmd[3] = ((unsigned char*)&offset)[0]; flags = bspi_disable_bspi(); result = mspi_transaction(cmd,4,data,1); bspi_enable_bspi(flags); return result; } /*************************************************************************** Summary: Erase flash sector at offset. ****************************************************************************/ bresult bspi_sector_erase( bspi_t h_spi, /* [in] SPI handle */ unsigned int offset /* [in] Offset in bytes */ ) { bresult result; unsigned char cmd[4]; unsigned char data; unsigned int flags; flags = bspi_disable_bspi(); cmd[0] = SPI_WREN_CMD; if ((result = mspi_transaction(cmd,1,NULL,0)) != b_ok) goto done; cmd[0] = h_spi->settings.se_cmd; cmd[1] = ((unsigned char*)&offset)[2]; cmd[2] = ((unsigned char*)&offset)[1]; cmd[3] = ((unsigned char*)&offset)[0]; if ((result = mspi_transaction(cmd,4,NULL,0)) != b_ok) goto done; #ifndef CONFIG_ROM_EMULATOR do { cmd[0] = SPI_RDSR_CMD; if ((result = mspi_transaction(cmd,1,&data,1)) != b_ok) goto done; }while (data & 0x01/* busy*/); #else bspi_udelay(100); #endif cmd[0] = SPI_WRDI_CMD; result = mspi_transaction(cmd,1,NULL,0); done: bspi_enable_bspi(flags); return result; } /*************************************************************************** Summary: Program the flash page at offset with the data provided in the buffer. ****************************************************************************/ static bresult bspi_program( bspi_t h_spi, /* [in] SPI handle */ unsigned int offset, /* [in] Offset in bytes */ unsigned char *buf, /* [in] Data buffer to program */ int len /* [in] Size in bytes of data buffer */ ) { bresult result; static unsigned char cmd[16]; unsigned char data; unsigned int flags; if (len > 12) /* Max bytes per transaction */ return berr_invalid_parameter; flags = bspi_disable_bspi(); cmd[0] = SPI_WREN_CMD; if ((result = mspi_transaction(cmd,1,NULL,0)) != b_ok) goto done; cmd[0] = SPI_PP_CMD; cmd[1] = ((unsigned char*)&offset)[2]; cmd[2] = ((unsigned char*)&offset)[1]; cmd[3] = ((unsigned char*)&offset)[0]; memcpy(&(cmd[4]),buf,len); if ((result = mspi_transaction(cmd,len + 4,NULL,0)) != b_ok) goto done; #ifndef CONFIG_ROM_EMULATOR do { cmd[0] = SPI_RDSR_CMD; if ((result = mspi_transaction(cmd,1,&data,1)) != b_ok) goto done; }while (data & 0x01/* busy*/); #else bspi_udelay(100); #endif cmd[0] = SPI_WRDI_CMD; result = mspi_transaction(cmd,1,NULL,0); done: bspi_enable_bspi(flags); return result; } /*************************************************************************** Summary: Program the flash page at offset with the data provided in the buffer. ****************************************************************************/ bresult bspi_page_program( bspi_t h_spi, /* [in] SPI handle */ unsigned int offset, /* [in] Offset in bytes */ unsigned char *buf, /* [in] Data buffer to program */ int len /* [in] Size in bytes of data buffer */ ) { int res; bresult bres; unsigned int block_count; block_count = len / h_spi->max_write; bres = b_ok; while (block_count > 0) { bres = bspi_program(h_spi, offset, buf, h_spi->max_write); if (b_ok != bres) { goto ExitFunc; } buf = (void*)(((unsigned int)buf) + h_spi->max_write); offset = offset + h_spi->max_write; block_count--; } block_count = len % h_spi->max_write; if (block_count != 0) { bres = bspi_program(h_spi, offset, buf, block_count); if (b_ok != bres) { goto ExitFunc; } buf = (void*)(((unsigned int)buf) + block_count); offset = offset + block_count; } bspi_flush(); ExitFunc: res = (b_ok == bres) ? b_ok : berr_timeout; return res; } /*************************************************************************** Summary: Flush BSPI interface. ****************************************************************************/ void bspi_flush(void) { WriteReg32(BCHP_BSPI_B0_CTRL, 1); WriteReg32(BCHP_BSPI_B1_CTRL, 1); WriteReg32(BCHP_BSPI_B0_CTRL, 0); WriteReg32(BCHP_BSPI_B1_CTRL, 0); } /*************************************************************************** Summary: Use RDID command to determine flash settings. ****************************************************************************/ bresult bspi_identify( bspi_settings_t *p_settings /* [out] SPI flash settings structure */ ) { bresult res; unsigned int flags; unsigned char cmd[1]; unsigned char data[3]; unsigned fid; cmd[0] = SPI_RDID_CMD; flags = bspi_disable_bspi(); if (b_ok != (res = mspi_transaction(cmd, 1, data, 3))) { data[0] = data[1] = data[2] = 0; res = b_ok; } bspi_enable_bspi(flags); fid = (data[0] << 16) | (data[1] << 8) | data[2]; printf("fid = 0x%X\n", fid); switch (fid) { case 0xc22017: /* MX25L6406E */ //megakiss 20140412 MX25L6406E SE ¸í·É¾î Å©±â¸¦ 64K¿¡¼­ 4K·Î º¯°æ s_spi.settings.se_cmd = SPI_SE4_CMD; s_spi.settings.sector_size = BSPI_SECTOR_4K; p_settings->total_size = 8*1024*1024; break; case 0x1c3016: /* CFEON EN25Q31B1 */ s_spi.settings.se_cmd = SPI_SE4_CMD; s_spi.settings.sector_size = BSPI_SECTOR_4K; p_settings->total_size = 4*1024*1024; break; default: s_spi.settings.se_cmd = SPI_SE4_CMD; s_spi.settings.sector_size = BSPI_SECTOR_4K; p_settings->total_size = 4*1024*1024; break; } p_settings->sector_size = s_spi.settings.sector_size; p_settings->se_cmd = s_spi.settings.se_cmd; p_settings->page_size = BSPI_PAGE_1; return res; }