text: /* Copyright 2017 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.
   */
  
  /* System module for Chrome EC : MCHP hardware specific implementation */
  
  #include <stdnoreturn.h>
  
  #include "common.h" /* includes config.h and board.h */
  #include "clock.h"
  #include "clock_chip.h"
  #include "console.h"
  #include "cpu.h"
  #include "gpio.h"
  #include "hooks.h"
  #include "host_command.h"
  #include "lpc_chip.h"
  #include "registers.h"
  #include "shared_mem.h"
  #include "spi.h"
  #include "system.h"
  #include "task.h"
  #include "tfdp_chip.h"
  #include "timer.h"
  #include "util.h"
  
  #define CPUTS(outstr) cputs(CC_LPC, outstr)
  #define CPRINTS(format, args...) cprints(CC_LPC, format, ## args)
  
  /* Index values for hibernate data registers (RAM backed by VBAT) */
  enum hibdata_index {
  	HIBDATA_INDEX_SCRATCHPAD = 0,    /* General-purpose scratch pad */
  	HIBDATA_INDEX_SAVED_RESET_FLAGS, /* Saved reset flags */
  	HIBDATA_INDEX_PD0,		/* USB-PD0 saved port state */
  	HIBDATA_INDEX_PD1,		/* USB-PD1 saved port state */
  	HIBDATA_INDEX_PD2,		/* USB-PD2 saved port state */
  };
  
  /*
   * Voltage rail configuration
   * MEC172x VTR1 is 3.3V only, VTR2 is auto-detected 3.3 or 1.8V, and
   * VTR3 is always 1.8V.
   * MEC170x and MEC152x require manual selection of VTR3 for 1.8 or 3.3V.
   * The eSPI pins are on VTR3 and require 1.8V
   */
  #ifdef CHIP_FAMILY_MEC172X
  static void vtr3_voltage_select(int use18v)
  {
  	(void) use18v;
  }
  #else
  static void vtr3_voltage_select(int use18v)
  {
  	if (use18v)
  		MCHP_EC_GPIO_BANK_PWR |= MCHP_EC_GPIO_BANK_PWR_VTR3_18;
  	else
  		MCHP_EC_GPIO_BANK_PWR &= ~(MCHP_EC_GPIO_BANK_PWR_VTR3_18);
  }
  #endif
  
  
  /*
   * The current logic will set EC_RESET_FLAG_RESET_PIN flag
   * even if the reset was caused by WDT. MEC170x/MEC152x HW RESET_SYS
   * status goes active for any of the following:
   *	RESET_VTR: power rail change
   *	WDT Event: WDT timed out
   *	FW triggered chip reset: SYSRESETREQ or PCR sys reset bit
   * The code does check WDT status in the VBAT PFR register.
   * Is it correct to report both EC_RESET_FLAG_RESET_PIN and
   * EC_RESET_FLAG_WATCHDOG on a WDT only reset?
   */
  static void check_reset_cause(void)
  {
  	uint32_t status = MCHP_VBAT_STS;
  	uint32_t flags = 0;
  	uint32_t rst_sts = MCHP_PCR_PWR_RST_STS &
  				(MCHP_PWR_RST_STS_SYS |
  				MCHP_PWR_RST_STS_VBAT);
  
  	/* Clear the reset causes now that we've read them */
  	MCHP_VBAT_STS |= status;
  	MCHP_PCR_PWR_RST_STS |= rst_sts;
  
  	/*
  	 * BIT[6] indicates RESET_SYS asserted.
  	 * RESET_SYS will assert on VTR reset, WDT reset, or
  	 * firmware triggering a reset using Cortex-M4 SYSRESETREQ
  	 * or MCHP PCR system reset register.
  	 */
  	if (rst_sts & MCHP_PWR_RST_STS_SYS)
  		flags |= EC_RESET_FLAG_RESET_PIN;
  
  
  	flags |= chip_read_reset_flags();
  	chip_save_reset_flags(0);
  
  	if ((status & MCHP_VBAT_STS_WDT) && !(flags & (EC_RESET_FLAG_SOFT |
  					    EC_RESET_FLAG_HARD |
  					    EC_RESET_FLAG_HIBERNATE)))
  		flags |= EC_RESET_FLAG_WATCHDOG;
  
  	system_set_reset_flags(flags);
  }
  
  int system_is_reboot_warm(void)
  {
  	uint32_t reset_flags;
  	/*
  	 * Check reset cause here,
  	 * gpio_pre_init is executed faster than system_pre_init
  	 */
  	check_reset_cause();
  	reset_flags = system_get_reset_flags();
  
  	if ((reset_flags & EC_RESET_FLAG_RESET_PIN) ||
  		(reset_flags & EC_RESET_FLAG_POWER_ON) ||
  		(reset_flags & EC_RESET_FLAG_WATCHDOG) ||
  		(reset_flags & EC_RESET_FLAG_HARD) ||
  		(reset_flags & EC_RESET_FLAG_SOFT))
  		return 0;
  	else
  		return 1;
  }
  
  /*
   * Sleep unused blocks to reduce power.
   * Drivers/modules will clear PCR sleep enables for their blocks.
   * Keep sleep enables cleared for required blocks:
   * ECIA, PMC, CPU, ECS and optionally JTAG.
   * SLEEP_ALL feature will set these upon sleep entry.
   * Based on CONFIG_CHIPSET_DEBUG enable or disable ARM SWD
   * 2-pin JTAG mode.
   */
  static void chip_periph_sleep_control(void)
  {
  	uint32_t d;
  
  	d = MCHP_PCR_SLP_EN0_SLEEP;
  
  	if (IS_ENABLED(CONFIG_CHIPSET_DEBUG)) {
  		d &= ~(MCHP_PCR_SLP_EN0_JTAG);
  		MCHP_EC_JTAG_EN = MCHP_JTAG_MODE_SWD | MCHP_JTAG_ENABLE;
  	} else
  		MCHP_EC_JTAG_EN &= ~(MCHP_JTAG_ENABLE);
  
  	MCHP_PCR_SLP_EN0 = d;
  	MCHP_PCR_SLP_EN1 = MCHP_PCR_SLP_EN1_UNUSED_BLOCKS;
  	MCHP_PCR_SLP_EN2 = MCHP_PCR_SLP_EN2_SLEEP;
  	MCHP_PCR_SLP_EN3 = MCHP_PCR_SLP_EN3_SLEEP;
  	MCHP_PCR_SLP_EN4 = MCHP_PCR_SLP_EN4_SLEEP;
  }
  
  #ifdef CONFIG_CHIP_PRE_INIT
  void chip_pre_init(void)
  {
  	chip_periph_sleep_control();
  
  	if (IS_ENABLED(CONFIG_MCHP_TFDP)) {
  		/* MCHP Enable TFDP for fast debug messages */
  		tfdp_power(1);
  		tfdp_enable(1, 1);
  		CPRINTS("chip_pre_init: Image type = 0x%02x",
  			MCHP_VBAT_RAM(MCHP_IMAGETYPE_IDX));
  	}
  }
  #endif
  
  void system_pre_init(void)
  {
  	/*
  	 * Make sure AHB Error capture is enabled.
  	 * Signals bus fault to Cortex-M4 core if an address presented
  	 * to AHB is not claimed by any HW block.
  	 */
  	MCHP_EC_AHB_ERR = 0;	/* write any value to clear */
  	MCHP_EC_AHB_ERR_EN = 0; /* enable capture of address on error */
  
  	/* Manual voltage selection only required for MEC170x and MEC152x */
  	if (IS_ENABLED(CONFIG_HOSTCMD_ESPI))
  		vtr3_voltage_select(1);
  	else
  		vtr3_voltage_select(0);
  
  	if (!IS_ENABLED(CONFIG_CHIP_PRE_INIT))
  		chip_periph_sleep_control();
  
  	/* Enable direct NVIC */
  	MCHP_EC_INT_CTRL |= 1;
  
  	/* Disable ARM TRACE debug port */
  	MCHP_EC_TRACE_EN &= ~1;
  
  	/*
  	 * Enable aggregated only interrupt GIRQ's
  	 * Make sure direct mode interrupt sources aggregated outputs
  	 * are not enabled.
  	 * Aggregated only GIRQ's 8,9,10,11,12,22,24,25,26
  	 * Direct GIRQ's = 13,14,15,16,17,18,19,21,23
  	 * These bits only need to be touched again on RESET_SYS.
  	 * NOTE: GIRQ22 wake for AHB peripherals not processor.
  	 */
  	MCHP_INT_BLK_DIS = 0xfffffffful;
  	MCHP_INT_BLK_EN = MCHP_INT_AGGR_ONLY_BITMAP;
  
  	spi_enable(SPI_FLASH_DEVICE, 1);
  }
  
  uint32_t chip_read_reset_flags(void)
  {
  	return MCHP_VBAT_RAM(HIBDATA_INDEX_SAVED_RESET_FLAGS);
  }
  
  void chip_save_reset_flags(uint32_t flags)
  {
  	MCHP_VBAT_RAM(HIBDATA_INDEX_SAVED_RESET_FLAGS) = flags;
  }
  
  noreturn void _system_reset(int flags, int wake_from_hibernate)
  {
  	uint32_t save_flags = 0;
  
  	/* DEBUG */
  	CPRINTS("MEC system reset: flag = 0x%08x wake = %d", flags,
  		wake_from_hibernate);
  
  	/* Disable interrupts to avoid task swaps during reboot */
  	interrupt_disable();
  
  	/* Save current reset reasons if necessary */
  	if (flags & SYSTEM_RESET_PRESERVE_FLAGS)
  		save_flags = system_get_reset_flags() | EC_RESET_FLAG_PRESERVED;
  
  	if (flags & SYSTEM_RESET_LEAVE_AP_OFF)
  		save_flags |= EC_RESET_FLAG_AP_OFF;
  
  	if (wake_from_hibernate)
  		save_flags |= EC_RESET_FLAG_HIBERNATE;
  	else if (flags & SYSTEM_RESET_HARD)
  		save_flags |= EC_RESET_FLAG_HARD;
  	else
  		save_flags |= EC_RESET_FLAG_SOFT;
  
  	chip_save_reset_flags(save_flags);
  
  	/*
  	 * Trigger chip reset
  	 */
  	if (!IS_ENABLED(CONFIG_DEBUG_BRINGUP))
  		MCHP_PCR_SYS_RST |= MCHP_PCR_SYS_SOFT_RESET;
  
  	/* Spin and wait for reboot; should never return */
  	while (1)
  		;
  }
  
  void system_reset(int flags)
  {
  	_system_reset(flags, 0);
  }
  
  const char *system_get_chip_vendor(void)
  {
  	return "mchp";
  }
  
  #ifdef CHIP_VARIANT_MEC1701
  /*
   * MEC1701H Chip ID = 0x2D
   *              Rev = 0x82
   */
  const char *system_get_chip_name(void)
  {
  	switch (MCHP_CHIP_DEV_ID) {
  	case 0x2D:
  		return "mec1701";
  	default:
  		return "unknown";
  	}
  }
  #endif
  
  #ifdef CHIP_FAMILY_MEC152X
  /*
   * MEC152x family implements chip ID as a 32-bit
   * register where:
   * b[31:16] = 16-bit Device ID
   * b[15:8] = 8-bit Sub ID
   * b[7:0] = Revision
   *
   * MEC1521-128 WFBGA 0023_33_xxh
   * MEC1521-144 WFBGA 0023_34_xxh
   * MEC1523-144 WFBGA 0023_B4_xxh
   * MEC1527-144 WFBGA 0023_74_xxh
   * MEC1527-128 WFBGA 0023_73_xxh
   */
  const char *system_get_chip_name(void)
  {
  	switch (MCHP_CHIP_DEVRID32 & ~(MCHP_CHIP_REV_MASK)) {
  	case 0x00201400: /* 144 pin rev A? */
  		return "mec1503_revA";
  	case 0x00203400: /* 144 pin */
  		return "mec1501";
  	case 0x00207400: /* 144 pin */
  		return "mec1507";
  	case 0x00208400: /* 144 pin */
  		return "mec1503";
  	case 0x00233300: /* 128 pin */
  	case 0x00233400: /* 144 pin */
  		return "mec1521";
  	case 0x0023B400: /* 144 pin */
  		return "mec1523";
  	case 0x00237300: /* 128 pin */
  	case 0x00237400: /* 144 pin */
  		return "mec1527";
  	default:
  		return "unknown";
  	}
  }
  #endif
  
  #ifdef CHIP_FAMILY_MEC172X
  /*
   * MEC172x family implements chip ID as a 32-bit
   * register where:
   * b[31:16] = 16-bit Device ID
   * b[15:8] = 8-bit Sub ID
   * b[7:0] = Revision
   *
   * MEC1723N-B0-I/SZ 144 pin: 0x0022_34_xx
   * MEC1727N-B0-I/SZ 144 pin: 0x0022_74_xx
   * MEC1721N-B0-I/LJ 176 pin: 0x0022_27_xx
   * MEC1723N-B0-I/LJ 176 pin: 0x0022_37_xx
   * MEC1727N-B0-I/LJ 176 pin: 0x0022_77_xx
   */
  const char *system_get_chip_name(void)
  {
  	switch (MCHP_CHIP_DEVRID32 & ~(MCHP_CHIP_REV_MASK)) {
  	case 0x00223400:
  		return "MEC1723NSZ";
  	case 0x00227400:
  		return "MEC1727NSZ";
  	case 0x00222700:
  		return "MEC1721NLJ";
  	case 0x00223700:
  		return "MEC1723NLJ";
  	case 0x00227700:
  		return "MEC1727NLJ";
  	default:
  		return "unknown";
  	}
  }
  #endif
  
  static char to_hex(int x)
  {
  	if (x >= 0 && x <= 9)
  		return '0' + x;
  	return 'a' + x - 10;
  }
  
  const char *system_get_chip_revision(void)
  {
  	static char buf[3];
  	uint8_t rev = MCHP_CHIP_DEV_REV;
  
  	buf[0] = to_hex(rev / 16);
  	buf[1] = to_hex(rev & 0xf);
  	buf[2] = '\0';
  	return buf;
  }
  
  static int bbram_idx_lookup(enum system_bbram_idx idx)
  {
  	switch (idx) {
  	case SYSTEM_BBRAM_IDX_PD0:
  		return HIBDATA_INDEX_PD0;
  	case SYSTEM_BBRAM_IDX_PD1:
  		return HIBDATA_INDEX_PD1;
  	case SYSTEM_BBRAM_IDX_PD2:
  		return HIBDATA_INDEX_PD2;
  	default:
  		return 1;
  	}
  }
  
  int system_get_bbram(enum system_bbram_idx idx, uint8_t *value)
  {
  	int hibdata = bbram_idx_lookup(idx);
  
  	if (hibdata < 0)
  		return EC_ERROR_UNIMPLEMENTED;
  
  	*value = MCHP_VBAT_RAM(hibdata);
  	return EC_SUCCESS;
  }
  
  int system_set_bbram(enum system_bbram_idx idx, uint8_t value)
  {
  	int hibdata = bbram_idx_lookup(idx);
  
  	if (hibdata < 0)
  		return EC_ERROR_UNIMPLEMENTED;
  
  	MCHP_VBAT_RAM(hibdata) = value;
  	return EC_SUCCESS;
  }
  
  int system_set_scratchpad(uint32_t value)
  {
  	MCHP_VBAT_RAM(HIBDATA_INDEX_SCRATCHPAD) = value;
  	return EC_SUCCESS;
  }
  
  int system_get_scratchpad(uint32_t *value)
  {
  	*value = MCHP_VBAT_RAM(HIBDATA_INDEX_SCRATCHPAD);
  	return EC_SUCCESS;
  }
  
  /*
   * Local function to disable clocks in the chip's host interface
   * so the chip can enter deep sleep. Only MEC170X has LPC.
   * MEC152x and MEC172x only include eSPI and SPI host interfaces.
   * NOTE: we do it this way because the LPC registers are only
   * defined for MEC170x and the IS_ENABLED() macro causes the
   * compiler to evaluate both true and false code paths.
   */
  #if defined(CONFIG_HOSTCMD_ESPI)
  static void disable_host_ifc_clocks(void)
  {
  	MCHP_ESPI_ACTIVATE &= ~0x01;
  }
  #else
  static void disable_host_ifc_clocks(void)
  {
  	#ifdef CHIP_FAMILY_MEC170X
  	MCHP_LPC_ACT &= ~0x1;
  	#endif
  }
  #endif
  
  
  /*
   * Called when hibernation timer is not used in deep sleep.
   * Switch 32 KHz clock logic from external 32KHz input to
   * internal silicon OSC.
   * NOTE: MEC172x auto-switches from external source to silicon
   * oscillator.
   */
  #ifdef CHIP_FAMILY_MEC172X
  static void switch_32k_pin2sil(void) {}
  #else
  static void switch_32k_pin2sil(void)
  {
  	MCHP_VBAT_CE &= ~(MCHP_VBAT_CE_32K_DOMAIN_32KHZ_IN_PIN);
  }
  #endif
  
  void system_hibernate(uint32_t seconds, uint32_t microseconds)
  {
  	int i;
  
  	if (IS_ENABLED(CONFIG_HOSTCMD_PD)) {
  		/* Inform the PD MCU that we are going to hibernate. */
  		host_command_pd_request_hibernate();
  		/* Wait to ensure exchange with PD before hibernating. */
  		msleep(100);
  	}
  
  	cflush();
  
  	if (board_hibernate)
  		board_hibernate();
  
  	/* Disable interrupts */
  	interrupt_disable();
  	for (i = 0; i < MCHP_IRQ_MAX; ++i) {
  		task_disable_irq(i);
  		task_clear_pending_irq(i);
  	}
  
  	for (i = MCHP_INT_GIRQ_FIRST; i <= MCHP_INT_GIRQ_LAST; ++i) {
  		MCHP_INT_DISABLE(i) = 0xffffffff;
  		MCHP_INT_SOURCE(i)  = 0xffffffff;
  	}
  
  	/* Disable UART */
  	MCHP_UART_ACT(0) &= ~0x1;
  
  	disable_host_ifc_clocks();
  
  	/* Disable JTAG */
  	MCHP_EC_JTAG_EN &= ~1;
  
  	/* Stop watchdog */
  	MCHP_WDG_CTL &= ~(MCHP_WDT_CTL_ENABLE);
  
  	/* Stop timers */
  	MCHP_TMR32_CTL(0) &= ~1;
  	MCHP_TMR32_CTL(1) &= ~1;
  	for (i = 0; i < MCHP_TMR16_INSTANCES; i++)
  		MCHP_TMR16_CTL(i) &= ~1;
  
  	/* Power down ADC */
  	/*
  	 * If ADC is in middle of acquisition it will continue until finished
  	 */
  	MCHP_ADC_CTRL &= ~1;
  
  	/* Disable blocks */
  	MCHP_PCR_SLOW_CLK_CTL &= ~(MCHP_PCR_SLOW_CLK_CTL_MASK);
  
  	/* Setup GPIOs for hibernate */
  	if (board_hibernate_late)
  		board_hibernate_late();
  
  	if (hibernate_wake_pins_used > 0) {
  		for (i = 0; i < hibernate_wake_pins_used; ++i) {
  			const enum gpio_signal pin = hibernate_wake_pins[i];
  
  			gpio_reset(pin);
  			gpio_enable_interrupt(pin);
  		}
  
  		interrupt_enable();
  		task_enable_irq(MCHP_IRQ_GIRQ8);
  		task_enable_irq(MCHP_IRQ_GIRQ9);
  		task_enable_irq(MCHP_IRQ_GIRQ10);
  		task_enable_irq(MCHP_IRQ_GIRQ11);
  		task_enable_irq(MCHP_IRQ_GIRQ12);
  		task_enable_irq(MCHP_IRQ_GIRQ26);
  	}
  
  	if (seconds || microseconds) {
  		htimer_init();
  		system_set_htimer_alarm(seconds, microseconds);
  		interrupt_enable();
  	} else
  		switch_32k_pin2sil();
  
  	/*
  	 * Set sleep state
  	 * arm sleep state to trigger on next WFI
  	 */
  	CPU_SCB_SYSCTRL |= 0x4;
  	MCHP_PCR_SYS_SLP_CTL = MCHP_PCR_SYS_SLP_HEAVY;
  	MCHP_PCR_SYS_SLP_CTL = MCHP_PCR_SYS_SLP_ALL;
  
  	asm("dsb");
  	asm("wfi");
  	asm("isb");
  	asm("nop");
  
  	/* Use fastest clock to speed through wake-up */
  	MCHP_PCR_PROC_CLK_CTL = MCHP_PCR_CLK_CTL_FASTEST;
  
  	/* Reboot */
  	_system_reset(0, 1);
  
  	/* We should never get here. */
  	while (1)
  		;
  }
  
  void htimer_interrupt(void)
  {
  	/* Time to wake up */
  }
  DECLARE_IRQ(MCHP_IRQ_HTIMER0, htimer_interrupt, 1);
  
  enum ec_image system_get_shrspi_image_copy(void)
  {
  	return MCHP_VBAT_RAM(MCHP_IMAGETYPE_IDX);
  }
  
  uint32_t system_get_lfw_address(void)
  {
  	uint32_t * const lfw_vector =
  		(uint32_t * const)CONFIG_PROGRAM_MEMORY_BASE;
  
  	return *(lfw_vector + 1);
  }
  
  void system_set_image_copy(enum ec_image copy)
  {
  	MCHP_VBAT_RAM(MCHP_IMAGETYPE_IDX) = (copy == EC_IMAGE_RW) ?
  				EC_IMAGE_RW : EC_IMAGE_RO;
  }
  