/* Copyright 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. */ #include "board_id.h" #include "ccd_config.h" #include "clock.h" #include "closed_source_set1.h" #include "common.h" #include "console.h" #include "dcrypto/dcrypto.h" #include "ec_version.h" #include "endian.h" #include "extension.h" #include "flash.h" #include "flash_config.h" #include "gpio.h" #include "ite_sync.h" #include "hooks.h" #include "i2c.h" #include "i2cs.h" #include "init_chip.h" #include "nvmem.h" #include "nvmem_vars.h" #include "rbox.h" #include "rdd.h" #include "recovery_button.h" #include "registers.h" #include "scratch_reg1.h" #include "signed_header.h" #include "spi.h" #include "system.h" #include "system_chip.h" #include "task.h" #include "tpm_registers.h" #include "trng.h" #include "uart_bitbang.h" #include "uartn.h" #include "usart.h" #include "usb_descriptor.h" #include "usb_hid.h" #include "usb_i2c.h" #include "usb_spi.h" #include "util.h" #include "wp.h" /* Define interrupt and gpio structs */ #include "gpio_list.h" #include "cryptoc/sha.h" /* * Need to include Implementation.h here to make sure that NVRAM size * definitions match across different git repos. * * MAX() definition from include/utils.h does not work in Implementation.h, as * it is used in a preprocessor expression there; * * SHA_DIGEST_SIZE is defined to be the same in both git repos, but using * different expressions. * * To untangle compiler errors let's just undefine MAX() and SHA_DIGEST_SIZE * here, as nether is necessary in this case: all we want from * Implementation.h at this point is the definition for NV_MEMORY_SIZE. */ #undef MAX #undef SHA_DIGEST_SIZE #include "Implementation.h" #define CPRINTS(format, args...) cprints(CC_SYSTEM, format, ## args) #define NVMEM_TPM_SIZE ((sizeof((struct nvmem_partition *)0)->buffer) \ - NVMEM_CR50_SIZE) /* * Make sure NV memory size definition in Implementation.h matches reality. It * should be set to * * NVMEM_PARTITION_SIZE - NVMEM_CR50_SIZE - 8 * * Both of these macros are defined in board.h. */ BUILD_ASSERT(NVMEM_TPM_SIZE == NV_MEMORY_SIZE); /* NvMem user buffer lengths table */ uint32_t nvmem_user_sizes[NVMEM_NUM_USERS] = { NVMEM_TPM_SIZE, NVMEM_CR50_SIZE }; /* Board specific configuration settings */ static uint32_t board_properties; /* Mainly used as a cache for strap config. */ static uint8_t reboot_request_posted; /* Which UARTs we'd like to be able to bitbang. */ struct uart_bitbang_properties bitbang_config = { .uart = UART_EC, .tx_gpio = GPIO_DETECT_SERVO, /* This is TX to EC console. */ .rx_gpio = GPIO_EC_TX_CR50_RX, .rx_irq = GC_IRQNUM_GPIO1_GPIO11INT, /* Must match gpoi.inc */ /* * The rx/tx_pinmux_regval values MUST agree with the pin config for * both the TX and RX GPIOs in gpio.inc. Don't change one without * changing the other. */ .tx_pinmux_reg = GBASE(PINMUX) + GOFFSET(PINMUX, DIOB5_SEL), .tx_pinmux_regval = GC_PINMUX_GPIO1_GPIO3_SEL, .rx_pinmux_reg = GBASE(PINMUX) + GOFFSET(PINMUX, DIOB6_SEL), .rx_pinmux_regval = GC_PINMUX_GPIO1_GPIO11_SEL, }; DECLARE_IRQ(GC_IRQNUM_GPIO1_GPIO11INT, uart_bitbang_irq, 0); const char *device_state_names[] = { "init", "init_debouncing", "init_rx_only", "disconnected", "off", "undetectable", "connected", "on", "debouncing", "unknown", "ignored" }; BUILD_ASSERT(ARRAY_SIZE(device_state_names) == DEVICE_STATE_COUNT); const char *device_state_name(enum device_state state) { if (state >= 0 && state < DEVICE_STATE_COUNT) return device_state_names[state]; else return "?"; } int board_use_plt_rst(void) { return !!(board_properties & BOARD_USE_PLT_RESET); } /* Allow enabling deep sleep if the board supports it. */ int board_deep_sleep_allowed(void) { return !(board_properties & BOARD_DEEP_SLEEP_DISABLED); } int board_rst_pullup_needed(void) { return !!(board_properties & BOARD_NEEDS_SYS_RST_PULL_UP); } int board_tpm_uses_i2c(void) { return !!(board_properties & BOARD_SLAVE_CONFIG_I2C); } int board_tpm_uses_spi(void) { return !!(board_properties & BOARD_SLAVE_CONFIG_SPI); } int board_uses_closed_source_set1(void) { return !!(board_properties & BOARD_CLOSED_SOURCE_SET1); } int board_uses_closed_loop_reset(void) { return !!(board_properties & BOARD_CLOSED_LOOP_RESET); } int board_has_ina_support(void) { return !(board_properties & BOARD_NO_INA_SUPPORT); } int board_tpm_mode_change_allowed(void) { return !!(board_properties & BOARD_ALLOW_CHANGE_TPM_MODE); } /* Get header address of the backup RW copy. */ const struct SignedHeader *get_other_rw_addr(void) { if (system_get_image_copy() == SYSTEM_IMAGE_RW) return (const struct SignedHeader *) get_program_memory_addr(SYSTEM_IMAGE_RW_B); return (const struct SignedHeader *) get_program_memory_addr(SYSTEM_IMAGE_RW); } /* Return true if the other RW is not ready to run. */ static int other_rw_is_inactive(void) { const struct SignedHeader *header = get_other_rw_addr(); return !!(header->image_size & TOP_IMAGE_SIZE_BIT); } /* I2C Port definition */ const struct i2c_port_t i2c_ports[] = { {"master", I2C_PORT_MASTER, 100, GPIO_I2C_SCL_INA, GPIO_I2C_SDA_INA}, }; const unsigned int i2c_ports_used = ARRAY_SIZE(i2c_ports); /* Strapping pin info structure */ #define STRAP_PIN_DELAY_USEC 100 enum strap_list { a0, a1, b0, b1, }; struct strap_desc { /* GPIO enum from gpio.inc for the strap pin */ uint8_t gpio_signal; /* Offset into pinmux register section for pad SEL register */ uint8_t sel_offset; /* Entry in the pinmux peripheral selector table for pad */ uint8_t pad_select; const char *pad_name; }; struct board_cfg { /* Value the strap pins should read for a given board */ uint8_t strap_cfg; /* Properties required for a given board */ uint32_t board_properties; }; /* * This table contains both the GPIO and pad specific information required to * configure each strapping pin to be either a GPIO input or output. */ const struct strap_desc strap_regs[] = { {GPIO_STRAP_A0, GOFFSET(PINMUX, DIOA1_SEL), GC_PINMUX_DIOA1_SEL, "a1"}, {GPIO_STRAP_A1, GOFFSET(PINMUX, DIOA9_SEL), GC_PINMUX_DIOA9_SEL, "a9"}, {GPIO_STRAP_B0, GOFFSET(PINMUX, DIOA6_SEL), GC_PINMUX_DIOA6_SEL, "a6"}, {GPIO_STRAP_B1, GOFFSET(PINMUX, DIOA12_SEL), GC_PINMUX_DIOA12_SEL, "a12"}, }; #define BOARD_PROPERTIES_DEFAULT (BOARD_SLAVE_CONFIG_I2C | BOARD_USE_PLT_RESET) static struct board_cfg board_cfg_table[] = { /* SPI Variants: DIOA12 = 1M PD, DIOA6 = 1M PD */ /* Kevin/Gru: DI0A9 = 5k PD, DIOA1 = 1M PU */ { .strap_cfg = 0x02, .board_properties = BOARD_SLAVE_CONFIG_SPI | BOARD_NEEDS_SYS_RST_PULL_UP, }, /* Poppy: DI0A9 = 1M PU, DIOA1 = 1M PU */ { .strap_cfg = 0x0A, .board_properties = BOARD_SLAVE_CONFIG_SPI | BOARD_USE_PLT_RESET, }, /* Mistral: DI0A9 = 1M PU, DIOA1 = 5k PU */ { .strap_cfg = 0x0B, .board_properties = BOARD_SLAVE_CONFIG_SPI | BOARD_USE_PLT_RESET | BOARD_NO_INA_SUPPORT | BOARD_CLOSED_LOOP_RESET, }, /* Kukui: DI0A9 = 5k PU, DIOA1 = 5k PU */ { .strap_cfg = 0x0F, .board_properties = BOARD_SLAVE_CONFIG_SPI | BOARD_USE_PLT_RESET, }, /* I2C Variants: DIOA9 = 1M PD, DIOA1 = 1M PD */ /* Reef/Eve: DIOA12 = 5k PD, DIOA6 = 1M PU */ { .strap_cfg = 0x20, .board_properties = BOARD_SLAVE_CONFIG_I2C | BOARD_USE_PLT_RESET, }, /* Rowan: DIOA12 = 5k PD, DIOA6 = 5k PU */ { .strap_cfg = 0x30, .board_properties = BOARD_SLAVE_CONFIG_I2C | BOARD_DEEP_SLEEP_DISABLED | BOARD_DETECT_AP_WITH_UART, }, /* Sarien/Arcada: DIOA12 = 1M PD, DIOA6 = 5k PU */ { .strap_cfg = 0x70, .board_properties = BOARD_SLAVE_CONFIG_I2C | BOARD_USE_PLT_RESET | BOARD_WP_DISABLE_DELAY | BOARD_CLOSED_SOURCE_SET1 | BOARD_NO_INA_SUPPORT | BOARD_ALLOW_CHANGE_TPM_MODE, }, }; void post_reboot_request(void) { /* Reboot the device next time TPM reset is requested. */ reboot_request_posted = 1; } /*****************************************************************************/ /* */ /* * Battery cutoff monitor is needed on the devices where hardware alone does * not provide proper battery cutoff functionality. * * The sequence is as follows: set up an interrupt to react to the charger * disconnect event. When the interrupt happens observe status of the buttons * connected to PWRB_IN and KEY0_IN. * * If both are pressed, start the 5 second timeout, while keeping monitoring * the charger connection state. If it remains disconnected for the entire * duration - generate 5 second pulses on EC_RST_L and BAT_EN outputs. * * In reality the BAT_EN output pulse will cause the complete power cut off, * so strictly speaking the code does not need to do anything once BAT_EN * output is deasserted. */ /* Time to wait before initiating battery cutoff procedure. */ #define CUTOFF_TIMEOUT_US (5 * SECOND) /* A timeout hook to run in the end of the 5 s interval. */ static void ac_stayed_disconnected(void) { uint32_t saved_override_state; CPRINTS("%s", __func__); /* assert EC_RST_L and deassert BAT_EN */ GREG32(RBOX, ASSERT_EC_RST) = 1; /* * BAT_EN needs to use the RBOX override ability, bit 1 is battery * disable bit. */ saved_override_state = GREG32(RBOX, OVERRIDE_OUTPUT); GWRITE_FIELD(RBOX, OVERRIDE_OUTPUT, VAL, 0); /* Setting it to zero. */ GWRITE_FIELD(RBOX, OVERRIDE_OUTPUT, OEN, 1); GWRITE_FIELD(RBOX, OVERRIDE_OUTPUT, EN, 1); msleep(5000); /* * The system was supposed to be shut down the moment battery * disconnect was asserted, but if we made it here we might as well * restore the original state. */ GREG32(RBOX, OVERRIDE_OUTPUT) = saved_override_state; GREG32(RBOX, ASSERT_EC_RST) = 0; } DECLARE_DEFERRED(ac_stayed_disconnected); /* * Just a shortcut to make use of these AC power interrupt states better * readable. RED means rising edge and FED means falling edge. */ enum { ac_pres_red = GC_RBOX_INT_STATE_INTR_AC_PRESENT_RED_MASK, ac_pres_fed = GC_RBOX_INT_STATE_INTR_AC_PRESENT_FED_MASK, buttons_not_pressed = GC_RBOX_CHECK_INPUT_KEY0_IN_MASK | GC_RBOX_CHECK_INPUT_PWRB_IN_MASK }; /* * ISR reacting to both falling and raising edges of the AC_PRESENT signal. * Falling edge indicates AC no longer present (removal of the charger cable) * and rising edge indicates AP present (insertion of charger cable). */ static void ac_power_state_changed(void) { uint32_t req; /* Get current status and clear it. */ req = GREG32(RBOX, INT_STATE) & (ac_pres_red | ac_pres_fed); GREG32(RBOX, INT_STATE) = req; CPRINTS("AC: %c%c", req & ac_pres_red ? 'R' : '-', req & ac_pres_fed ? 'F' : '-'); /* Delay sleep so RDD state machines can stabilize */ delay_sleep_by(5 * SECOND); /* The remaining code is only used for battery cutoff */ if (!system_battery_cutoff_support_required()) return; /* Raising edge gets priority, stop timeout timer and go. */ if (req & ac_pres_red) { hook_call_deferred(&ac_stayed_disconnected_data, -1); return; } /* * If this is not a falling edge, or either of the buttons is not * pressed - bail out. */ if (!(req & ac_pres_fed) || (GREG32(RBOX, CHECK_INPUT) & buttons_not_pressed)) return; /* * Charger cable was yanked while the power and key0 buttons were kept * pressed - user wants a battery cut off. */ hook_call_deferred(&ac_stayed_disconnected_data, CUTOFF_TIMEOUT_US); } DECLARE_IRQ(GC_IRQNUM_RBOX0_INTR_AC_PRESENT_RED_INT, ac_power_state_changed, 1); DECLARE_IRQ(GC_IRQNUM_RBOX0_INTR_AC_PRESENT_FED_INT, ac_power_state_changed, 1); /* Enable interrupts on plugging in and yanking out of the charger cable. */ static void init_ac_detect(void) { /* It is set in idle.c also. */ GWRITE_FIELD(RBOX, WAKEUP, ENABLE, 1); GWRITE_FIELD(RBOX, INT_ENABLE, INTR_AC_PRESENT_RED, 1); GWRITE_FIELD(RBOX, INT_ENABLE, INTR_AC_PRESENT_FED, 1); task_enable_irq(GC_IRQNUM_RBOX0_INTR_AC_PRESENT_RED_INT); task_enable_irq(GC_IRQNUM_RBOX0_INTR_AC_PRESENT_FED_INT); } /* */ /*****************************************************************************/ /* * There's no way to trigger on both rising and falling edges, so force a * compiler error if we try. The workaround is to use the pinmux to connect * two GPIOs to the same input and configure each one for a separate edge. */ #define GPIO_INT(name, pin, flags, signal) \ BUILD_ASSERT(((flags) & GPIO_INT_BOTH) != GPIO_INT_BOTH); #include "gpio.wrap" /** * Reset wake logic * * If any wake pins are edge triggered, the pad logic latches the wakeup. Clear * and restore EXITEN0 to reset the wakeup logic. */ static void reset_wake_logic(void) { uint32_t exiten = GREG32(PINMUX, EXITEN0); GREG32(PINMUX, EXITEN0) = 0; GREG32(PINMUX, EXITEN0) = exiten; } static void init_pmu(void) { clock_enable_module(MODULE_PMU, 1); /* This boot sequence may be a result of previous soft reset, * in which case the PMU low power sequence register needs to * be reset. */ GREG32(PMU, LOW_POWER_DIS) = 0; /* Enable wakeup interrupt */ task_enable_irq(GC_IRQNUM_PMU_INTR_WAKEUP_INT); GWRITE_FIELD(PMU, INT_ENABLE, INTR_WAKEUP, 1); } void pmu_wakeup_interrupt(void) { int wakeup_src; static uint8_t count; static uint8_t ws; static uint8_t line_length; static const char wheel[] = { '|', '/', '-', '\\' }; delay_sleep_by(1 * MSEC); wakeup_src = GR_PMU_EXITPD_SRC; /* Clear interrupt state */ GWRITE_FIELD(PMU, INT_STATE, INTR_WAKEUP, 1); /* Clear pmu reset */ GWRITE(PMU, CLRRST, 1); /* * This will print the next state of the "rotating wheel" every time * cr50 resumes from regular sleep (8 is the ASCII code for * 'backspace'). Each time wake source changes, its hex value is * printed out preceded by a space. * * In steady state when there is no other activity Cr50 wakes up every * half second for HOOK_TICK, so that is the rate the wheel will be * spinning at when device is idle. */ if (ws == wakeup_src) { ccprintf("%c%c%c%2x%c", 8, 8, 8, ws, wheel[count++ % sizeof(wheel)]); } else { ws = wakeup_src; line_length += 3; if (line_length > 50) { ccprintf("\n"); line_length = 0; } ccprintf(" %2x ", wakeup_src); } if (wakeup_src & GC_PMU_EXITPD_SRC_RBOX_WAKEUP_MASK) rbox_clear_wakeup(); /* Disable rbox wakeup. It will be reenabled before entering sleep. */ GREG32(RBOX, WAKEUP) = 0; if (wakeup_src & GC_PMU_EXITPD_SRC_PIN_PD_EXIT_MASK) { reset_wake_logic(); /* * Delay sleep long enough for a SPI slave transaction to start * or for the system to be reset. */ delay_sleep_by(5 * SECOND); } /* Trigger timer0 interrupt */ if (wakeup_src & GC_PMU_EXITPD_SRC_TIMELS0_PD_EXIT_TIMER0_MASK) task_trigger_irq(GC_IRQNUM_TIMELS0_TIMINT0); /* Trigger timer1 interrupt */ if (wakeup_src & GC_PMU_EXITPD_SRC_TIMELS0_PD_EXIT_TIMER1_MASK) task_trigger_irq(GC_IRQNUM_TIMELS0_TIMINT1); } DECLARE_IRQ(GC_IRQNUM_PMU_INTR_WAKEUP_INT, pmu_wakeup_interrupt, 1); void board_configure_deep_sleep_wakepins(void) { /* * Disable the i2c and spi slave wake sources since the TPM is * not being used and reenable them in their init functions on * resume. */ GWRITE_FIELD(PINMUX, EXITEN0, DIOA12, 0); /* SPS_CS_L */ GWRITE_FIELD(PINMUX, EXITEN0, DIOA1, 0); /* I2CS_SDA */ GWRITE_FIELD(PINMUX, EXITEN0, DIOA9, 0); /* I2CS_SCL */ /* Remove the pulldown on EC uart tx and disable the input */ GWRITE_FIELD(PINMUX, DIOB5_CTL, PD, 0); GWRITE_FIELD(PINMUX, DIOB5_CTL, IE, 0); /* * Configure the TPM_RST_L signal as wake on high. There is a * requirement the tpm reset has to remain asserted when cr50 should * be in deep sleep, so cr50 should not wake up until it goes high. * * Whether it is a short pulse or long one waking on the high level is * fine, because the goal of TPM_RST_L is to reset the TPM and after * resuming from deep sleep the TPM will be reset. Cr50 doesn't need to * read the low value and then reset. */ if (board_use_plt_rst()) { /* Configure plt_rst_l to wake on high */ /* Disable plt_rst_l as a wake pin */ GWRITE_FIELD(PINMUX, EXITEN0, DIOM3, 0); /* Reconfigure the pin */ GWRITE_FIELD(PINMUX, EXITEDGE0, DIOM3, 0); /* level sensitive */ GWRITE_FIELD(PINMUX, EXITINV0, DIOM3, 0); /* wake on high */ /* enable powerdown exit */ GWRITE_FIELD(PINMUX, EXITEN0, DIOM3, 1); } else { /* Configure plt_rst_l to wake on high */ /* Disable sys_rst_l as a wake pin */ GWRITE_FIELD(PINMUX, EXITEN0, DIOM0, 0); /* Reconfigure the pin */ GWRITE_FIELD(PINMUX, EXITEDGE0, DIOM0, 0); /* level sensitive */ GWRITE_FIELD(PINMUX, EXITINV0, DIOM0, 0); /* wake on high */ /* enable powerdown exit */ GWRITE_FIELD(PINMUX, EXITEN0, DIOM0, 1); } } static void deferred_tpm_rst_isr(void); DECLARE_DEFERRED(deferred_tpm_rst_isr); static void configure_board_specific_gpios(void) { /* Add a pullup to sys_rst_l */ if (board_rst_pullup_needed()) GWRITE_FIELD(PINMUX, DIOM0_CTL, PU, 1); /* * Connect either plt_rst_l or sys_rst_l to GPIO_TPM_RST_L based on the * board type. This signal is used to monitor AP resets and reset the * TPM. * * Also configure these pins to be wake triggers on the rising edge, * this will apply to regular sleep only, entering deep sleep would * reconfigure this. * * plt_rst_l is on diom3, and sys_rst_l is on diom0. */ if (board_use_plt_rst()) { /* Use plt_rst_l as the tpm reset signal. */ /* Select for TPM_RST_L */ GWRITE(PINMUX, GPIO1_GPIO0_SEL, GC_PINMUX_DIOM3_SEL); /* Select for DETECT_TPM_RST_L_ASSERTED */ GWRITE(PINMUX, GPIO1_GPIO4_SEL, GC_PINMUX_DIOM3_SEL); /* Enable the input */ GWRITE_FIELD(PINMUX, DIOM3_CTL, IE, 1); /* * Make plt_rst_l routed to DIOM3 a low level sensitive wake * source. This way when a plt_rst_l pulse comes along while * H1 is in sleep, the H1 wakes from sleep first, enabling all * necessary clocks, and becomes ready to generate an * interrupt on the rising edge of plt_rst_l. * * It takes at most 150 us to wake up, and the pulse is at * least 1ms long. */ GWRITE_FIELD(PINMUX, EXITEDGE0, DIOM3, 0); GWRITE_FIELD(PINMUX, EXITINV0, DIOM3, 1); /* Enable powerdown exit on DIOM3 */ GWRITE_FIELD(PINMUX, EXITEN0, DIOM3, 1); } else { /* Use sys_rst_l as the tpm reset signal. */ /* Select for TPM_RST_L */ GWRITE(PINMUX, GPIO1_GPIO0_SEL, GC_PINMUX_DIOM0_SEL); /* Select for DETECT_TPM_RST_L_ASSERTED */ GWRITE(PINMUX, GPIO1_GPIO4_SEL, GC_PINMUX_DIOM0_SEL); /* Enable the input */ GWRITE_FIELD(PINMUX, DIOM0_CTL, IE, 1); /* Set to be level sensitive */ GWRITE_FIELD(PINMUX, EXITEDGE0, DIOM0, 0); /* wake on low */ GWRITE_FIELD(PINMUX, EXITINV0, DIOM0, 1); /* Enable powerdown exit on DIOM0 */ GWRITE_FIELD(PINMUX, EXITEN0, DIOM0, 1); } if (board_uses_closed_source_set1()) closed_source_set1_configure_gpios(); } static uint8_t mismatched_board_id; int board_id_is_mismatched(void) { return !!mismatched_board_id; } static void check_board_id_mismatch(void) { if (!board_id_mismatch(NULL)) return; if (system_rollback_detected()) { /* * We are in a rollback, the other image must be no good. * Let's keep going with the TPM disabled, only updates will * be allowed. */ mismatched_board_id = 1; ccprintf("Board ID mismatched, but can not reboot.\n"); /* Force CCD disabled */ ccd_disable(); return; } system_ensure_rollback(); ccprintf("Rebooting due to board ID mismatch\n"); cflush(); system_reset(0); } /* * Check if ITE SYNC sequence generation was requested before the reset, if so * - clear the request and call the function to generate the sequence. */ static void maybe_trigger_ite_sync(void) { uint32_t lls1; lls1 = GREG32(PMU, LONG_LIFE_SCRATCH1); if (!(lls1 & BOARD_ITE_EC_SYNC_NEEDED)) return; /* Clear the sync required bit, this should work only once. */ GWRITE_FIELD(PMU, LONG_LIFE_SCRATCH_WR_EN, REG1, 1); GREG32(PMU, LONG_LIFE_SCRATCH1) = lls1 & ~BOARD_ITE_EC_SYNC_NEEDED; GWRITE_FIELD(PMU, LONG_LIFE_SCRATCH_WR_EN, REG1, 0); generate_ite_sync(); } /* Initialize board. */ static void board_init(void) { #ifdef CR50_DEV static enum ccd_state ccd_init_state = CCD_STATE_OPENED; #else static enum ccd_state ccd_init_state = CCD_STATE_LOCKED; #endif /* * Deep sleep resets should be considered valid and should not impact * the rolling reboot count. */ if (system_get_reset_flags() & EC_RESET_FLAG_HIBERNATE) system_decrement_retry_counter(); configure_board_specific_gpios(); init_pmu(); reset_wake_logic(); init_trng(); maybe_trigger_ite_sync(); init_jittery_clock(1); init_runlevel(PERMISSION_MEDIUM); /* Initialize NvMem partitions */ nvmem_init(); /* * If this was a low power wake and not a rollback, restore the ccd * state from the long-life register. */ if ((system_get_reset_flags() & EC_RESET_FLAG_HIBERNATE) && !system_rollback_detected()) { ccd_init_state = (GREG32(PMU, LONG_LIFE_SCRATCH1) & BOARD_CCD_STATE) >> BOARD_CCD_SHIFT; } /* Load case-closed debugging config. Must be after initvars(). */ ccd_config_init(ccd_init_state); system_update_rollback_mask_with_both_imgs(); /* Indication that firmware is running, for debug purposes. */ GREG32(PMU, PWRDN_SCRATCH16) = 0xCAFECAFE; /* * Call the function twice to make it harder to glitch execution into * passing the check when not supposed to. */ check_board_id_mismatch(); check_board_id_mismatch(); /* * Start monitoring AC detect to wake Cr50 from deep sleep. This is * needed to detect RDD cable changes in deep sleep. AC detect is also * used for battery cutoff software support on detachable devices. */ init_ac_detect(); init_rdd_state(); /* Initialize write protect. Must be after CCD config init. */ init_wp_state(); /* * Need to do this at run time as compile time constant initialization * to a variable value (even to a const known at compile time) is not * supported. */ bitbang_config.uart_in = ec_uart.producer.queue; /* * Enable interrupt handler for RBOX key combo so it can be used to * store the recovery request. */ if (board_uses_closed_source_set1()) { /* Enable interrupt handler for reset button combo */ task_enable_irq(GC_IRQNUM_RBOX0_INTR_BUTTON_COMBO0_RDY_INT); GWRITE_FIELD(RBOX, INT_ENABLE, INTR_BUTTON_COMBO0_RDY, 1); } /* * Note that the AP, EC, and servo state machines do not have explicit * init_xxx_state() functions, because they don't need to configure * registers prior to starting their state machines. Their state * machines run in HOOK_SECOND, which first triggers right after * HOOK_INIT, not at +1.0 seconds. */ } DECLARE_HOOK(HOOK_INIT, board_init, HOOK_PRIO_DEFAULT); /** * Hook for CCD config loaded/changed. */ static void board_ccd_config_changed(void) { /* Store the current CCD state so we can restore it after deep sleep */ GWRITE_FIELD(PMU, LONG_LIFE_SCRATCH_WR_EN, REG1, 1); GREG32(PMU, LONG_LIFE_SCRATCH1) &= ~BOARD_CCD_STATE; GREG32(PMU, LONG_LIFE_SCRATCH1) |= (ccd_get_state() << BOARD_CCD_SHIFT) & BOARD_CCD_STATE; GWRITE_FIELD(PMU, LONG_LIFE_SCRATCH_WR_EN, REG1, 0); if (board_uses_closed_source_set1()) closed_source_set1_update_factory_mode(); /* Update CCD state */ ccd_update_state(); } DECLARE_HOOK(HOOK_CCD_CHANGE, board_ccd_config_changed, HOOK_PRIO_DEFAULT); #if defined(CONFIG_USB) const void * const usb_strings[] = { [USB_STR_DESC] = usb_string_desc, [USB_STR_VENDOR] = USB_STRING_DESC("Google Inc."), [USB_STR_PRODUCT] = USB_STRING_DESC("Cr50"), [USB_STR_VERSION] = USB_STRING_DESC(CROS_EC_VERSION32), [USB_STR_CONSOLE_NAME] = USB_STRING_DESC("Shell"), [USB_STR_BLOB_NAME] = USB_STRING_DESC("Blob"), [USB_STR_HID_KEYBOARD_NAME] = USB_STRING_DESC("PokeyPokey"), [USB_STR_AP_NAME] = USB_STRING_DESC("AP"), [USB_STR_EC_NAME] = USB_STRING_DESC("EC"), [USB_STR_UPGRADE_NAME] = USB_STRING_DESC("Firmware upgrade"), [USB_STR_SPI_NAME] = USB_STRING_DESC("AP EC upgrade"), [USB_STR_SERIALNO] = USB_STRING_DESC(DEFAULT_SERIALNO), [USB_STR_I2C_NAME] = USB_STRING_DESC("I2C"), }; BUILD_ASSERT(ARRAY_SIZE(usb_strings) == USB_STR_COUNT); #endif /* SPI devices */ const struct spi_device_t spi_devices[] = { [CONFIG_SPI_FLASH_PORT] = {0, 2, GPIO_COUNT} }; const unsigned int spi_devices_used = ARRAY_SIZE(spi_devices); int flash_regions_to_enable(struct g_flash_region *regions, int max_regions) { /* * This needs to account for two regions: the "other" RW partition and * the NVRAM in TOP_B. * * When running from RW_A the two regions are adjacent, but it is * simpler to keep function logic the same and always configure two * separate regions. */ if (max_regions < 3) return 0; /* Enable access to the other RW image... */ if (system_get_image_copy() == SYSTEM_IMAGE_RW) /* Running RW_A, enable RW_B */ regions[0].reg_base = CONFIG_MAPPED_STORAGE_BASE + CONFIG_RW_B_MEM_OFF; else /* Running RW_B, enable RW_A */ regions[0].reg_base = CONFIG_MAPPED_STORAGE_BASE + CONFIG_RW_MEM_OFF; /* Size is the same */ regions[0].reg_size = CONFIG_RW_SIZE; regions[0].reg_perms = FLASH_REGION_EN_ALL; /* Enable access to the NVRAM partition A region */ regions[1].reg_base = CONFIG_MAPPED_STORAGE_BASE + CFG_TOP_A_OFF; regions[1].reg_size = CFG_TOP_SIZE; regions[1].reg_perms = FLASH_REGION_EN_ALL; /* Enable access to the NVRAM partition B region */ regions[2].reg_base = CONFIG_MAPPED_STORAGE_BASE + CFG_TOP_B_OFF; regions[2].reg_size = CFG_TOP_SIZE; regions[2].reg_perms = FLASH_REGION_EN_ALL; return 3; } /** * Deferred TPM reset interrupt handling * * This is always called from the HOOK task. */ static void deferred_tpm_rst_isr(void) { CPRINTS("%s", __func__); /* * TPM reset is used to detect the AP, connect AP. Let the AP state * machine know the AP is on. */ set_ap_on(); /* * If no reboot request is posted, OR if the other RW's header is not * ready to run - do not try rebooting the device, just reset the * TPM. * * The inactive header will have to be restored by the appropriate * vendor command, the device will be rebooted then. */ if (!reboot_request_posted || other_rw_is_inactive()) { /* Reset TPM, no need to wait for completion. */ tpm_reset_request(0, 0); return; } /* * Reset TPM and wait to completion to make sure nvmem is * committed before reboot. */ tpm_reset_request(1, 0); /* This will never return. */ system_reset(SYSTEM_RESET_MANUALLY_TRIGGERED | SYSTEM_RESET_HARD); } /** * Handle TPM_RST_L deasserting * * This can also be called explicitly from AP detection, if it thinks the * interrupt handler missed the rising edge. */ void tpm_rst_deasserted(enum gpio_signal signal) { hook_call_deferred(&deferred_tpm_rst_isr_data, 0); } void assert_sys_rst(void) { /* Assert it */ gpio_set_level(GPIO_SYS_RST_L_OUT, 0); } void deassert_sys_rst(void) { /* Deassert it */ gpio_set_level(GPIO_SYS_RST_L_OUT, 1); } static int is_sys_rst_asserted(void) { /* * SYS_RST_L is pseudo open drain. It is only an output when it's * asserted. */ return gpio_get_flags(GPIO_SYS_RST_L_OUT) & GPIO_OUTPUT; } /** * Reboot the AP */ void board_reboot_ap(void) { if (board_uses_closed_loop_reset()) { board_closed_loop_reset(); return; } assert_sys_rst(); msleep(20); deassert_sys_rst(); } /** * Reboot the EC */ void board_reboot_ec(void) { if (board_uses_closed_loop_reset()) { board_closed_loop_reset(); return; } assert_ec_rst(); deassert_ec_rst(); } /* * This interrupt handler will be called if the RBOX key combo is detected. */ static void key_combo0_irq(void) { GWRITE_FIELD(RBOX, INT_STATE, INTR_BUTTON_COMBO0_RDY, 1); recovery_button_record(); board_reboot_ec(); CPRINTS("Recovery Requested"); } DECLARE_IRQ(GC_IRQNUM_RBOX0_INTR_BUTTON_COMBO0_RDY_INT, key_combo0_irq, 0); /** * Console command to toggle system (AP) reset */ static int command_sys_rst(int argc, char **argv) { int val; char *e; int ms = 20; if (argc > 1) { if (!ccd_is_cap_enabled(CCD_CAP_REBOOT_EC_AP)) return EC_ERROR_ACCESS_DENIED; if (!strcasecmp("pulse", argv[1])) { if (argc == 3) { ms = strtoi(argv[2], &e, 0); if (*e) return EC_ERROR_PARAM2; } ccprintf("Pulsing AP reset for %dms\n", ms); assert_sys_rst(); msleep(ms); deassert_sys_rst(); } else if (parse_bool(argv[1], &val)) { if (val) assert_sys_rst(); else deassert_sys_rst(); } else return EC_ERROR_PARAM1; } ccprintf("SYS_RST_L is %s\n", is_sys_rst_asserted() ? "asserted" : "deasserted"); return EC_SUCCESS; } DECLARE_SAFE_CONSOLE_COMMAND(sysrst, command_sys_rst, "[pulse [time] | ]", "Assert/deassert SYS_RST_L to reset the AP"); /* * Set RBOX register controlling EC reset and wait until RBOX updates the * output. * * Input parameter is treated as a Boolean, 1 means reset needs to be * asserted, 0 means reset needs to be deasserted. */ static void wait_ec_rst(int level) { int i; /* Just in case. */ level = !!level; GWRITE(RBOX, ASSERT_EC_RST, level); /* * If ec_rst value is being explicitly set while power button is held * pressed after reset, do not let "power button release" ISR change * the ec_rst value. */ power_button_release_enable_interrupt(0); /* * RBOX is running on its own clock, let's make sure we don't exit * this function until the ecr_rst output matches the desired setting. * 1000 cycles is way more than needed for RBOX to react. * * Note that the read back value is the inversion of the value written * into the register once it propagates through RBOX. */ for (i = 0; i < 1000; i++) if (GREAD_FIELD(RBOX, CHECK_OUTPUT, EC_RST) != level) break; } void assert_ec_rst(void) { /* Prevent bit bang interrupt storm. */ if (uart_bitbang_is_enabled()) task_disable_irq(bitbang_config.rx_irq); wait_ec_rst(1); } static void deassert_ec_rst_now(void) { wait_ec_rst(0); if (uart_bitbang_is_enabled()) task_enable_irq(bitbang_config.rx_irq); } DECLARE_DEFERRED(deassert_ec_rst_now); void deassert_ec_rst(void) { /* * On closed source set1, the EC requires a minimum 30 ms pulse to * properly reset. Ensure EC reset is never de-asesrted for less * than this time. */ if (board_uses_closed_source_set1()) hook_call_deferred(&deassert_ec_rst_now_data, 30 * MSEC); else deassert_ec_rst_now(); } int is_ec_rst_asserted(void) { return GREAD(RBOX, ASSERT_EC_RST); } /** * Console command to toggle EC reset */ static int command_ec_rst(int argc, char **argv) { int val; if (argc > 1) { if (!ccd_is_cap_enabled(CCD_CAP_REBOOT_EC_AP)) return EC_ERROR_ACCESS_DENIED; if (!strcasecmp("cl", argv[1])) { /* Assert EC_RST_L until TPM_RST_L is asserted */ board_closed_loop_reset(); } else if (!strcasecmp("pulse", argv[1])) { ccprintf("Pulsing EC reset\n"); board_reboot_ec(); } else if (parse_bool(argv[1], &val)) { if (val) assert_ec_rst(); else deassert_ec_rst(); } else return EC_ERROR_PARAM1; } ccprintf("EC_RST_L is %s\n", is_ec_rst_asserted() ? "asserted" : "deasserted"); return EC_SUCCESS; } DECLARE_SAFE_CONSOLE_COMMAND(ecrst, command_ec_rst, "[cl | pulse | ]", "Assert/deassert EC_RST_L to reset the EC (and AP)"); /* * This function duplicates some of the functionality in chip/g/gpio.c in order * to configure a given strap pin to be either a low gpio output, a gpio input * with or without an internal pull resistor, or disconnect the gpio signal * from the pin pad. * * The desired gpio functionality is contained in the input parameter flags, * while the strap parameter is an index into the array strap_regs. */ static void strap_config_pin(enum strap_list strap, int flags) { const struct gpio_info *g = gpio_list + strap_regs[strap].gpio_signal; int bitnum = GPIO_MASK_TO_NUM(g->mask); int mask = DIO_CTL_IE_MASK | DIO_CTL_PD_MASK | DIO_CTL_PU_MASK; int val; if (!flags) { /* Reset strap pins, disconnect output and clear pull up/dn */ /* Disconnect gpio from pin mux */ DIO_SEL_REG(strap_regs[strap].sel_offset) = 0; /* Clear input enable and pulldown/pullup in pinmux */ REG_WRITE_MLV(DIO_CTL_REG(strap_regs[strap].sel_offset), mask, 0, 0); return; } if (flags & GPIO_OUT_LOW) { /* Config gpio to output and drive low */ gpio_set_flags(strap_regs[strap].gpio_signal, GPIO_OUT_LOW); /* connect pin mux to gpio */ DIO_SEL_REG(strap_regs[strap].sel_offset) = GET_GPIO_FUNC(g->port, bitnum); return; } if (flags & GPIO_INPUT) { /* Configure gpio pin to be an input */ gpio_set_flags(strap_regs[strap].gpio_signal, GPIO_INPUT); /* Connect pad to gpio */ GET_GPIO_SEL_REG(g->port, bitnum) = strap_regs[strap].pad_select; /* * Input enable is bit 2 of the CTL register. Pulldown enable is * bit 3, and pullup enable is bit 4. Always set input enable * and clear the pullup/pulldown bits unless the flags variable * specifies that pulldown or pullup should be enabled. */ val = DIO_CTL_IE_MASK; if (flags & GPIO_PULL_DOWN) val |= DIO_CTL_PD_MASK; if (flags & GPIO_PULL_UP) val |= DIO_CTL_PU_MASK; /* Set input enable and pulldown/pullup in pinmux */ REG_WRITE_MLV(DIO_CTL_REG(strap_regs[strap].sel_offset), mask, 0, val); } } static int get_strap_config(uint8_t *config) { enum strap_list s0; int lvl; int flags; uint8_t use_i2c; uint8_t i2c_prop; uint8_t use_spi; uint8_t spi_prop; /* * There are 4 pins that are used to determine Cr50 board strapping * options. These pins are: * 1. DIOA1 -> I2CS_SDA * 2. DI0A9 -> I2CS_SCL * 3. DIOA6 -> SPS_CLK * 4. DIOA12 -> SPS_CS_L * There are two main configuration options based on whether I2C or SPI * is used for TPM2 communication to/from the host AP. If SPI is the * TPM2 bus, then the pair of pins DIOA9|DIOA1 are used to designate * strapping options. If TPM uses I2C, then DIOA12|DIOA6 are the * strapping pins. * * Each strapping pin will have either an external pullup or pulldown * resistor. The external pull resistors have two levels, 5k for strong * and 1M for weak. Cr50 has internal pullup/pulldown 50k resistors that * can be configured via pinmux register settings. This combination of * external and internal pullup/pulldown resistors allows for 4 possible * states per strapping pin. The following table shows the different * combinations. Note that when a strong external pull down/up resistor * is used, the internal resistor is a don't care and those cases are * marked by n/a. The bits column represents the signal level read on * the gpio pin. Bit 1 of this field is the value read with the internal * pull down/up resistors disabled, and bit 0 is the gpio signal level * of the same pin when the internal pull resistor is selected as shown * in the 'internal' column. * external internal bits * -------- -------- ---- * 5K PD n/a 00 * 1M PD 50k PU 01 * 1M PU 50k PD 10 * 5K PU n/a 11 * * To determine the bits associated with each strapping pin, the * following method is used. * 1. Set all 4 pins as inputs with internal pulls disabled. * 2. For each pin do the following to encode 2 bits b1:b0 * a. b1 = gpio_get_level(pin) * b. If b1 == 1, then enable internal pulldown, else enable * internal pullup resistor. * c. b0 = gpio_get_level(pin) * * To be considered a valid strap configuraiton, the upper 4 bits must * have no pullups and at least one pullup in the lower 4 bits or vice * versa. So can use 0xA0 and 0x0A as masks to check for each * condition. Once this check is passed, the 4 bits which are used to * distinguish between SPI vs I2C are masked since reading them as weak * pulldowns is not being explicitly required due to concerns that the * AP could prevent accurate differentiation between strong and weak * pull down cases. */ /* Drive all 4 strap pins low to discharge caps. */ for (s0 = a0; s0 < ARRAY_SIZE(strap_regs); s0++) strap_config_pin(s0, GPIO_OUT_LOW); /* Delay long enough to discharge any caps. */ udelay(STRAP_PIN_DELAY_USEC); /* Set all 4 strap pins as inputs with pull resistors disabled. */ for (s0 = a0; s0 < ARRAY_SIZE(strap_regs); s0++) strap_config_pin(s0, GPIO_INPUT); /* Delay so voltage levels can settle. */ udelay(STRAP_PIN_DELAY_USEC); *config = 0; /* Read 2 bit value of each strapping pin. */ ccprintf("strap pin readings:"); for (s0 = a0; s0 < ARRAY_SIZE(strap_regs); s0++) { lvl = gpio_get_level(strap_regs[s0].gpio_signal); flags = GPIO_INPUT; if (lvl) flags |= GPIO_PULL_DOWN; else flags |= GPIO_PULL_UP; /* Enable internal pull down/up resistor. */ strap_config_pin(s0, flags); udelay(STRAP_PIN_DELAY_USEC); lvl = (lvl << 1) | gpio_get_level(strap_regs[s0].gpio_signal); ccprintf(" %s:%d", strap_regs[s0].pad_name, lvl); *config |= lvl << s0 * 2; /* * Finished with this pin. Disable internal pull up/dn resistor * and disconnect gpio from pin mux. The pins used for straps * are configured for their desired role when either the SPI or * I2C interfaces are initialized. */ strap_config_pin(s0, 0); } ccprintf("\n"); /* * The strap bits for DIOA12|DIOA6 are in the upper 4 bits of 'config' * while the strap bits for DIOA9|DIOA1 are in the lower 4 bits. Check * for SPI vs I2C config by checking for presence of external pullups in * one group of 4 bits and confirming no external pullups in the other * group. For SPI config the weak pulldowns may not be accurately read * on DIOA12|DIOA6 and similarly for I2C config on * DIOA9|DIOA1. Therefore, only requiring that there be no external * pullups on these pins and will mask the bits so they will match the * config table entries. */ use_i2c = *config & 0xa0; use_spi = *config & 0x0a; /* * The strap signals should have at least one pullup. Nothing can * interfere with these. If we did not read any pullups, these are * invalid straps. The config can't be salvaged. */ if (!use_i2c && !use_spi) return EC_ERROR_INVAL; /* * The unused strap signals are used for the bus to the AP. If the AP * has added pullups to the signals, it could interfere with the strap * readings. If pullups are found on both the SPI and I2C straps, use * the board properties to determine SPI vs I2C. We can use this to mask * unused config pins the AP is interfering with. */ if (use_i2c && use_spi) { spi_prop = (GREG32(PMU, LONG_LIFE_SCRATCH1) & BOARD_SLAVE_CONFIG_SPI); i2c_prop = (GREG32(PMU, LONG_LIFE_SCRATCH1) & BOARD_SLAVE_CONFIG_I2C); /* Make sure exactly one interface is selected */ if ((i2c_prop && spi_prop) || (!spi_prop && !i2c_prop)) return EC_ERROR_INVAL; use_spi = spi_prop; CPRINTS("Ambiguous strap config. Use %s based on old " "brdprop.", use_spi ? "spi" : "i2c"); } /* Now that I2C vs SPI is known, mask the unused strap bits. */ *config &= use_spi ? 0xf : 0xf0; return EC_SUCCESS; } static uint32_t get_properties(void) { int i; uint8_t config; uint32_t properties; if (chip_factory_mode()) { CPRINTS("Chip factory mode, short circuit to SPI"); return BOARD_SLAVE_CONFIG_SPI; } #ifdef H1_RED_BOARD CPRINTS("Unconditionally enabling SPI and platform reset"); return (BOARD_SLAVE_CONFIG_SPI | BOARD_USE_PLT_RESET); #endif if (get_strap_config(&config) != EC_SUCCESS) { /* * No pullups were detected on any of the strap pins so there * is no point in checking for a matching config table entry. * For this case use default properties. */ CPRINTS("Invalid strap pins! Default properties = 0x%x", BOARD_PROPERTIES_DEFAULT); return BOARD_PROPERTIES_DEFAULT; } /* Search board config table to find a matching entry */ for (i = 0; i < ARRAY_SIZE(board_cfg_table); i++) { if (board_cfg_table[i].strap_cfg == config) { properties = board_cfg_table[i].board_properties; CPRINTS("Valid strap: 0x%x properties: 0x%x", config, properties); /* Read board properties for this config */ return properties; } } /* * Reached the end of the table and didn't find a matching config entry. * However, the SPI vs I2C determination can still be made as * get_strap_config() returned EC_SUCCESS. */ if (config & 0xa) { properties = BOARD_SLAVE_CONFIG_SPI; /* * Determine PLT_RST_L vs SYS_RST_L. Any board with a pullup on * DIOA9 uses PLT_RST_L. */ properties |= config & 0x8 ? BOARD_USE_PLT_RESET : 0; } else { /* All I2C boards use same default properties. */ properties = BOARD_PROPERTIES_DEFAULT; } CPRINTS("strap_cfg 0x%x has no table entry, prop = 0x%x", config, properties); return properties; } static void init_board_properties(void) { uint32_t properties; properties = GREG32(PMU, LONG_LIFE_SCRATCH1); /* * This must be a power on reset or maybe restart due to a software * update from a version not setting the register. */ if (!(properties & BOARD_ALL_PROPERTIES) || (system_get_reset_flags() & EC_RESET_FLAG_HARD)) { /* * Mask board properties because following hard reset, they * won't be cleared. */ properties &= ~BOARD_ALL_PROPERTIES; properties |= get_properties(); /* * Now save the properties value for future use. * * Enable access to LONG_LIFE_SCRATCH1 reg. */ GWRITE_FIELD(PMU, LONG_LIFE_SCRATCH_WR_EN, REG1, 1); /* Save properties in LONG_LIFE register */ GREG32(PMU, LONG_LIFE_SCRATCH1) = properties; /* Disable access to LONG_LIFE_SCRATCH1 reg */ GWRITE_FIELD(PMU, LONG_LIFE_SCRATCH_WR_EN, REG1, 0); } /* Save this configuration setting */ board_properties = properties; } DECLARE_HOOK(HOOK_INIT, init_board_properties, HOOK_PRIO_FIRST); void i2cs_set_pinmux(void) { /* Connect I2CS SDA/SCL output to A1/A9 pads */ GWRITE(PINMUX, DIOA1_SEL, GC_PINMUX_I2CS0_SDA_SEL); GWRITE(PINMUX, DIOA9_SEL, GC_PINMUX_I2CS0_SCL_SEL); /* Connect A1/A9 pads to I2CS input SDA/SCL */ GWRITE(PINMUX, I2CS0_SDA_SEL, GC_PINMUX_DIOA1_SEL); GWRITE(PINMUX, I2CS0_SCL_SEL, GC_PINMUX_DIOA9_SEL); /* Enable SDA/SCL inputs from A1/A9 pads */ GWRITE_FIELD(PINMUX, DIOA1_CTL, IE, 1); /* I2CS_SDA */ GWRITE_FIELD(PINMUX, DIOA9_CTL, IE, 1); /* I2CS_SCL */ /* * Provide access to the SDA line to be able to detect 'hosed i2c * slave' condition. */ GWRITE(PINMUX, GPIO0_GPIO14_SEL, GC_PINMUX_DIOA1_SEL); /* Allow I2CS_SCL to wake from sleep */ GWRITE_FIELD(PINMUX, EXITEDGE0, DIOA9, 1); /* edge sensitive */ GWRITE_FIELD(PINMUX, EXITINV0, DIOA9, 1); /* wake on low */ GWRITE_FIELD(PINMUX, EXITEN0, DIOA9, 1); /* enable powerdown exit */ /* Allow I2CS_SDA to wake from sleep */ GWRITE_FIELD(PINMUX, EXITEDGE0, DIOA1, 1); /* edge sensitive */ GWRITE_FIELD(PINMUX, EXITINV0, DIOA1, 1); /* wake on low */ GWRITE_FIELD(PINMUX, EXITEN0, DIOA1, 1); /* enable powerdown exit */ } /* Determine key type based on the key ID. */ static const char *key_type(const struct SignedHeader *h) { if (G_SIGNED_FOR_PROD(h)) return "prod"; else return "dev"; } static int command_sysinfo(int argc, char **argv) { enum system_image_copy_t active; uintptr_t vaddr; const struct SignedHeader *h; int reset_count = GREG32(PMU, LONG_LIFE_SCRATCH0); char rollback_str[15]; uint8_t tpm_mode; ccprintf("Reset flags: 0x%08x (", system_get_reset_flags()); system_print_reset_flags(); ccprintf(")\n"); if (system_rollback_detected()) ccprintf("Rollback detected\n"); ccprintf("Reset count: %d\n", reset_count); ccprintf("Chip: %s %s %s\n", system_get_chip_vendor(), system_get_chip_name(), system_get_chip_revision()); active = system_get_ro_image_copy(); vaddr = get_program_memory_addr(active); h = (const struct SignedHeader *)vaddr; ccprintf("RO keyid: 0x%08x(%s)\n", h->keyid, key_type(h)); active = system_get_image_copy(); vaddr = get_program_memory_addr(active); h = (const struct SignedHeader *)vaddr; ccprintf("RW keyid: 0x%08x(%s)\n", h->keyid, key_type(h)); ccprintf("DEV_ID: 0x%08x 0x%08x\n", GREG32(FUSE, DEV_ID0), GREG32(FUSE, DEV_ID1)); system_get_rollback_bits(rollback_str, sizeof(rollback_str)); ccprintf("Rollback: %s\n", rollback_str); tpm_mode = get_tpm_mode(); ccprintf("TPM MODE: %s (%d)\n", (tpm_mode == TPM_MODE_DISABLED) ? "disabled" : "enabled", tpm_mode); ccprintf("Key Ladder: %s\n", DCRYPTO_ladder_is_enabled() ? "enabled" : "disabled"); return EC_SUCCESS; } DECLARE_SAFE_CONSOLE_COMMAND(sysinfo, command_sysinfo, NULL, "Print system info"); /* * SysInfo command: * There are no input args. * Output is this struct, all fields in network order. */ struct sysinfo_s { uint32_t ro_keyid; uint32_t rw_keyid; uint32_t dev_id0; uint32_t dev_id1; } __packed; static enum vendor_cmd_rc vc_sysinfo(enum vendor_cmd_cc code, void *buf, size_t input_size, size_t *response_size) { enum system_image_copy_t active; uintptr_t vaddr; const struct SignedHeader *h; struct sysinfo_s *sysinfo = buf; active = system_get_ro_image_copy(); vaddr = get_program_memory_addr(active); h = (const struct SignedHeader *)vaddr; sysinfo->ro_keyid = htobe32(h->keyid); active = system_get_image_copy(); vaddr = get_program_memory_addr(active); h = (const struct SignedHeader *)vaddr; sysinfo->rw_keyid = htobe32(h->keyid); sysinfo->dev_id0 = htobe32(GREG32(FUSE, DEV_ID0)); sysinfo->dev_id1 = htobe32(GREG32(FUSE, DEV_ID1)); *response_size = sizeof(*sysinfo); return VENDOR_RC_SUCCESS; } DECLARE_VENDOR_COMMAND(VENDOR_CC_SYSINFO, vc_sysinfo); static enum vendor_cmd_rc vc_invalidate_inactive_rw(enum vendor_cmd_cc code, void *buf, size_t input_size, size_t *response_size) { const struct SignedHeader *header; uint32_t ctrl; uint32_t base_addr; uint32_t size; const char zero[4] = {}; /* value to write to magic. */ *response_size = 0; /* Update INFO1 mask based on the currently active image. */ system_update_rollback_mask_with_active_img(); if (other_rw_is_inactive()) { CPRINTS("%s: Inactive region is disabled", __func__); return VENDOR_RC_SUCCESS; } /* save the original flash region6 register values */ ctrl = GREAD(GLOBALSEC, FLASH_REGION6_CTRL); base_addr = GREG32(GLOBALSEC, FLASH_REGION6_BASE_ADDR); size = GREG32(GLOBALSEC, FLASH_REGION6_SIZE); header = get_other_rw_addr(); /* Enable RW access to the other header. */ GREG32(GLOBALSEC, FLASH_REGION6_BASE_ADDR) = (uint32_t) header; GREG32(GLOBALSEC, FLASH_REGION6_SIZE) = 1023; GWRITE_FIELD(GLOBALSEC, FLASH_REGION6_CTRL, EN, 1); GWRITE_FIELD(GLOBALSEC, FLASH_REGION6_CTRL, RD_EN, 1); GWRITE_FIELD(GLOBALSEC, FLASH_REGION6_CTRL, WR_EN, 1); CPRINTS("%s: TPM verified corrupting inactive image, magic before %x", __func__, header->magic); flash_physical_write((intptr_t)&header->magic - CONFIG_PROGRAM_MEMORY_BASE, sizeof(zero), zero); CPRINTS("%s: magic after: %x", __func__, header->magic); /* Restore original values */ GREG32(GLOBALSEC, FLASH_REGION6_BASE_ADDR) = base_addr; GREG32(GLOBALSEC, FLASH_REGION6_SIZE) = size; GREG32(GLOBALSEC, FLASH_REGION6_CTRL) = ctrl; return VENDOR_RC_SUCCESS; } DECLARE_VENDOR_COMMAND(VENDOR_CC_INVALIDATE_INACTIVE_RW, vc_invalidate_inactive_rw); static enum vendor_cmd_rc vc_commit_nvmem(enum vendor_cmd_cc code, void *buf, size_t input_size, size_t *response_size) { nvmem_enable_commits(); *response_size = 0; return VENDOR_RC_SUCCESS; } DECLARE_VENDOR_COMMAND(VENDOR_CC_COMMIT_NVMEM, vc_commit_nvmem); static int command_board_properties(int argc, char **argv) { /* * The board properties are stored in LONG_LIFE_SCRATCH1. Note that we * don't just simply return board_properties here since that's just a * cached value from init time. */ ccprintf("properties = 0x%x\n", GREG32(PMU, LONG_LIFE_SCRATCH1)); return EC_SUCCESS; } DECLARE_SAFE_CONSOLE_COMMAND(brdprop, command_board_properties, NULL, "Display board properties"); int chip_factory_mode(void) { static uint8_t mode_set; /* * Bit 0x2 used to indicate that mode has been set, bit 0x1 is the * actual indicator of the chip factory mode. */ if (!mode_set) mode_set = 2 | !!gpio_get_level(GPIO_DIOB4); return mode_set & 1; } #ifdef CR50_RELAXED static int command_rollback(int argc, char **argv) { system_ensure_rollback(); ccprintf("Rebooting to alternate RW due to manual request\n"); cflush(); system_reset(0); return EC_SUCCESS; } DECLARE_SAFE_CONSOLE_COMMAND(rollback, command_rollback, "", "Force rollback to escape DEV image."); #endif /* * Set long life register bit requesting generating of the ITE SYNC sequence * and reboot. */ static void deferred_ite_sync_reset(void) { /* Enable writing to the long life register */ GWRITE_FIELD(PMU, LONG_LIFE_SCRATCH_WR_EN, REG1, 1); GREG32(PMU, LONG_LIFE_SCRATCH1) |= BOARD_ITE_EC_SYNC_NEEDED; /* Disable writing to the long life register */ GWRITE_FIELD(PMU, LONG_LIFE_SCRATCH_WR_EN, REG1, 0); system_reset(SYSTEM_RESET_MANUALLY_TRIGGERED | SYSTEM_RESET_HARD); } DECLARE_DEFERRED(deferred_ite_sync_reset); void board_start_ite_sync(void) { /* Let the usb reply to make it to the host. */ hook_call_deferred(&deferred_ite_sync_reset_data, 10 * MSEC); } void board_unwedge_i2cs(void) { /* * Create connection between i2cs_scl and the 'unwedge_scl' GPIO, and * generate the i2c stop sequence which will reset the i2cs FSM. * * First, disconnect the external pin from the i2cs_scl input. */ GWRITE(PINMUX, DIOA9_SEL, 0); /* Connect the 'unwedge' GPIO to the i2cs_scl input. */ GWRITE(PINMUX, GPIO1_GPIO5_SEL, GC_PINMUX_I2CS0_SCL_SEL); /* Generate a 'stop' condition. */ gpio_set_level(GPIO_UNWEDGE_I2CS_SCL, 1); usleep(2); GWRITE_FIELD(I2CS, CTRL_SDA_VAL, READ0_S, 1); usleep(2); GWRITE_FIELD(I2CS, CTRL_SDA_VAL, READ0_S, 0); usleep(2); /* Disconnect the 'unwedge' mode SCL. */ GWRITE(PINMUX, GPIO1_GPIO5_SEL, 0); /* Restore external pin connection to the i2cs_scl. */ GWRITE(PINMUX, DIOA9_SEL, GC_PINMUX_I2CS0_SCL_SEL); }