soc/qc/x1p42100: Add APIs to read PON reason from PMIC

Add the Power-On (PON) history log parsing and status API to the
SOC layer (soc/qualcomm/x1p42100/pmic.c).

This code is specific to the Qualcomm PMIC architecture (reading
registers for PON events and reasons), making it an SOC-specific
utility rather than a board-level policy. Moving it here improves
modularization and allows other X1p42100-based boards to reuse this
critical power management logic.

Key APIs introduced:
- pm_pon_read_pon_hist(): Reads the raw circular PON event log
  from the PMIC, reverses the buffer to put the latest entry first.
- is_pon_on_ac(): Interprets the log to detect if the power-on
  reason was due to AC/Cable Power (PON_CBLPWR_RSN).

Key changes:
- Create src/soc/qualcomm/x1p42100/include/soc/pmic.h with PON
  definitions and API prototypes.
- Create src/soc/qualcomm/x1p42100/pmic.c containing the PON
  log reading and parsing logic.
- Add pmic.c to the SOC's romstage build via Makefile.mk.

BUG=b:439819922
TEST=Verify off-mode charging behavior on Google/Quenbi.

Change-Id: I8cd1478b9f8d53519f603e8f5168d0a51fa54971
Signed-off-by: Kapil Porwal <kapilporwal@google.com>
Reviewed-on: https://review.coreboot.org/c/coreboot/+/90192
Reviewed-by: Subrata Banik <subratabanik@google.com>
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
This commit is contained in:
Kapil Porwal 2025-11-24 22:03:50 +05:30 committed by Matt DeVillier
commit 201ebd48ee
3 changed files with 229 additions and 0 deletions

View file

@ -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

View file

@ -0,0 +1,53 @@
/* SPDX-License-Identifier: GPL-2.0-only */
#ifndef _SOC_QUALCOMM_X1P42100_PMIC_H__
#define _SOC_QUALCOMM_X1P42100_PMIC_H__
#include <types.h>
#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__

View file

@ -0,0 +1,175 @@
/* SPDX-License-Identifier: GPL-2.0-only */
#include <console/console.h>
#include <soc/pmic.h>
#include <soc/qcom_spmi.h>
#include <types.h>
/*
* 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;
}