/*************************************************************************** * Copyright (c) 2011, Broadcom Corporation * All Rights Reserved * Confidential Property of Broadcom Corporation * * 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. * * $brcm_Workfile: $ * $brcm_Revision: $ * $brcm_Date: $ * * Module Description: spi interface for jtag flash programmer. * * Revision History: * * $brcm_Log: $ * * ***************************************************************************/ #include "bstd.h" #include "bchp_hif_mspi.h" #include "bchp_timer.h" #include "bchp_bspi.h" #include "bchp_sun_top_ctrl.h" #include "jflu_spi.h" #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 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_PAGE_SIZE 0x100 #define SPI_SECTOR_64K 0x10000 #define SPI_SECTOR_4K 0x1000 #define SPI_SYSTEM_CLK 27000000 /* 27 MHz */ #define MAX_SPI_BAUD 1687500 /* SPBR = 8, 27MHZ */ #define SPI_CALC_TIMEOUT(bytes,baud) ((((bytes * 9000)/baud) * 110)/100 + 1) #define SPI_OK 0 #define SPI_ERROR 1 #define REG_BASE 0xb0000000 #define ReadReg32(reg) (*((volatile uint32_t *)((unsigned int)(reg) + REG_BASE))) #define WriteReg32(reg, data) (*((volatile uint32_t *)((unsigned int)(reg) + REG_BASE)) = (data)) typedef enum spi_transfer_t { SPI_TRAN_MULTI, /* start of multipart transfer */ SPI_TRAN_FINAL, /* end of multipart transfer */ } spi_transfer_t; void udelay(int microseconds); static unsigned int disable_bspi(void) { while (ReadReg32(BCHP_BSPI_BUSY_STATUS)); WriteReg32(BCHP_BSPI_MAST_N_BOOT_CTRL,1); return 0; } static void enable_bspi(unsigned int flags) { WriteReg32(BCHP_BSPI_MAST_N_BOOT_CTRL,0); udelay(100); } #define COUNT_PER_TICK 500000 void udelay(int microseconds) { unsigned int mips_cycles,end_cycles; unsigned int clock; __asm__("mfc0 %0, $9":"=r"(mips_cycles)); end_cycles = mips_cycles + ((200 * COUNT_PER_TICK) / 1000000) * microseconds; if (end_cycles < mips_cycles) { do { __asm__ volatile ("mfc0 %0, $9":"=r"(clock)); } while(clock > end_cycles); } do { __asm__ volatile ("mfc0 %0, $9":"=r"(clock)); } while(clock < end_cycles); } static int spi_wait(unsigned int timeout_ms) { unsigned int loopCnt,lval; /* * Polling mode */ loopCnt = ((timeout_ms * 1000) / SPI_POLLING_INTERVAL) + 1; while (1) { lval = ReadReg32(BCHP_HIF_MSPI_MSPI_STATUS); if (lval & BCHP_HIF_MSPI_MSPI_STATUS_SPIF_MASK) { break; } if (loopCnt == 0) { /* Transfer finished, clear SPIF bit */ WriteReg32( BCHP_HIF_MSPI_MSPI_STATUS, 0); return SPI_ERROR; } udelay(SPI_POLLING_INTERVAL); loopCnt--; } /* Transfer finished, clear SPIF bit */ WriteReg32( BCHP_HIF_MSPI_MSPI_STATUS, 0); return SPI_OK; } void jflu_spi_init(spi_flash_t * flash) { unsigned int lval; lval = SPI_SYSTEM_CLK / (2 * MAX_SPI_BAUD); WriteReg32(BCHP_HIF_MSPI_SPCR0_LSB, lval ); /* Configure the clock */ lval = ReadReg32(BCHP_HIF_MSPI_SPCR0_MSB); lval &= ~(BCHP_HIF_MSPI_SPCR0_MSB_CPOL_MASK | BCHP_HIF_MSPI_SPCR0_MSB_CPHA_MASK); lval |= (BCHP_HIF_MSPI_SPCR0_MSB_MSTR_MASK | (BCHP_HIF_MSPI_SPCR0_MSB_CPOL_MASK | BCHP_HIF_MSPI_SPCR0_MSB_CPHA_MASK)); WriteReg32(BCHP_HIF_MSPI_SPCR0_MSB, lval ); flash->se_cmd = 0; flash->sector_size = 0; flash->page_size = 0; } static int spi_transfer(unsigned char * w_buf, unsigned int w_len, unsigned char *r_buf, unsigned int r_len, spi_transfer_t tran) { unsigned int lval; unsigned char i, len; len = w_len + r_len; for (i = 0; i < len; i++) { if (i < w_len) WriteReg32((BCHP_HIF_MSPI_TXRAM00 + (i * 8)), (unsigned int)w_buf[i] ); lval = SPI_CDRAM_CONT | SPI_CDRAM_PCS_DISABLE_ALL; lval &= ~(SPI_CDRAM_PCS_PCS0); WriteReg32((BCHP_HIF_MSPI_CDRAM00 + (i * 4)), lval ); } if(SPI_TRAN_FINAL == tran){ lval = SPI_CDRAM_PCS_DISABLE_ALL; lval &= ~(SPI_CDRAM_PCS_PCS0); WriteReg32( BCHP_HIF_MSPI_CDRAM00 + ((len - 1) * 4), lval ); } /* Set queue pointers */ WriteReg32(BCHP_HIF_MSPI_NEWQP, 0); WriteReg32(BCHP_HIF_MSPI_ENDQP, len - 1); /* Start SPI transfer */ lval = ReadReg32(BCHP_HIF_MSPI_SPCR2); if(SPI_TRAN_FINAL == tran){ lval &= ~(BCHP_HIF_MSPI_SPCR2_cont_after_cmd_MASK); }else{ /* set WriteLock bit before starting transaction and keep it set during all SPI_TRAN_MULTI transactions */ WriteReg32(BCHP_HIF_MSPI_WRITE_LOCK, 1); lval |= BCHP_HIF_MSPI_SPCR2_cont_after_cmd_MASK; } lval |= BCHP_HIF_MSPI_SPCR2_spe_MASK; WriteReg32(BCHP_HIF_MSPI_SPCR2, lval); /* Wait for SPI to finish */ if (spi_wait(SPI_CALC_TIMEOUT(len,MAX_SPI_BAUD)) != 0) { return 1; } if(SPI_TRAN_FINAL == tran){ /* clear WriteLock bit after completing final transation */ WriteReg32(BCHP_HIF_MSPI_WRITE_LOCK, 0); } for (i = w_len; i < len; ++i) { r_buf[i-w_len] = (unsigned char)ReadReg32( BCHP_HIF_MSPI_RXRAM01 + (i * 8)); } return 0; } /* Summary: Erase the spi flash sector at offset. */ int jflu_spi_sector_erase(spi_flash_t * flash, unsigned int offset) { int result; unsigned char cmd[4]; unsigned char data; unsigned int flags; flags = disable_bspi(); cmd[0] = SPI_WREN_CMD; if ((result = spi_transfer(cmd,1,NULL,0, SPI_TRAN_FINAL)) != SPI_OK) goto done; cmd[0] = flash->se_cmd; cmd[1] = ((unsigned char*)&offset)[2]; cmd[2] = ((unsigned char*)&offset)[1]; cmd[3] = ((unsigned char*)&offset)[0]; if ((result = spi_transfer(cmd,4,NULL,0, SPI_TRAN_FINAL)) != SPI_OK) goto done; do { cmd[0] = SPI_RDSR_CMD; if ((result = spi_transfer(cmd,1,&data,1, SPI_TRAN_FINAL)) != SPI_OK) goto done; }while(data & 0x01/* busy*/); cmd[0] = SPI_WRDI_CMD; result = spi_transfer(cmd,1,NULL,0, SPI_TRAN_FINAL); enable_bspi(flags); done: return result; } int jflu_spi_page_program(spi_flash_t * flash, unsigned int offset, unsigned char *buf, int len) { int result; static unsigned char cmd[SPI_PAGE_SIZE + 4]; int i,len_total; unsigned char data; unsigned int flags; if (len > flash->page_size){ /* Max bytes per transaction */ result = SPI_ERROR; goto done; } flags = disable_bspi(); cmd[0] = SPI_WREN_CMD; if ((result = spi_transfer(cmd,1,NULL,0,SPI_TRAN_FINAL)) != SPI_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]; /* transfer command */ if ((result = spi_transfer(cmd,4,NULL,0,SPI_TRAN_MULTI)) != SPI_OK) goto done; /* transfer buffer in 16 byte chunks */ i = 0; len_total = len; while (len_total > 16){ if ((result = spi_transfer(buf+i,16,NULL,0,SPI_TRAN_MULTI)) != SPI_OK) goto done; i+=16; len_total -= 16; } /* transfer last chunk of the buffer */ if(len_total <= 16 && len_total > 0) { if ((result = spi_transfer(buf + i,len_total,NULL,0,SPI_TRAN_FINAL)) != SPI_OK) goto done; } /* read status */ do { cmd[0] = SPI_RDSR_CMD; if ((result = spi_transfer(cmd,1,&data,1,SPI_TRAN_FINAL)) != SPI_OK) goto done; }while(data & 0x01/* busy*/); cmd[0] = SPI_WRDI_CMD; result = spi_transfer(cmd,1,NULL,0,SPI_TRAN_FINAL); enable_bspi(flags); done: return result; } int jflu_spi_identify(spi_flash_t * flash) { int res; unsigned int flags; unsigned char cmd[1]; unsigned char data[3]; unsigned fid; cmd[0] = SPI_RDID_CMD; flags = disable_bspi(); if (0 != (res = spi_transfer(cmd, 1, data, 3,SPI_TRAN_FINAL))) { data[0] = data[1] = data[2] = 0; res = 0; } enable_bspi(flags); fid = (data[0] << 16) | (data[1] << 8) | data[2]; switch (fid) { case 0x010213: case 0x010214: case 0x010215: case 0x010216: case 0x012018: /* Spansion S25FL 129P (Model 00) */ /* spansion S25FL01XA */ flash->se_cmd = SPI_BE64_CMD; flash->sector_size = SPI_SECTOR_64K; break; case 0xc22015: /* MX25L1606E */ case 0xc22016: /* MX25L3206E */ case 0xc22017: /* MX25L6406E */ flash->se_cmd = SPI_BE64_CMD; flash->sector_size = SPI_SECTOR_64K; break; case 0xc22013: /* MX25L8004 */ case 0xc22014: /* MX25L8008 */ case 0xef4017: /* S25FL060K */ default: flash->se_cmd = SPI_SE4_CMD; flash->sector_size = SPI_SECTOR_4K; break; } flash->page_size = SPI_PAGE_SIZE; return res; } /* Please do not remove! */ /* Local Variables: */ /* mode: C */ /* indent-tabs-mode: nil */ /* End: */