/*
 * Copyright 2024 Google LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "drivers/flash/flash_impl.h"

#include "system/passert.h"
#include "system/rtc_registers.h"

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

#include <stdbool.h>
#include <stdint.h>

// RTC backup registers are reset to zero on a cold boot (power up from a dead
// battery) so the value stored in the backup register corresponding to no erase
// in progress must also be zero. We need to use a nonzero value to store an
// erase to sector zero, so we can't simply store just the address.
//
// We want to store whether an erase is in progress (1 bit), whether the erase
// is for a sector or a subsector (1 bit), and the address being erased (32
// bits) in a single 32-bit RTC register. Since we can't magically compress 34
// bits into 32, we'll need to play some tricks. The address is going to almost
// certainly be less than 32 bits long; we aren't going to be using
// gigabyte-sized flash memories any time soon (at least not with this
// homegrown API), leaving bits free on the high end.

#define ERASE_IN_PROGRESS 0x80000000
#define ERASE_IS_SUBSECTOR 0x40000000
#define ERASE_FLAGS_MASK (ERASE_IN_PROGRESS | ERASE_IS_SUBSECTOR)
#define ERASE_ADDRESS_MASK 0x3FFFFFFF

status_t flash_impl_set_nvram_erase_status(bool is_subsector,
                                           FlashAddress addr) {
  PBL_ASSERTN((addr & ERASE_FLAGS_MASK) == 0); // "Flash address too large to store"
  uint32_t reg = addr | ERASE_IN_PROGRESS;
  if (is_subsector) {
    reg |= ERASE_IS_SUBSECTOR;
  }
  RTC_WriteBackupRegister(RTC_BKP_FLASH_ERASE_PROGRESS, reg);
  return S_SUCCESS;
}

status_t flash_impl_clear_nvram_erase_status(void) {
  RTC_WriteBackupRegister(RTC_BKP_FLASH_ERASE_PROGRESS, 0);
  return S_SUCCESS;
}

status_t flash_impl_get_nvram_erase_status(bool *is_subsector,
                                           FlashAddress *addr) {
  uint32_t reg = RTC_ReadBackupRegister(RTC_BKP_FLASH_ERASE_PROGRESS);
  if (reg == 0) {
    return S_FALSE;
  }

  *addr = reg & ERASE_ADDRESS_MASK;
  *is_subsector = (reg & ERASE_IS_SUBSECTOR) != 0;
  return S_TRUE;
}
