/* * Copyright (c) 2019 Microchip Technology Inc. * Copyright (c) 2016 Intel Corporation. * * SPDX-License-Identifier: Apache-2.0 */ #include #include #include #include #include #include "device_power.h" #define ADC_0_XEC_REG_BASE \ ((struct adc_regs *)(DT_REG_ADDR(DT_NODELABEL(adc0)))) #define ECIA_XEC_REG_BASE \ ((struct ecia_named_regs *)(DT_REG_ADDR(DT_NODELABEL(ecia)))) #define ECS_XEC_REG_BASE \ ((struct ecs_regs *)(DT_REG_ADDR(DT_NODELABEL(ecs)))) #define PECI_XEC_REG_BASE \ ((struct peci_regs *)(DT_REG_ADDR(DT_NODELABEL(pcr)))) #define PCR_XEC_REG_BASE \ ((struct pcr_regs *)(DT_REG_ADDR(DT_NODELABEL(pcr)))) #define TFDP_0_XEC_REG_BASE \ ((struct tfdp_regs *)(DT_REG_ADDR(DT_NODELABEL(tfdp0)))) #define UART_0_XEC_REG_BASE \ ((struct uart_regs *)(DT_REG_ADDR(DT_NODELABEL(uart0)))) #define UART_1_XEC_REG_BASE \ ((struct uart_regs *)(DT_REG_ADDR(DT_NODELABEL(uart1)))) #define VBATR_XEC_REG_BASE \ ((struct vbatr_regs *)(DT_REG_ADDR_BY_NAME(DT_NODELABEL(pcr), vbatr))) #define VBATM_XEC_BASE_ADDR \ ((uintptr_t)(DT_REG_ADDR(DT_NODELABEL(bbram)))) #define BTMR16_0_ADDR DT_REG_ADDR(DT_NODELABEL(timer0)) #define BTMR16_1_ADDR DT_REG_ADDR(DT_NODELABEL(timer1)) #define BTMR16_2_ADDR DT_REG_ADDR(DT_NODELABEL(timer2)) #define BTMR16_3_ADDR DT_REG_ADDR(DT_NODELABEL(timer3)) #define BTMR32_0_ADDR DT_REG_ADDR(DT_NODELABEL(timer4)) #define BTMR32_1_ADDR DT_REG_ADDR(DT_NODELABEL(timer5)) #define VBATM_XEC_ADDR DT_REG_ADDR(DT_NODELABEL(vbr)) #ifdef DEBUG_DEEP_SLEEP_CLK_REQ void soc_debug_sleep_clk_req(void) { struct ecs_regs *ecs = ECS_XEC_REG_BASE; struct pcr_regs *pcr = PCR_XEC_REG_BASE; uintptr_t vbm_addr = VBATM_XEC_BASE_ADDR; /* Save status to debug LPM been blocked */ for (int i = 0; i < 5; i++) { sys_write32(pcr->CLK_REQ[i], vbm_addr); vbm_addr += 4; } sys_write32(pcr->SYS_SLP_CTRL, vbm_addr); vbm_addr += 4; sys_write32(ecs->SLP_STS_MIRROR, vbm_addr); } #endif /* * Allow peripherals connected to external masters to wake the PLL but not * the EC. Once the peripheral has serviced the external master the PLL * will be turned back off. For example, if the eSPI master requests eSPI * configuration information or state of virtual wires the EC doesn't need * to be involved. The hardware can power on the PLL long enough to service * the request and then turn the PLL back off. The SMBus and I2C peripherals * in slave mode can also make use of this feature. */ void soc_deep_sleep_non_wake_en(void) { #ifdef CONFIG_ESPI struct ecia_named_regs *regs = ECIA_XEC_REG_BASE; regs->GIRQ22.SRC = UINT32_MAX; regs->GIRQ22.EN_SET = MCHP_ESPI_WK_CLK_GIRQ_BIT; #endif } void soc_deep_sleep_non_wake_dis(void) { #ifdef CONFIG_ESPI struct ecia_named_regs *regs = ECIA_XEC_REG_BASE; regs->GIRQ22.EN_CLR = UINT32_MAX; regs->GIRQ22.SRC = UINT32_MAX; #endif } /* When MEC172x drivers are power-aware this should be move there */ void soc_deep_sleep_wake_en(void) { #if defined(CONFIG_KSCAN) || DT_NODE_HAS_STATUS(DT_NODELABEL(ps2_0), okay) struct ecia_named_regs *regs = ECIA_XEC_REG_BASE; #if defined(CONFIG_KSCAN) /* Enable PLL wake via KSCAN */ regs->GIRQ21.SRC = MCHP_KEYSCAN_GIRQ_BIT; regs->GIRQ21.EN_SET = MCHP_KEYSCAN_GIRQ_BIT; #endif #if DT_NODE_HAS_STATUS(DT_NODELABEL(ps2_0), okay) /* Enable PS2_0B_WK */ regs->GIRQ21.SRC = MCHP_PS2_0_PORT0B_WK_GIRQ_BIT; regs->GIRQ21.EN_SET = MCHP_PS2_0_PORT0B_WK_GIRQ_BIT; #endif #endif } void soc_deep_sleep_wake_dis(void) { #if DT_NODE_HAS_STATUS(DT_NODELABEL(ps2_0), okay) struct ecia_named_regs *regs = ECIA_XEC_REG_BASE; /* Enable PS2_0B_WK */ regs->GIRQ21.EN_CLR = MCHP_PS2_0_PORT0B_WK_GIRQ_BIT; regs->GIRQ21.SRC = MCHP_PS2_0_PORT0B_WK_GIRQ_BIT; #endif } /* Variables used to save various HW state */ #ifdef DEEP_SLEEP_PERIPH_SAVE_RESTORE const struct ds_timer_info ds_timer_tbl[NUM_DS_TIMER_ENTRIES] = { { (uintptr_t)(BTMR16_0_ADDR + MCHP_BTMR_CTRL_OFS), MCHP_BTMR_CTRL_HALT, 0 }, { (uintptr_t)(BTMR16_1_ADDR + MCHP_BTMR_CTRL_OFS), MCHP_BTMR_CTRL_HALT, 0 }, { (uintptr_t)(BTMR16_2_ADDR + MCHP_BTMR_CTRL_OFS), MCHP_BTMR_CTRL_HALT, 0 }, { (uintptr_t)(BTMR16_3_ADDR + MCHP_BTMR_CTRL_OFS), MCHP_BTMR_CTRL_HALT, 0 }, { (uintptr_t)(BTMR32_0_ADDR + MCHP_BTMR_CTRL_OFS), MCHP_BTMR_CTRL_HALT, 0 }, { (uintptr_t)(BTMR32_1_ADDR + MCHP_BTMR_CTRL_OFS), MCHP_BTMR_CTRL_HALT, 0 }, }; static struct ds_dev_info ds_ctx; static void deep_sleep_save_ecs(void) { struct ecs_regs *regs = ECS_XEC_REG_BASE; ds_ctx.ecs[0] = regs->ETM_CTRL; ds_ctx.ecs[1] = regs->DEBUG_CTRL; #ifdef DEEP_SLEEP_JTAG regs->ETM_CTRL = 0; regs->DEBUG_CTRL = 0x00; #endif } #ifdef DEEP_SLEEP_UART_SAVE_RESTORE static void deep_sleep_save_uarts(void) { struct uart_regs *regs = UART_0_XEC_REG_BASE; ds_ctx.uart_info[0] = regs->ACTV; if (ds_ctx.uart_info[0]) { while ((regs->LSR & MCHP_UART_LSR_TEMT) == 0) { } } regs->ACTV = 0; regs = UART_1_XEC_REG_BASE; ds_ctx.uart_info[1] = regs->ACTV; if (ds_ctx.uart_info[1]) { while ((regs->LSR & MCHP_UART_LSR_TEMT) == 0) { } } regs->ACTV = 0; } #endif static void deep_sleep_save_timers(void) { const struct ds_timer_info *p; uint32_t i; p = &ds_timer_tbl[0]; for (i = 0; i < NUM_DS_TIMER_ENTRIES; i++) { ds_ctx.timers[i] = sys_read32(p->addr); if (p->stop_mask) { sys_write32(ds_ctx.timers[i] | p->stop_mask, p->addr); } else { sys_write32(0, p->addr); } p++; } } static void deep_sleep_restore_ecs(void) { #ifdef DEEP_SLEEP_JTAG struct ecs_regs *regs = ECS_XEC_REG_BASE; regs->ETM_CTRL = ds_ctx.ecs[0]; regs->DEBUG_CTRL = ds_ctx.ecs[1]; #endif } #ifdef DEEP_SLEEP_UART_SAVE_RESTORE static void deep_sleep_restore_uarts(void) { struct uart_regs *regs0 = UART_0_XEC_REG_BASE; struct uart_regs *regs1 = UART_1_XEC_REG_BASE; regs0->ACTV = ds_ctx.uart_info[0]; regs1->ACTV = ds_ctx.uart_info[1]; } #endif static void deep_sleep_restore_timers(void) { const struct ds_timer_info *p; uint32_t i, temp; p = &ds_timer_tbl[0]; for (i = 0; i < NUM_DS_TIMER_ENTRIES; i++) { if (p->stop_mask) { temp = sys_read32(p->addr) & ~(p->stop_mask); sys_write32(temp, p->addr); } else { sys_write32(ds_ctx.timers[i] & ~p->restore_mask, p->addr); } p++; } } #ifdef DEEP_SLEEP_PERIPH_SAVE_RESTORE_EXTENDED static void deep_sleep_save_blocks(void) { struct tfdp_regs *tfdp = TFDP_0_XEC_REG_BASE; struct ecs_regs *ecs = ECS_XEC_REG_BASE; #ifdef CONFIG_ADC struct adc_regs *adc0 = ADC_0_XEC_REG_BASE; /* ADC deactivate */ adc0->CONTROL &= ~(MCHP_ADC_CTRL_ACTV); #endif #ifdef CONFIG_PECI struct peci_regs *peci = PECI_XEC_REG_BASE; ds_ctx.peci_info.peci_ctrl = peci->CONTROL; ds_ctx.peci_info.peci_dis = ecs->PECI_DIS; ecs->PECI_DIS |= MCHP_ECS_PECI_DISABLE; #endif #ifdef CONFIG_I2C for (size_t n = 0; n > MCHP_I2C_SMB_INSTANCES; n++) { uint32_t addr = MCHP_I2C_SMB_BASE_ADDR(n) + MCHP_I2C_SMB_CFG_OFS; uint32_t regval = sys_read32(addr); ds_ctx.smb_info[n] = regval; sys_write32(regval & ~(MCHP_I2C_SMB_CFG_ENAB), addr); } #endif /* Disable comparator if enabled */ if (ecs->CMP_CTRL & BIT(0)) { ds_ctx.comp_en = 1; ecs->CMP_CTRL &= ~(MCHP_ECS_ACC_EN0); } #if defined(CONFIG_TACH_XEC) || defined(CONFIG_PWM_XEC) struct pcr_regs *pcr = PCR_XEC_REG_BASE; /* This low-speed clock derived from the 48MHz clock domain is used as * a time base for PWMs and TACHs * Set SLOW_CLOCK_DIVIDE = CLKOFF to save additional power */ ds_ctx.slwclk_info = pcr->SLOW_CLK_CTRL; pcr->SLOW_CLK_CTRL &= (~MCHP_PCR_SLOW_CLK_CTRL_100KHZ & MCHP_PCR_SLOW_CLK_CTRL_MASK); #endif /* TFDP HW block is not expose to any Zephyr subsystem */ if (tfdp->CTRL & MCHP_TFDP_CTRL_EN) { ds_ctx.tfdp_en = 1; tfdp->CTRL &= ~MCHP_TFDP_CTRL_EN; } /* Port 80 TODO Do we need to do anything? MEC172x BDP does not * include a timer so it should de-assert its CLK_REQ in response * to SLP_EN 0->1. */ } static void deep_sleep_restore_blocks(void) { struct tfdp_regs *tfdp = TFDP_0_XEC_REG_BASE; struct ecs_regs *ecs = ECS_XEC_REG_BASE; #ifdef CONFIG_ADC struct adc_regs *adc0 = ADC_0_XEC_REG_BASE; adc0->CONTROL |= MCHP_ADC_CTRL_ACTV; #endif #ifdef CONFIG_PECI struct peci_regs *peci = PECI_XEC_REG_BASE; ecs->PECI_DIS = ds_ctx.peci_info.peci_dis; peci->CONTROL = ds_ctx.peci_info.peci_ctrl; #endif #ifdef CONFIG_I2C for (size_t n = 0; n > MCHP_I2C_SMB_INSTANCES; n++) { uint32_t addr = MCHP_I2C_SMB_BASE_ADDR(n) + MCHP_I2C_SMB_CFG_OFS; sys_write32(ds_ctx.smb_info[n], addr); } #endif /* Restore comparator control values */ if (ds_ctx.comp_en) { ecs->CMP_CTRL |= MCHP_ECS_ACC_EN0; } #if defined(CONFIG_TACH_XEC) || defined(CONFIG_PWM_XEC) struct pcr_regs *pcr = PCR_XEC_REG_BASE; /* Restore slow clock control */ pcr->SLOW_CLK_CTRL = ds_ctx.slwclk_info; #endif /* TFDP HW block is not expose to any Zephyr subsystem */ if (ds_ctx.tfdp_en) { tfdp->CTRL |= MCHP_TFDP_CTRL_EN; } } #endif /* DEEP_SLEEP_PERIPH_SAVE_RESTORE_EXTENDED */ void soc_deep_sleep_periph_save(void) { #ifdef DEEP_SLEEP_PERIPH_SAVE_RESTORE_EXTENDED deep_sleep_save_blocks(); #endif deep_sleep_save_ecs(); deep_sleep_save_timers(); #ifdef DEEP_SLEEP_UART_SAVE_RESTORE deep_sleep_save_uarts(); #endif } void soc_deep_sleep_periph_restore(void) { deep_sleep_restore_ecs(); #ifdef DEEP_SLEEP_UART_SAVE_RESTORE deep_sleep_restore_uarts(); #endif deep_sleep_restore_timers(); #ifdef DEEP_SLEEP_PERIPH_SAVE_RESTORE_EXTENDED deep_sleep_restore_blocks(); #endif } #else void soc_deep_sleep_periph_save(void) { } void soc_deep_sleep_periph_restore(void) { } #endif /* DEEP_SLEEP_PERIPH_SAVE_RESTORE */