/* 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 #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; }