summaryrefslogtreecommitdiff
path: root/chip/npcx/spiflashfw/ec_npcxflash.c
diff options
context:
space:
mode:
Diffstat (limited to 'chip/npcx/spiflashfw/ec_npcxflash.c')
-rw-r--r--chip/npcx/spiflashfw/ec_npcxflash.c295
1 files changed, 295 insertions, 0 deletions
diff --git a/chip/npcx/spiflashfw/ec_npcxflash.c b/chip/npcx/spiflashfw/ec_npcxflash.c
new file mode 100644
index 0000000000..0a7dc2b2e2
--- /dev/null
+++ b/chip/npcx/spiflashfw/ec_npcxflash.c
@@ -0,0 +1,295 @@
+/* Copyright (c) 2014 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ *
+ * NPCX5M5G SoC spi flash update tool
+ */
+
+#include <stdint.h>
+#include "registers.h"
+#include "config_chip.h"
+
+/*****************************************************************************/
+/* spi flash internal functions */
+void sspi_flash_pinmux(int enable)
+{
+ if (enable)
+ CLEAR_BIT(NPCX_DEVALT(0), NPCX_DEVALT0_NO_F_SPI);
+ else
+ SET_BIT(NPCX_DEVALT(0), NPCX_DEVALT0_NO_F_SPI);
+
+ /* CS0/1 pinmux */
+ if (enable) {
+#if (FIU_CHIP_SELECT == 1)
+ SET_BIT(NPCX_DEVALT(0), NPCX_DEVALT0_F_SPI_CS1_1);
+#elif (FIU_CHIP_SELECT == 2)
+ SET_BIT(NPCX_DEVALT(0), NPCX_DEVALT0_F_SPI_CS1_2);
+#endif
+ } else {
+ CLEAR_BIT(NPCX_DEVALT(0), NPCX_DEVALT0_F_SPI_CS1_1);
+ CLEAR_BIT(NPCX_DEVALT(0), NPCX_DEVALT0_F_SPI_CS1_2);
+ }
+}
+
+void sspi_flash_tristate(int enable)
+{
+ if (enable) {
+ /* Enable FIU pins to tri-state */
+ SET_BIT(NPCX_DEVCNT, NPCX_DEVCNT_F_SPI_TRIS);
+ } else {
+ /* Disable FIU pins to tri-state */
+ CLEAR_BIT(NPCX_DEVCNT, NPCX_DEVCNT_F_SPI_TRIS);
+ }
+}
+
+void sspi_flash_execute_cmd(uint8_t code, uint8_t cts)
+{
+ /* set UMA_CODE */
+ NPCX_UMA_CODE = code;
+ /* execute UMA flash transaction */
+ NPCX_UMA_CTS = cts;
+ while (IS_BIT_SET(NPCX_UMA_CTS, NPCX_UMA_CTS_EXEC_DONE))
+ ;
+}
+
+void sspi_flash_cs_level(int level)
+{
+ /* level is high */
+ if (level) {
+ /* Set chip select to high */
+ SET_BIT(NPCX_UMA_ECTS, NPCX_UMA_ECTS_SW_CS1);
+ } else { /* level is low */
+ /* Set chip select to low */
+ CLEAR_BIT(NPCX_UMA_ECTS, NPCX_UMA_ECTS_SW_CS1);
+ }
+}
+
+void sspi_flash_wait_ready(void)
+{
+ uint8_t mask = SPI_FLASH_SR1_BUSY;
+
+ /* Chip Select down. */
+ sspi_flash_cs_level(0);
+ /* Command for Read status register */
+ sspi_flash_execute_cmd(CMD_READ_STATUS_REG, MASK_CMD_ONLY);
+ do {
+ /* Read status register */
+ NPCX_UMA_CTS = MASK_RD_1BYTE;
+ while (IS_BIT_SET(NPCX_UMA_CTS, NPCX_UMA_CTS_EXEC_DONE))
+ ;
+ } while (NPCX_UMA_DB0 & mask); /* Wait for Busy clear */
+ /* Chip Select high. */
+ sspi_flash_cs_level(1);
+}
+
+int sspi_flash_write_enable(void)
+{
+ uint8_t mask = SPI_FLASH_SR1_WEL;
+ /* Write enable command */
+ sspi_flash_execute_cmd(CMD_WRITE_EN, MASK_CMD_ONLY);
+ /* Wait for flash is not busy */
+ sspi_flash_wait_ready();
+
+ if (NPCX_UMA_DB0 & mask)
+ return 1;
+ else
+ return 0;
+}
+
+void sspi_flash_set_address(uint32_t dest_addr)
+{
+ uint8_t *addr = (uint8_t *)&dest_addr;
+ /* Write address */
+ NPCX_UMA_AB2 = addr[2];
+ NPCX_UMA_AB1 = addr[1];
+ NPCX_UMA_AB0 = addr[0];
+}
+
+void sspi_flash_burst_write(unsigned int dest_addr, unsigned int bytes,
+ const char *data)
+{
+ unsigned int i;
+ /* Chip Select down. */
+ sspi_flash_cs_level(0);
+ /* Set erase address */
+ sspi_flash_set_address(dest_addr);
+ /* Start write */
+ sspi_flash_execute_cmd(CMD_FLASH_PROGRAM, MASK_CMD_WR_ADR);
+ for (i = 0; i < bytes; i++) {
+ sspi_flash_execute_cmd(*data, MASK_CMD_WR_ONLY);
+ data++;
+ }
+ /* Chip Select up */
+ sspi_flash_cs_level(1);
+}
+
+void sspi_flash_physical_clear_stsreg(void)
+{
+ /* Disable tri-state */
+ sspi_flash_tristate(0);
+ /* Enable write */
+ sspi_flash_write_enable();
+
+ NPCX_UMA_DB0 = 0x0;
+ NPCX_UMA_DB1 = 0x0;
+
+ /* Write status register 1/2 */
+ sspi_flash_execute_cmd(CMD_WRITE_STATUS_REG, MASK_CMD_WR_2BYTE);
+
+ /* Wait writing completed */
+ sspi_flash_wait_ready();
+
+ /* Read status register 1/2 */
+ sspi_flash_execute_cmd(CMD_READ_STATUS_REG, MASK_CMD_RD_1BYTE);
+ sspi_flash_execute_cmd(CMD_READ_STATUS_REG2, MASK_CMD_RD_1BYTE);
+ /* Enable tri-state */
+ sspi_flash_tristate(1);
+}
+
+void sspi_flash_physical_write(int offset, int size, const char *data)
+{
+ int dest_addr = offset;
+ const int sz_page = CONFIG_FLASH_WRITE_IDEAL_SIZE;
+
+ /* Disable tri-state */
+ sspi_flash_tristate(0);
+
+ /* Write the data per CONFIG_FLASH_WRITE_IDEAL_SIZE bytes */
+ for (; size >= sz_page; size -= sz_page) {
+ /* Enable write */
+ sspi_flash_write_enable();
+ /* Burst UMA transaction */
+ sspi_flash_burst_write(dest_addr, sz_page, data);
+ /* Wait write completed */
+ sspi_flash_wait_ready();
+
+ data += sz_page;
+ dest_addr += sz_page;
+ }
+
+ /* Handle final partial page, if any */
+ if (size != 0) {
+ /* Enable write */
+ sspi_flash_write_enable();
+ /* Burst UMA transaction */
+ sspi_flash_burst_write(dest_addr, size, data);
+
+ /* Wait write completed */
+ sspi_flash_wait_ready();
+ }
+
+ /* Enable tri-state */
+ sspi_flash_tristate(1);
+}
+
+void sspi_flash_physical_erase(int offset, int size)
+{
+ /* Disable tri-state */
+ sspi_flash_tristate(0);
+
+ /* Alignment has been checked in upper layer */
+ for (; size > 0; size -= CONFIG_FLASH_ERASE_SIZE,
+ offset += CONFIG_FLASH_ERASE_SIZE) {
+ /* Enable write */
+ sspi_flash_write_enable();
+ /* Set erase address */
+ sspi_flash_set_address(offset);
+ /* Start erase */
+ sspi_flash_execute_cmd(CMD_SECTOR_ERASE, MASK_CMD_ADR);
+
+ /* Wait erase completed */
+ sspi_flash_wait_ready();
+ }
+
+ /* Enable tri-state */
+ sspi_flash_tristate(1);
+}
+
+/* Start to write */
+int sspi_flash_verify(int offset, int size, const char *data)
+{
+ int i, result;
+ uint8_t *ptr_flash;
+ uint8_t *ptr_mram;
+
+ ptr_flash = (uint8_t *)(0x64000000+offset);
+ ptr_mram = (uint8_t *)data;
+ result = 1;
+
+ /* Disable tri-state */
+ sspi_flash_tristate(0);
+
+ /* Start to verify */
+ for (i = 0; i < size; i++) {
+ if (ptr_flash[i] != ptr_mram[i]) {
+ result = 0;
+ break;
+ }
+ }
+
+ /* Enable tri-state */
+ sspi_flash_tristate(1);
+ return result;
+}
+
+int sspi_flash_get_image_used(const char *fw_base)
+{
+ const uint8_t *image;
+ int size = 0x20000; /* maximum size is 128KB */
+
+ image = (const uint8_t *)fw_base;
+ /*
+ * Scan backwards looking for 0xea byte, which is by definition the
+ * last byte of the image. See ec.lds.S for how this is inserted at
+ * the end of the image.
+ */
+ for (size--; size > 0 && image[size] != 0xea; size--)
+ ;
+
+ return size ? size + 1 : 0; /* 0xea byte IS part of the image */
+
+}
+
+volatile __attribute__((section(".up_flag"))) unsigned int flag_upload;
+
+/* Entry function of spi upload function */
+void __attribute__ ((section(".startup_text"), noreturn))
+sspi_flash_upload(int spi_offset, int spi_size)
+{
+ /*
+ * Flash image has been uploaded to Code RAM
+ */
+ const char *image_base = (char *)0x10088000;
+ uint32_t sz_image = spi_size;
+
+ /* Set pinmux first */
+ sspi_flash_pinmux(1);
+
+ /* Get size of image automatically */
+ if (sz_image == 0)
+ sz_image = sspi_flash_get_image_used(image_base);
+
+ /* Clear status reg of spi flash for protection */
+ sspi_flash_physical_clear_stsreg();
+
+ /* Start to erase */
+ sspi_flash_physical_erase(spi_offset, sz_image);
+
+ /* Start to write */
+ sspi_flash_physical_write(spi_offset, sz_image, image_base);
+
+ /* Verify data */
+ if (sspi_flash_verify(spi_offset, sz_image, image_base))
+ flag_upload |= 0x02;
+
+ /* Disable pinmux */
+ sspi_flash_pinmux(0);
+
+ /* Mark we have finished upload work */
+ flag_upload |= 0x01;
+
+ /* Should never reach this*/
+ for (;;)
+ ;
+}
+