/* SPDX-FileCopyrightText: 2024 Google LLC */
/* SPDX-License-Identifier: Apache-2.0 */

#include "reboot_reason.h"

#include "mcu/interrupts.h"
#include "os/tick.h"
#include "system/logging.h"

#define STM32F2_COMPATIBLE
#define STM32F4_COMPATIBLE
#define STM32F7_COMPATIBLE
#define NRF5_COMPATIBLE
#define SF32LB52_COMPATIBLE
#include <mcu.h>

#include <inttypes.h>

#include "FreeRTOS.h"
#include "task.h"

_Static_assert(sizeof(RebootReason) == sizeof(uint32_t[4]), "RebootReason is a funny size");

void reboot_reason_set(RebootReason *reason) {
#if MICRO_FAMILY_NRF5
  uint32_t *raw = (uint32_t*)reason;

  if (retained_read(REBOOT_REASON_REGISTER_1)) {
    // It's not safe to log if we're called from an ISR or from a FreeRTOS critical section (basepri != 0)
    if (!mcu_state_is_isr() && __get_BASEPRI() == 0
            && xTaskGetSchedulerState() == taskSCHEDULER_RUNNING) {
      PBL_LOG(LOG_LEVEL_WARNING, "Reboot reason is already set");
    }
    return;
  }

  retained_write(REBOOT_REASON_REGISTER_1, raw[0]);
  retained_write(REBOOT_REASON_STUCK_TASK_PC, raw[1]);
  retained_write(REBOOT_REASON_STUCK_TASK_LR, raw[2]);
  retained_write(REBOOT_REASON_STUCK_TASK_CALLBACK, raw[3]);
#elif defined MICRO_FAMILY_SF32LB52
  uint32_t *raw = (uint32_t*)reason;

  if (HAL_Get_backup(REBOOT_REASON_REGISTER_1)) {
    // It's not safe to log if we're called from an ISR or from a FreeRTOS critical section (basepri != 0)
    if (!mcu_state_is_isr() && __get_BASEPRI() == 0
            && xTaskGetSchedulerState() == taskSCHEDULER_RUNNING) {
      PBL_LOG(LOG_LEVEL_WARNING, "Reboot reason is already set");
    }
    return;
  }

  HAL_Set_backup(REBOOT_REASON_REGISTER_1, raw[0]);
  HAL_Set_backup(REBOOT_REASON_STUCK_TASK_PC, raw[1]);
  HAL_Set_backup(REBOOT_REASON_STUCK_TASK_LR, raw[2]);
  HAL_Set_backup(REBOOT_REASON_STUCK_TASK_CALLBACK, raw[3]);
#else
  uint32_t *raw = (uint32_t*)reason;

  if (RTC_ReadBackupRegister(REBOOT_REASON_REGISTER_1)) {
    // It's not safe to log if we're called from an ISR or from a FreeRTOS critical section (basepri != 0)
    if (!mcu_state_is_isr() && __get_BASEPRI() == 0
            && xTaskGetSchedulerState() == taskSCHEDULER_RUNNING) {
      PBL_LOG(LOG_LEVEL_WARNING, "Reboot reason is already set");
    }
    return;
  }

  RTC_WriteBackupRegister(REBOOT_REASON_REGISTER_1, raw[0]);
  RTC_WriteBackupRegister(REBOOT_REASON_STUCK_TASK_PC, raw[1]);
  RTC_WriteBackupRegister(REBOOT_REASON_STUCK_TASK_LR, raw[2]);
  RTC_WriteBackupRegister(REBOOT_REASON_STUCK_TASK_CALLBACK, raw[3]);
#endif
}

void reboot_reason_set_restarted_safely(void) {
  RebootReason reason;
  reboot_reason_get(&reason);
  reason.restarted_safely = true;

#if MICRO_FAMILY_NRF5
  uint32_t* raw = (uint32_t *)&reason;
  retained_write(REBOOT_REASON_REGISTER_1, *raw);
#elif defined MICRO_FAMILY_SF32LB52
  uint32_t* raw = (uint32_t *)&reason;
  HAL_Set_backup(REBOOT_REASON_REGISTER_1, *raw);
#else
  uint32_t* raw = (uint32_t *)&reason;
  RTC_WriteBackupRegister(REBOOT_REASON_REGISTER_1, *raw);
#endif
}

void reboot_reason_get(RebootReason *reason) {
#if MICRO_FAMILY_NRF5
  uint32_t *raw = (uint32_t *)reason;
  raw[0] = retained_read(REBOOT_REASON_REGISTER_1);
  raw[1] = retained_read(REBOOT_REASON_STUCK_TASK_PC);
  raw[2] = retained_read(REBOOT_REASON_STUCK_TASK_LR);
  raw[3] = retained_read(REBOOT_REASON_STUCK_TASK_CALLBACK);
#elif defined MICRO_FAMILY_SF32LB52
  uint32_t *raw = (uint32_t *)reason;
  raw[0] = HAL_Get_backup(REBOOT_REASON_REGISTER_1);
  raw[1] = HAL_Get_backup(REBOOT_REASON_STUCK_TASK_PC);
  raw[2] = HAL_Get_backup(REBOOT_REASON_STUCK_TASK_LR);
  raw[3] = HAL_Get_backup(REBOOT_REASON_STUCK_TASK_CALLBACK);
#else
  uint32_t *raw = (uint32_t *)reason;
  raw[0] = RTC_ReadBackupRegister(REBOOT_REASON_REGISTER_1);
  raw[1] = RTC_ReadBackupRegister(REBOOT_REASON_STUCK_TASK_PC);
  raw[2] = RTC_ReadBackupRegister(REBOOT_REASON_STUCK_TASK_LR);
  raw[3] = RTC_ReadBackupRegister(REBOOT_REASON_STUCK_TASK_CALLBACK);
#endif
}

void reboot_reason_clear(void) {
#if MICRO_FAMILY_NRF5
  retained_write(REBOOT_REASON_REGISTER_1, 0);
  retained_write(REBOOT_REASON_STUCK_TASK_PC, 0);
  retained_write(REBOOT_REASON_STUCK_TASK_LR, 0);
  retained_write(REBOOT_REASON_STUCK_TASK_CALLBACK, 0);
#elif defined MICRO_FAMILY_SF32LB52
  HAL_Set_backup(REBOOT_REASON_REGISTER_1, 0);
  HAL_Set_backup(REBOOT_REASON_STUCK_TASK_PC, 0);
  HAL_Set_backup(REBOOT_REASON_STUCK_TASK_LR, 0);
  HAL_Set_backup(REBOOT_REASON_STUCK_TASK_CALLBACK, 0);
#else
  RTC_WriteBackupRegister(REBOOT_REASON_REGISTER_1, 0);
  RTC_WriteBackupRegister(REBOOT_REASON_STUCK_TASK_PC, 0);
  RTC_WriteBackupRegister(REBOOT_REASON_STUCK_TASK_LR, 0);
  RTC_WriteBackupRegister(REBOOT_REASON_STUCK_TASK_CALLBACK, 0);
#endif
}

uint32_t reboot_get_slot_of_last_launched_app(void) {
#if MICRO_FAMILY_NRF5
  return retained_read(SLOT_OF_LAST_LAUNCHED_APP);
#elif defined MICRO_FAMILY_SF32LB52
  return HAL_Get_backup(SLOT_OF_LAST_LAUNCHED_APP);
#else
  return RTC_ReadBackupRegister(SLOT_OF_LAST_LAUNCHED_APP);
#endif
}

void reboot_set_slot_of_last_launched_app(uint32_t app_slot) {
#if MICRO_FAMILY_NRF5
  retained_write(SLOT_OF_LAST_LAUNCHED_APP, app_slot);
#elif defined MICRO_FAMILY_SF32LB52
  HAL_Set_backup(SLOT_OF_LAST_LAUNCHED_APP, app_slot);
#else
  RTC_WriteBackupRegister(SLOT_OF_LAST_LAUNCHED_APP, app_slot);
#endif
}

