diff --git a/src/soc/qualcomm/x1p42100/Makefile.mk b/src/soc/qualcomm/x1p42100/Makefile.mk index 0c6b9469f2..85191ea52c 100644 --- a/src/soc/qualcomm/x1p42100/Makefile.mk +++ b/src/soc/qualcomm/x1p42100/Makefile.mk @@ -37,6 +37,7 @@ romstage-y += mmu.c romstage-y += ../common/aop_load_reset.c romstage-$(CONFIG_DRIVERS_UART) += ../common/qupv3_uart.c romstage-y += ../common/spmi.c +romstage-y += pmic.c ################################################################################ ramstage-y += soc.c diff --git a/src/soc/qualcomm/x1p42100/include/soc/pmic.h b/src/soc/qualcomm/x1p42100/include/soc/pmic.h new file mode 100644 index 0000000000..db798d6b75 --- /dev/null +++ b/src/soc/qualcomm/x1p42100/include/soc/pmic.h @@ -0,0 +1,53 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#ifndef _SOC_QUALCOMM_X1P42100_PMIC_H__ +#define _SOC_QUALCOMM_X1P42100_PMIC_H__ + +#include + +#define PMIC_SLAVE_ID 0x00 +#define SDAM05_BASE_ADDR 0x7400 +#define MEM_OFFSET_START 0x40 +#define PON_EVENT_LOG_AREA_SIZE (127 - 11 + 1) +#define PON_EVENT_TOTAL_LOG_AREA_SIZE (PON_EVENT_LOG_AREA_SIZE * 2) + +#define PM_PON_SDAM_COUNT_ADDR (SDAM05_BASE_ADDR + MEM_OFFSET_START + 5) +#define PM_PON_ENQUEUE_ADDR (SDAM05_BASE_ADDR + MEM_OFFSET_START + 6) +#define PM_PON_ENQUEUE_SDAM_NUM (SDAM05_BASE_ADDR + MEM_OFFSET_START + 7) +#define PM_PON_LOGGING_AREA_START (SDAM05_BASE_ADDR + MEM_OFFSET_START + 11) +#define PM_PON_LOGGING_AREA_END (SDAM05_BASE_ADDR + MEM_OFFSET_START + 127) +#define PM_PON_PUSH_PTR_INDEX(x) (x - (MEM_OFFSET_START + 11)) + +#define PM_PON_EVENT_PON_TRIGGER 0x1 +#define PM_PON_EVENT_RESET_TYPE 0x7 +#define PM_PON_EVENT_FUNDAMENTAL_RESET 0xC +#define PM_PON_EVENT_RESET_TRIGGER 0x6 +#define BEGIN_PON 0XD +#define PON_EVENT_PARSE_LIMIT 2 + +#define PON_KEYPD_PWR_N_S2_RSN 0x0080 +#define PON_RESIN_N_S2_RSN 0x0081 +#define PON_KPDPWR_AND_RESIN_RSN 0x0082 +#define PON_PMIC_WD_RSN 0x0083 +#define PON_PS_HOLD_RSN 0x0084 +#define PON_SW_RST_RSN 0x0085 +#define PON_RESIN_N_DEB_RSN 0x0086 +#define PON_KEYPD_PWR_N_RSN 0x0087 +#define PON_SMPL_RSN 0x0640 +#define PON_PON1_RSN 0x18C1 +#define PON_CBLPWR_RSN 0x18C0 +#define PON_SYS_OK_RSN 0x71C2 +#define PON_RTC_ALARM_RSN 0x0621 + +#define PON_RAW_XVDD_RB_MASK 0x8000 +#define PON_RAW_DVDD_RB_MASK 0x4000 +#define PON_S3_RESET_MASK 0xF0 + +#define PON_HARDRESET 0x7 +#define PON_SHUTDOWN 0x4 +#define PON_WARMRESET 0x1 + +int pm_pon_read_pon_hist(uint8_t *pon_hist_raw); +bool is_pon_on_ac(void); + +#endif // _SOC_QUALCOMM_X1P42100_PMIC_H__ diff --git a/src/soc/qualcomm/x1p42100/pmic.c b/src/soc/qualcomm/x1p42100/pmic.c new file mode 100644 index 0000000000..db2ff3890d --- /dev/null +++ b/src/soc/qualcomm/x1p42100/pmic.c @@ -0,0 +1,175 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#include +#include +#include +#include + +/* + * pm_pon_get_pon_event - Search for a specific event type in the PON log. + * @event: The event type to search for. + * @pon_hist_log: Pointer to the PON history log buffer. + * @pon_hist_raw_size: Total size of the raw log buffer. + * @return_data: Pointer to store the associated event data. + * + * This helper function iterates through the PON log buffer and searches for an + * entry matching the specified event type (e.g., PM_PON_EVENT_PON_TRIGGER). + * If found, it extracts the associated 16-bit data field (which usually contains + * the power-on reason or status) and returns it via return_data. + * + * @return 0 on success, -1 on error. + */ +static int pm_pon_get_pon_event(uint8_t event, uint8_t *pon_hist_log, uint32_t pon_hist_raw_size, uint16_t *return_data) +{ + bool event_found = false; + uint8_t data0; + uint8_t data1; + uint32_t i; + + if (!pon_hist_log || !return_data) + return -1; + + for (i = 0; i < pon_hist_raw_size; i += 4) { + if (event == *(pon_hist_log + 2)) { + data0 = *pon_hist_log; + data1 = *(pon_hist_log + 1); + event_found = true; + break; + } + pon_hist_log += 4; + } + + if (event_found == false) + *return_data = 0; + else + *return_data = ((uint16_t) data1 << 8) | data0; + + return 0; +} + +/* + * pm_pon_read_pon_hist - Read, reorder, and prepare the PMIC Power-On history log. + * @pon_hist_raw: Buffer to store the resulting PON history log. The buffer size + * must be at least PON_EVENT_TOTAL_LOG_AREA_SIZE bytes. + * + * This function handles the low-level logic to read the PON event data from + * the PMIC's SDAM registers (which function as a circular buffer) and reorders + * the data in the output buffer (pon_hist_raw) so that the most recent event + * entry is placed at the beginning (index 0). + * + * @return 0 on success, -1 on failure (e.g., if pon_hist_raw is NULL or SPMI read fails). + */ +int pm_pon_read_pon_hist(uint8_t *pon_hist_raw) +{ + int status; + uint8_t sdam_count; + uint8_t enqueue_sdam_num; + uint32_t i; + uint32_t read_size; + uint32_t push_ptr; + uint8_t enqueue_addr; + uint8_t temp; + + if (!pon_hist_raw) + return -1; + + enqueue_addr = spmi_read8(SPMI_ADDR(PMIC_SLAVE_ID, PM_PON_ENQUEUE_ADDR)); + sdam_count = spmi_read8(SPMI_ADDR(PMIC_SLAVE_ID, PM_PON_SDAM_COUNT_ADDR)); + enqueue_sdam_num = spmi_read8(SPMI_ADDR(PMIC_SLAVE_ID, PM_PON_ENQUEUE_SDAM_NUM)); + + /* if sdam_count == 1, each SDAM contains half of the total size + * otherwise, the SDAM stores the whole size + */ + status = spmi_read_bytes(SPMI_ADDR(PMIC_SLAVE_ID, PM_PON_LOGGING_AREA_START), pon_hist_raw, PON_EVENT_LOG_AREA_SIZE); + + if (sdam_count != 0) { + /* 0: Only used 1 SDAM + * 1: Used 2 SDAM + * Currently only extend to 2 continuous SDAM. + */ + status |= spmi_read_bytes(SPMI_ADDR(PMIC_SLAVE_ID, (PM_PON_LOGGING_AREA_START + 0x100)), pon_hist_raw + PON_EVENT_LOG_AREA_SIZE, PON_EVENT_LOG_AREA_SIZE); + } + + push_ptr = enqueue_addr; + push_ptr = PM_PON_PUSH_PTR_INDEX(push_ptr) + (enqueue_sdam_num * PON_EVENT_LOG_AREA_SIZE); + + if (status != 0 || push_ptr >= PON_EVENT_TOTAL_LOG_AREA_SIZE) + return -1; + + read_size = push_ptr/2; + + /* Reverse the Buffer to start from latest event */ + for (i = 0; i < read_size; i++) { + temp = pon_hist_raw[push_ptr - i - 1]; + pon_hist_raw[push_ptr - i - 1] = pon_hist_raw[i]; + pon_hist_raw[i] = temp; + } + + read_size = (PON_EVENT_TOTAL_LOG_AREA_SIZE - push_ptr) / 2; + + for (i = 0; i < read_size; i++) { + temp = pon_hist_raw[PON_EVENT_TOTAL_LOG_AREA_SIZE - i - 1]; + if (push_ptr + i < PON_EVENT_TOTAL_LOG_AREA_SIZE) { + pon_hist_raw[PON_EVENT_TOTAL_LOG_AREA_SIZE - i - 1] = pon_hist_raw[push_ptr + i]; + pon_hist_raw[push_ptr + i] = temp; + } + } + + return status; +} + +/* + * is_pon_on_ac - Checks if the system was powered on by an AC/cable insertion event. + * + * This function reads and parses the PMIC Power-On (PON) history log to determine + * the specific cause of the system power-up. It specifically looks for the + * PON_CBLPWR_RSN trigger, which indicates that external power (AC/charging cable) + * was the reason for the boot sequence initiation. + * + * @return true if AC/Cable Power was the PON reason, false otherwise. + */ +bool is_pon_on_ac(void) +{ + uint16_t data, data2; + uint32_t i; + int8_t current_index = -1; + uint8_t *pon_hist_curr_addr; + uint8_t pon_hist_raw[PON_EVENT_TOTAL_LOG_AREA_SIZE] = {0}; + + printk(BIOS_INFO, "PON: Show power on reason -\n"); + if (pm_pon_read_pon_hist(pon_hist_raw)) + return false; + + pon_hist_curr_addr = pon_hist_raw; + + for (i = 0; i < PON_EVENT_LOG_AREA_SIZE - 2; i += 4) { + if (current_index >= PON_EVENT_PARSE_LIMIT) { + pon_hist_curr_addr += 4; + continue; + } + data = ((uint16_t)(*(pon_hist_curr_addr + 1)) << 8) | *pon_hist_curr_addr; + + if (BEGIN_PON == *(pon_hist_curr_addr + 2)) { + pm_pon_get_pon_event(BEGIN_PON, pon_hist_curr_addr, (PON_EVENT_LOG_AREA_SIZE - 2) - (uint8_t)(pon_hist_curr_addr - pon_hist_raw), &data2); + current_index += 1; + } else if (PM_PON_EVENT_PON_TRIGGER == *(pon_hist_curr_addr + 2)) { + switch (data) { /* SID<<12|PID<<4|IRQ */ + case PON_CBLPWR_RSN: + printk(BIOS_INFO, " PON Reason : cblpwr\n"); + return true; + default: + printk(BIOS_INFO, " PON Reason : %d\n", data); + return false; + } + } else if (PM_PON_EVENT_RESET_TYPE == *(pon_hist_curr_addr + 2)) { + printk(BIOS_INFO, " Reset Reason : %d\n", data & 0xFF); + return false; + } else if (PM_PON_EVENT_FUNDAMENTAL_RESET == *(pon_hist_curr_addr + 2)) { + printk(BIOS_INFO, " POFF Reason : %d\n", data & PON_RAW_XVDD_RB_MASK); + return false; + } + pon_hist_curr_addr += 4; + } + printk(BIOS_INFO, " Unable to detect PON reason.\n"); + return false; +}