text: /* 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.
   *
   * NPCX SoC spi flash update tool - monitor firmware
   */
  
  #include <stdint.h>
  #include "config.h"
  #include "npcx_monitor.h"
  #include "registers.h"
  #include "util.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);
  }
  
  int 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 for checking */
  	sspi_flash_execute_cmd(CMD_READ_STATUS_REG, MASK_CMD_RD_1BYTE);
  	if (NPCX_UMA_DB0 != 0x00)
  		return 0;
  	sspi_flash_execute_cmd(CMD_READ_STATUS_REG2, MASK_CMD_RD_1BYTE);
  	if (NPCX_UMA_DB0 != 0x00)
  		return 0;
  	/* Enable tri-state */
  	sspi_flash_tristate(1);
  
  	return 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 -= NPCX_MONITOR_FLASH_ERASE_SIZE,
  		offset += NPCX_MONITOR_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);
  }
  
  int sspi_flash_verify(int offset, int size, const char *data)
  {
  	int i, result;
  	uint8_t *ptr_flash;
  	uint8_t *ptr_mram;
  	uint8_t cmp_data;
  
  	ptr_flash = (uint8_t *)(CONFIG_MAPPED_STORAGE_BASE + offset);
  	ptr_mram  = (uint8_t *)data;
  	result = 1;
  
  	/* Disable tri-state */
  	sspi_flash_tristate(0);
  
  	/* Start to verify */
  	for (i = 0; i < size; i++) {
  		cmp_data = ptr_mram ? ptr_mram[i] : 0xFF;
  		if (ptr_flash[i] != cmp_data) {
  			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 = MAX(CONFIG_RO_SIZE, CONFIG_RW_SIZE); /* max 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 */
  
  }
  
  /* Entry function of spi upload function */
  uint32_t  __attribute__ ((section(".startup_text")))
  sspi_flash_upload(int spi_offset, int spi_size)
  {
  	/*
  	 * Flash image has been uploaded to Code RAM
  	 */
  	uint32_t sz_image;
  	uint32_t uut_tag;
  	const char *image_base;
  	uint32_t *flag_upload = (uint32_t *)SPI_PROGRAMMING_FLAG;
  	struct monitor_header_tag *monitor_header =
  		(struct monitor_header_tag *)NPCX_MONITOR_HEADER_ADDR;
  
  	*flag_upload = 0;
  
  	uut_tag = monitor_header->tag;
  	/* If it is UUT tag, read required parameters from header */
  	if (uut_tag == NPCX_MONITOR_UUT_TAG) {
  		sz_image = monitor_header->size;
  		spi_offset = monitor_header->dest_addr;
  		image_base = (const char *)(monitor_header->src_addr);
  	} else {
  		sz_image = spi_size;
  		image_base = (const char *)CONFIG_PROGRAM_MEMORY_BASE;
  	}
  
  	/* Unlock & stop watchdog */
  	NPCX_WDSDM = 0x87;
  	NPCX_WDSDM = 0x61;
  	NPCX_WDSDM = 0x63;
  
  	/* UMA Unlock */
  	CLEAR_BIT(NPCX_UMA_ECTS, NPCX_UMA_ECTS_UMA_LOCK);
  
  	/* 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 */
  	if (sspi_flash_physical_clear_stsreg()) {
  		/* Start to erase */
  		sspi_flash_physical_erase(spi_offset, sz_image);
  		/* Start to write */
  		if (image_base != NULL)
  			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;
  
  	/* Return the status back to ROM code is required for UUT */
  	if (uut_tag == NPCX_MONITOR_UUT_TAG)
  		return *flag_upload;
  
  	/* Infinite loop */
  	for (;;)
  		;
  }
  
  