/* 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. */ #include "clock.h" #include "common.h" #include "console.h" #include "dcrypto/dcrypto.h" #include "device_state.h" #include "ec_version.h" #include "flash_config.h" #include "gpio.h" #include "hooks.h" #include "i2cs.h" #include "init_chip.h" #include "nvmem.h" #include "registers.h" #include "spi.h" #include "system.h" #include "task.h" #include "tpm_registers.h" #include "trng.h" #include "uartn.h" #include "usb_descriptor.h" #include "usb_hid.h" #include "usb_spi.h" #include "util.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 NVMEM_CR50_SIZE 300 #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 */ 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; /* * 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" 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 exiten, wakeup_src; int plt_rst_asserted; 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); if (wakeup_src & GC_PMU_EXITPD_SRC_PIN_PD_EXIT_MASK) { /* * If any wake pins are edge triggered, the pad logic latches * the wakeup. Clear EXITEN0 to reset the wakeup logic. */ exiten = GREG32(PINMUX, EXITEN0); GREG32(PINMUX, EXITEN0) = 0; GREG32(PINMUX, EXITEN0) = exiten; /* * Delay sleep long enough for a SPI slave transaction to start * or for the system to be reset. */ delay_sleep_by(3 * MINUTE); /* * If sys_rst_l or plt_rst_l (if signal is present) is * configured to wake on low and the signal is low, then call * sys_rst_asserted */ /* * TODO(crosbug.com/p/56540): When plt_rst_l is connected to * DIOM3, need to change DIOA13 below to DIOM3 so that * the correct wake on low setting is being checked. */ plt_rst_asserted = board_properties & BOARD_USE_PLT_RESET ? !gpio_get_level(GPIO_PLT_RST_L) : 0; if ((!gpio_get_level(GPIO_SYS_RST_L_IN) && GREAD_FIELD(PINMUX, EXITINV0, DIOM0)) || (plt_rst_asserted && GREAD_FIELD(PINMUX, EXITINV0, DIOA13))) sys_rst_asserted(GPIO_SYS_RST_L_IN); } /* 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 */ /* TODO remove i2cs wake event */ /* * DIOA3 is GPIO_DETECT_AP which is used to detect if the AP is in S0. * If the AP is in s0, cr50 should not be in deep sleep so wake up. */ GWRITE_FIELD(PINMUX, EXITEDGE0, DIOA3, 1); /* edge sensitive */ GWRITE_FIELD(PINMUX, EXITINV0, DIOA3, 0); /* wake on high */ GWRITE_FIELD(PINMUX, EXITEN0, DIOA3, 1); /* GPIO_DETECT_AP */ /* * Whether it is a short pulse or long one waking on the rising edge is * fine because the goal of sys_rst 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. * * Configure cr50 to resume on the rising edge of sys_rst_l */ /* Disable sys_rst_l as a wake pin */ GWRITE_FIELD(PINMUX, EXITEN0, DIOM0, 0); /* Reconfigure and reenable it. */ GWRITE_FIELD(PINMUX, EXITEDGE0, DIOM0, 1); /* edge sensitive */ GWRITE_FIELD(PINMUX, EXITINV0, DIOM0, 0); /* wake on high */ GWRITE_FIELD(PINMUX, EXITEN0, DIOM0, 1); /* enable powerdown exit */ /* * If the board includes plt_rst_l, configure Cr50 to resume on the * rising edge of this signal. */ if (system_get_board_properties() & BOARD_USE_PLT_RESET) { /* * TODO(crosbug.com/p/56540): When plt_rst_l is connected to * DIOM3, need to change DIOA13 below to DIOM3 so that * the correct pin is being configured. */ /* Disable sys_rst_l as a wake pin */ GWRITE_FIELD(PINMUX, EXITEN0, DIOA13, 0); /* Reconfigure and reenable it. */ GWRITE_FIELD(PINMUX, EXITEDGE0, DIOA13, 1); /* edge sensitive */ GWRITE_FIELD(PINMUX, EXITINV0, DIOA13, 0); /* wake on high */ /* enable powerdown exit */ GWRITE_FIELD(PINMUX, EXITEN0, DIOA13, 1); } } static void init_interrupts(void) { int i; uint32_t exiten = GREG32(PINMUX, EXITEN0); /* Clear wake pin interrupts */ GREG32(PINMUX, EXITEN0) = 0; GREG32(PINMUX, EXITEN0) = exiten; /* Enable all GPIO interrupts */ for (i = 0; i < gpio_ih_count; i++) if (gpio_list[i].flags & GPIO_INT_ANY) gpio_enable_interrupt(i); } enum permission_level { PERMISSION_LOW = 0x00, PERMISSION_MEDIUM = 0x33, /* APPS run at medium */ PERMISSION_HIGH = 0x3C, PERMISSION_HIGHEST = 0x55 }; /* Drop run level to at least medium. */ static void init_runlevel(const enum permission_level desired_level) { volatile uint32_t *const reg_addrs[] = { /* CPU's use of the system peripheral bus */ GREG32_ADDR(GLOBALSEC, CPU0_S_PERMISSION), /* CPU's use of the system bus via the debug access port */ GREG32_ADDR(GLOBALSEC, CPU0_S_DAP_PERMISSION), /* DMA's use of the system peripheral bus */ GREG32_ADDR(GLOBALSEC, DDMA0_PERMISSION), /* Current software level affects which (if any) scratch * registers can be used for a warm boot hardware-verified * jump. */ GREG32_ADDR(GLOBALSEC, SOFTWARE_LVL), }; int i; /* Permission registers drop by 1 level (e.g. HIGHEST -> HIGH) * each time a write is encountered (the value written does * not matter). So we repeat writes and reads, until the * desired level is reached. */ for (i = 0; i < ARRAY_SIZE(reg_addrs); i++) { uint32_t current_level; while (1) { current_level = *reg_addrs[i]; if (current_level <= desired_level) break; *reg_addrs[i] = desired_level; } } } static void configure_board_specific_gpios(void) { /* Add a pullup to sys_rst_l */ if (system_get_board_properties() & BOARD_NEEDS_SYS_RST_PULL_UP) GWRITE_FIELD(PINMUX, DIOM0_CTL, PU, 1); /* * TODO(crosbug.com/p/56540): Need to connect platform reset to DI0A13 * for current Reef boards. This function is a no-op for Kevin/Gru. When * platform reset is moved to DIOM3 in HW, then need change to * GC_PINMUX_DIOM3_SEL and DIOM3_CTL respectively. In addition, * uncomment the 3 GRWITE() lines for enabling wake on falling * edge. Note that the DIO_WAKE_FALLING config is not required for * DIOA13 as the default for this pad is for uart which already includes * this option for the pminmux setting. */ /* Connect PLT_RST_L signal to the pinmux */ if (system_get_board_properties() & BOARD_USE_PLT_RESET) { /* Signal using GPIO1 pin 10 for DIOA13 */ GWRITE(PINMUX, GPIO1_GPIO10_SEL, GC_PINMUX_DIOA13_SEL); /* Enbale the input */ GWRITE_FIELD(PINMUX, DIOA13_CTL, IE, 1); /* Set power down for the equivalent of DIO_WAKE_FALLING */ /* Set to be edge sensitive */ /* GWRITE_FIELD(PINMUX, EXITEDGE0, DIOM3, 1); */ /* Select failling edge polarity */ /* GWRITE_FIELD(PINMUX, EXITINV0, DIOM3, 1); */ /* Enable powerdown exit on DIOM3 */ /* GWRITE_FIELD(PINMUX, EXITEN0, DIOM3, 1); */ } } /* Initialize board. */ static void board_init(void) { configure_board_specific_gpios(); init_pmu(); init_interrupts(); init_trng(); init_jittery_clock(1); init_runlevel(PERMISSION_MEDIUM); /* Initialize NvMem partitions */ nvmem_init(); /* TODO(crosbug.com/p/49959): For now, leave flash WP unlocked */ GREG32(RBOX, EC_WP_L) = 1; /* Indication that firmware is running, for debug purposes. */ GREG32(PMU, PWRDN_SCRATCH16) = 0xCAFECAFE; } DECLARE_HOOK(HOOK_INIT, board_init, 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_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"), }; 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 + CONFIG_FLASH_NVMEM_OFFSET_A; regions[1].reg_size = NVMEM_PARTITION_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 + CONFIG_FLASH_NVMEM_OFFSET_B; regions[2].reg_size = NVMEM_PARTITION_SIZE; regions[2].reg_perms = FLASH_REGION_EN_ALL; return 3; } #define CPRINTS(format, args...) cprints(CC_SYSTEM, format, ## args) /* This is the interrupt handler to react to SYS_RST_L_IN */ void sys_rst_asserted(enum gpio_signal signal) { /* * Cr50 drives SYS_RST_L in certain scenarios, in those cases * this signal's assertion should be ignored here. */ CPRINTS("%s", __func__); if (usb_spi_update_in_progress() || is_sys_rst_asserted()) return; /* Re-initialize the TPM software state */ tpm_reset(); } void assert_sys_rst(void) { /* * We don't have a good (any?) way to easily look up the pinmux/gpio * assignments in gpio.inc, so they're hard-coded in this routine. This * assertion is just to ensure it hasn't changed. */ ASSERT(GREAD(PINMUX, GPIO0_GPIO4_SEL) == GC_PINMUX_DIOM0_SEL); /* Set SYS_RST_L_OUT as an output, connected to the pad */ GWRITE(PINMUX, DIOM0_SEL, GC_PINMUX_GPIO0_GPIO4_SEL); gpio_set_flags(GPIO_SYS_RST_L_OUT, GPIO_OUT_HIGH); /* Assert it */ gpio_set_level(GPIO_SYS_RST_L_OUT, 0); } void deassert_sys_rst(void) { ASSERT(GREAD(PINMUX, GPIO0_GPIO4_SEL) == GC_PINMUX_DIOM0_SEL); /* Deassert SYS_RST_L */ gpio_set_level(GPIO_SYS_RST_L_OUT, 1); /* Set SYS_RST_L_OUT as an input, disconnected from the pad */ gpio_set_flags(GPIO_SYS_RST_L_OUT, GPIO_INPUT); GWRITE(PINMUX, DIOM0_SEL, 0); } int is_sys_rst_asserted(void) { return (GREAD(PINMUX, DIOM0_SEL) == GC_PINMUX_GPIO0_GPIO4_SEL) #ifdef CONFIG_CMD_GPIO_EXTENDED && (gpio_get_flags(GPIO_SYS_RST_L_OUT) & GPIO_OUTPUT) #endif && (gpio_get_level(GPIO_SYS_RST_L_OUT) == 0); } void assert_ec_rst(void) { GWRITE(RBOX, ASSERT_EC_RST, 1); } void deassert_ec_rst(void) { GWRITE(RBOX, ASSERT_EC_RST, 0); } int is_ec_rst_asserted(void) { return GREAD(RBOX, ASSERT_EC_RST); } void nvmem_wipe_or_reboot(void) { /* * Blindly zapping the TPM space while the AP is awake and poking at it * will bork the TPM task and the AP itself, so force the whole system * off by holding the EC in reset. */ assert_ec_rst(); /* * If we can't clear the NVMEM or can't reset the TPM task, something * is unexpectedly wrong. To be safe, let's reboot the Cr50 (which also * reboots the EC and AP). */ if (nvmem_setup(0) != EC_SUCCESS || tpm_reset() != 1) system_reset(SYSTEM_RESET_HARD); /* Wipe & reset is complete. Allow the EC and AP to reboot */ deassert_ec_rst(); } void nvmem_compute_sha(uint8_t *p_buf, int num_bytes, uint8_t *p_sha, int sha_len) { uint8_t sha1_digest[SHA_DIGEST_SIZE]; /* * Taking advantage of the built in dcrypto engine to generate * a CRC-like value that can be used to validate contents of an * NvMem partition. Only using the lower 4 bytes of the sha1 hash. */ DCRYPTO_SHA1_hash((uint8_t *)p_buf, num_bytes, sha1_digest); memcpy(p_sha, sha1_digest, sha_len); } static int device_state_changed(enum device_type device, enum device_state state) { int state_changed = state != device_states[device].last_known_state; device_set_state(device, state); /* * We've determined the device state, so cancel any deferred callbacks. */ hook_call_deferred(device_states[device].deferred, -1); return state_changed; } /* * If the UART is enabled we cant tell anything about the * servo state, so disable servo detection. */ static int servo_state_unknown(void) { if (uartn_enabled(UART_EC)) { device_set_state(DEVICE_SERVO, DEVICE_STATE_UNKNOWN); return 1; } return 0; } static int device_powered_off(enum device_type device, int uart) { if (device_get_state(device) == DEVICE_STATE_ON) return EC_ERROR_UNKNOWN; if (!device_state_changed(device, DEVICE_STATE_OFF)) return EC_ERROR_UNKNOWN; if (uart) { /* Disable RX and TX on the UART peripheral */ uartn_disable(uart); /* Disconnect the TX pin from the UART peripheral */ uartn_tx_disconnect(uart); } return EC_SUCCESS; } static void servo_deferred(void) { if (servo_state_unknown()) return; device_powered_off(DEVICE_SERVO, 0); } DECLARE_DEFERRED(servo_deferred); static void ap_deferred(void) { if (device_powered_off(DEVICE_AP, UART_AP) == EC_SUCCESS) hook_notify(HOOK_CHIPSET_SHUTDOWN); } DECLARE_DEFERRED(ap_deferred); static void ec_deferred(void) { device_powered_off(DEVICE_EC, UART_EC); } DECLARE_DEFERRED(ec_deferred); struct device_config device_states[] = { [DEVICE_SERVO] = { .deferred = &servo_deferred_data, .detect = GPIO_DETECT_SERVO, .name = "Servo" }, [DEVICE_AP] = { .deferred = &ap_deferred_data, .detect = GPIO_DETECT_AP, .name = "AP" }, [DEVICE_EC] = { .deferred = &ec_deferred_data, .detect = GPIO_DETECT_EC, .name = "EC" }, }; BUILD_ASSERT(ARRAY_SIZE(device_states) == DEVICE_COUNT); /* Returns EC_SUCCESS if the device state changed to on */ static int device_powered_on(enum device_type device, int uart) { /* Update the device state */ if (!device_state_changed(device, DEVICE_STATE_ON)) return EC_ERROR_UNKNOWN; /* Enable RX and TX on the UART peripheral */ uartn_enable(uart); /* Connect the TX pin to the UART TX Signal */ if (device_get_state(DEVICE_SERVO) != DEVICE_STATE_ON && !uartn_enabled(uart)) uartn_tx_connect(uart); return EC_SUCCESS; } static void servo_attached(void) { if (servo_state_unknown()) return; /* Update the device state */ device_state_changed(DEVICE_SERVO, DEVICE_STATE_ON); /* Disconnect AP and EC UART when servo is attached */ uartn_tx_disconnect(UART_AP); uartn_tx_disconnect(UART_EC); } void device_state_on(enum gpio_signal signal) { gpio_disable_interrupt(signal); switch (signal) { case GPIO_DETECT_AP: if (device_powered_on(DEVICE_AP, UART_AP) == EC_SUCCESS) hook_notify(HOOK_CHIPSET_RESUME); break; case GPIO_DETECT_EC: device_powered_on(DEVICE_EC, UART_EC); break; case GPIO_DETECT_SERVO: servo_attached(); break; default: CPRINTS("Device not supported"); return; } } void board_update_device_state(enum device_type device) { if (device == DEVICE_SERVO && servo_state_unknown()) return; /* * If the device is currently on set its state immediately. If it * thinks the device is powered off debounce the signal. */ if (gpio_get_level(device_states[device].detect)) device_state_on(device_states[device].detect); else { device_set_state(device, DEVICE_STATE_UNKNOWN); gpio_enable_interrupt(device_states[device].detect); /* * The signal is low now, but the detect signals are on UART RX * which may be receiving something. Wait long enough for an * entire data chunk to be sent to declare that the device is * off. If the detect signal remains low for 100us then the * signal is low because the device is off. */ hook_call_deferred(device_states[device].deferred, 100); } } void disable_int_ap_l(void) { /* * If I2C TPM is configured then the INT_AP_L signal is used as * a low pulse trigger to sync I2C transactions with the * host. By default Cr50 is driving this line high, but when the * AP powers off, the 1.8V rail that it's pulled up to will be * off and cause exessive power to be consumed by the Cr50. Set * INT_AP_L as an input while the AP is powered off. */ gpio_set_flags(GPIO_INT_AP_L, GPIO_INPUT); } DECLARE_HOOK(HOOK_CHIPSET_SHUTDOWN, disable_int_ap_l, HOOK_PRIO_DEFAULT); void enable_int_ap_l(void) { /* * AP is powering up, set the I2C host sync signal to output and set * it high which is the default level. */ gpio_set_flags(GPIO_INT_AP_L, GPIO_OUT_HIGH); gpio_set_level(GPIO_INT_AP_L, 1); } DECLARE_HOOK(HOOK_CHIPSET_RESUME, enable_int_ap_l, HOOK_PRIO_DEFAULT); void system_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 || system_get_reset_flags() & RESET_FLAG_HARD) { /* * Reset the properties, because after a hard reset the register * won't be cleared. */ properties = 0; /* Read DIOA1 strap pin */ if (gpio_get_level(GPIO_STRAP0)) { /* Strap is pulled high -> Kevin SPI TPM option */ properties |= BOARD_SLAVE_CONFIG_SPI; /* Add an internal pull up on sys_rst_l */ /* * TODO(crosbug.com/p/56945): Remove once SYS_RST_L can * be pulled up externally. */ properties |= BOARD_NEEDS_SYS_RST_PULL_UP; } else { /* Strap is low -> Reef I2C TPM option */ properties |= BOARD_SLAVE_CONFIG_I2C; /* One PHY is connected to the AP */ properties |= BOARD_USB_AP; /* * TODO(crosbug.com/p/56540): enable UART0 RX on Reef. * Early reef boards dont have the necessary pullups on * UART0RX so disable it until that is fixed. */ properties |= BOARD_DISABLE_UART0_RX; /* * Use receiving a usb set address request as a * benchmark for marking the updated image as good. */ properties |= BOARD_MARK_UPDATE_ON_USB_REQ; /* * Platform reset is present and will need to be * configured as a an falling edge interrupt. */ properties |= BOARD_USE_PLT_RESET; } /* * Now save the properties value for future use. * * First enable write access to the LONG_LIFE_SCRATCH1 register. */ GWRITE_FIELD(PMU, LONG_LIFE_SCRATCH_WR_EN, REG1, 1); /* Save properties in LONG_LIFE register */ GREG32(PMU, LONG_LIFE_SCRATCH1) = properties; /* Disabel write access to the LONG_LIFE_SCRATCH1 register */ GWRITE_FIELD(PMU, LONG_LIFE_SCRATCH_WR_EN, REG1, 0); } /* Save this configuration setting */ board_properties = properties; } uint32_t system_board_properties_callback(void) { return board_properties; } 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 */ /* * Enable pull ups on both signals. TODO(vbendeb): consider * adjusting pull strength. */ GWRITE_FIELD(PINMUX, DIOA1_CTL, PU, 1); GWRITE_FIELD(PINMUX, DIOA9_CTL, PU, 1); /* TODO(scollyer): Do we need to add wake on SCL activity here? */ }